C# How to capture input from pipeline from continuous stream source - c#

I have a service that runs as a console app and outputs to stdout and stderr streams to the console. The developers don't want to add any logging capabilities since it was mainly designed for linux world and they have tools like logroate and other means already but don't care about the windows port. I want to redirect those streams into a log and I'm able to do it like this:
service.exe 2>&1 >> service.log
But if I leave this running this log will grow out of control. So I tried using something called logrotateWin but it needs to rename the log file and I get access denied since its in use. So I'm trying to write another app that can write to logfile and rotate it based once a day and only keep last n logs.
I want to be able to run it like:
service.exe 2>&1 | logrotater.exe
So I'm trying to write logrotater with the following code but it seems to fail to read the stream from the pipeline. It can read the stream if it stops like from a simple echo "test" command source, but not from service.exe that continuously keeps streaming data out. Any suggestions?
So this works:
echo "test" | logrotater.exe
But this doesn't work:
service.exe 2>&1 | logrotater.exe
static void Main(string[] args) {
Console.SetIn(new StreamReader(Console.OpenStandardInput(8192))); // This will allow input >256 chars
while (Console.In.Peek() != -1) {
string input = Console.In.ReadLine();
Console.WriteLine("Data read was " + input);
}
}

Add a Log() method that opens and closes the file as needed. Moving to a separate method will make it easier to isolate these changes from the rest of the program. One easy way to open/close as needed is with File.AppendAllText().
static void Main(string[] args)
{
Console.SetIn(new StreamReader(Console.OpenStandardInput(8192))); // This will allow input >256 chars
string input;
while ( (input= Console.ReadLine()) != null)
{
Log(input);
}
}
static void Log(string data)
{
string msg = $"Data read was {data}";
Console.WriteLine(msg);
File.AppendAllText("C:\YourPath", msg);
}

Related

How to keep standard input stream open?

I am redirecting all streams (in/out/error) from a commandline application that runs some ChaiScript scripts. The user is able to interact with a running script via input commands such as PromptForDouble("Enter a double:"); which are defined in the separate commandline application and not my C# program.
Right now I have a RichTextBox that behaves like a console, and is readonly. I have another textbox at the bottom that can be used to "inject" input when needed. However, after the script is tossed into standard input, I'm forced to close standard input, and obviously can't "reopen" it once the script asks the user for input.
My question is how can I "close" standard input after the script is entered, continue with the application (receive output), and enter input again when the user is prompted?
Relevant code:
// Executes the script in the specified workspace (workspace only specified for logging)
public ScriptOutput ExecuteScript(WorkArea workspace, string script)
{
ScriptOutput output;
output.StdOut = "";
output.StdErr = "";
process.Start(); // the instance of the commandline program
StreamWriter inStream = process.StandardInput;
inStream.WriteLine(script + "\n//<SCRIPT END>\n");
inStream.Close();
output.StdErr = process.StandardError.ReadToEnd();
string line = "";
while((line = process.StandardOutput.ReadLine()) != null)
{
workspace.log(line, false);
}
process.WaitForExit();
//inStream.Close(); <-- closing the stream here makes the program "pause" after the script is entered as it expects more input
return output;
}
ScriptOutput is just a lazy way of having stdout and stderr strings in one object (it's a struct). This was coded before tuples so...Sorry :P
tl;dr how do I emulate closing and reopening standard input?

NUnit ITestEventListener get output only continuously

I have a very simple TestEventListener that send output to the console and to a text file. However, it currently prints everything and I only want to capture the event, but I'm not sure how to do it. Also, is it possible to get it as it happened? As of right now it only seems to be printing after the test is run which isn't really ideal. Here's what I have now:
class TestListener : ITestEventListener
{
StreamWriter _outputStream;
string _outputFile;
public TestListener(string outputPath)
{
_outputFile = outputPath + "\\ConsoleLog.txt";
_outputStream = new StreamWriter(_outputFile);
}
public void OnTestEvent (string report)
{
WriteText(report);
}
public void WriteText(string text)
{
Console.WriteLine(text);
_outputStream.WriteLine(text);
}
}
It's your code that's printing everything. The report, as you have seen, is XML. It's up to you to look at it in your code and decide what to print.
Most likely, in the events you receive, you will see test starts, test results and immediate text output if you have any. Which of those do you want to display?
As far as only getting output after the test finishes, that would be the test result output, which obviously has to come after the end of the test. It includes an element output with any text writes that were done in the course of the test.
If you want immediate output, which comes as a test-output report, you have to create it in your tests. The only output that is sent this way is Console.Error, TestContext.Error and TestContext.Progress. This is the only output that comes to you before the end of the test.

Send argument to console app and get string array in return

I am sending email body to the console application as parameter, the thing is I only see first 4 characters <div on the console application what happens to the other part? Can I send html email text as parameter to console application? Also is there any way to return a string[] array from console app?
My so far code below:
TestingConsoleApp to check the send n receive:
static void Main(string[] args)
{
string emailBody = System.IO.File.ReadAllText(#"C:\Users\ehsankayani\Desktop\email1Html.txt");
CallProcess(emailBody);
}
static void CallProcess(string body)
{
string path = #"F:\Scrappers\emailParser_app\emailParser_app\bin\Debug\emailParser_app.exe";
Process.Start(path, body);
}
Main console app:
static void Main(string[] args)
{
Console.WriteLine("EMAIL BODY = ");
string[] dataToReturn = new string[8];
//string emailBody = System.IO.File.ReadAllText(#"C:\Users\ehsankayani\Desktop\email1Html.txt");
string emailBody = args[0];
Console.WriteLine(emailBody);
Console.WriteLine(emailBody.Length);
Console.ReadLine();
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(emailBody);
}
Any suggestions?
You are passing body as the parameter set in CallProcess. So if body were eg
xxxx yyyy ....
then args[0] would be just xxxx. You'll need to put "" around the text and escape in "'s in the text too.
A far better solution though would be to set up your Process to redirect stdin and to write the body to the process' stdin. This will avoid issues with whitespace and quotes. Take a look at http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardinput.aspx for details on doing this.
A simply HttpUtility.EncodeHtml(Emailbody) while sending and HttpUtility.DecodeHtml(emailBody) on receiving does the job.
Since you ask for suggestions: For complex and/or large data, I'd rather pass only the information where to find the data to the process being called. Same can be done for returned data
This has several positive effects, the more complex and the larger these data are: You don't have to make sure the data are matching the command line restrictions, you don't have to allocate lots of memory to encode/decode the data in both processes, and you don't need to implement the logic to do that. Instead, write the data into a file, a shared memory region (MMF) or the like and pass that address.
Nearly the only downside is that you have to think about who's responsible for cleaning up.
Another possible approach could involve interprocess communication, but I think that's a bit overkill here.

Make windows service run as if it was run from a specific user

I want to create a windows service to mount and dismount a True Crypt volume.
(this question is not about true crypt so if you do not know what that program that is ok. True Crypt is just a program that enables you to encrypt data. When you decrypt it then trueCrypt will create a virtual drive where you can see the contents unencrypted).
Here is the code I use in my console application:
static void Main(string[] args)
{
Test();
}
static void Test()
{
try
{
// location where true crypt is installed
const string trueCryptExeLocation = #"C:\Program Files\TrueCrypt\TrueCrypt.exe";
// command line arguments to mount a volume located at 'C:\TC Volumes\vol1.tc' where
// it's password is 'demo' and it will be mounted on drive letter y
const string mountVolumeCmd = #"/v ""C:\TC Volumes\vol1.tc"" /ly /q /p demo /f /a";
// mount the volume on drive letter y
Process.Start(trueCryptExeLocation, mountVolumeCmd).WaitForExit();
// write to the volume!
System.IO.File.WriteAllText("y:\\someFile.txt", "it works");
// wait 30 seconds before dismounting
Thread.Sleep(3000);
// dismount the volume
Process.Start(#"C:\Program Files\TrueCrypt\TrueCrypt.exe", #"/ly /q /d /f");
}
catch (Exception ex)
{
// if there is an error write it to disk
System.IO.File.WriteAllText(#"A:\Dropbox\Eduardo\WindowsNodeService\WindowsNodeService\bin\Debug\startup.txt", ex.ToString());
}
}
That code basically mounts a volume writes to it and dismounts it. IF I run that program this is what I see:
When I open the program trueCrypt.exe I can see that the volume is actually mounted.
I can see that the program actually wrote to the file someFile.txt
The path Y:\ exist. In other words I can open My Computer and see drive Y.
Now my question is why if I run the same code on a windows service it runs differently but it runns ok with no errors.
In other words having the following windows service:
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Test(); // same method as console application
}
protected override void OnStop()
{
}
// etc... Test method implementation
Running that code runs fine. It writes to the file someFile.txt! which is great. The problem is that if I open trueCrypt I do not see the volume mounted in there. Also if I go to My Computer I do not see the drive Y. How can I make my windows service behave like the console application?
If you're using .Net 4.5, you can use this Process.Start call:
Code from MSDN:
Process.Start(path + "HelloWorld.exe", args, uname, password, domain);
Austin's answer should work for the specific case. However, if you just want to know how to do this at installation time, then you want to use the ServiceProcess.ServiceAccount property. An example with more details can be found here
You might want to try the LocalSystem ServiceAccount type.

Turning C output into C# input

I am currently trying to develop a program which takes the output of an existing program (written in C) and uses it as input (in C#). The problem I am having is that the existing program prints data in redundant format but it dynamically changes. An example could be a random name generator and I need to make a program that logs all of the random names as they appear.
Could I just pipe this and the output will be grabbed as it comes? The C program is run from a CLI.
You could redirect the output streams from the Process object to get direct access to it. You make a call and it will invoke an event of your choice when output is received. One of my questions may provide some explanation on how to do this - C# Shell - IO redirection:
processObject.StartInfo.RedirectStandardOutput = true;
processObject.OutputDataReceived += new DataReceivedEventHandler(processObject_OutputDataReceived);
/* ... */
processObject.Start();
processObject.BeginOutputReadLine();
And then later:
public void processObject_OutputDataReceived(object sender, DataReceivedEventArgs e) {
ProcessNewData(e.Data);
}
Note that this includes the trailing newline.
Or you can just pipe it and use Console.ReadLine to read it in. On the command line, you would execute:
cprogram | csharp_program
Unfortunately, you can't do this directly with the Process object - just use the method above. If you do choose to go this route, you can use:
string input = "";
int currentChar = 0;
while ( (currentChar = Console.Read()) > -1 ) {
input += Convert.ToChar(currentChar);
}
Which will read from the input until EOF.
If the C# program is also command-line based, running this command from the command-line will chain them together:
my_c_program.exe | my_csharp_program.exe
The C# program will receive the output of the C program, via its standard input stream (Console.Read, etc.)

Categories