C# Get standard input/output/error of running, NOT child process - c#

My question is pretty simple: how do I get the standard output/input/error of a process I did not start? Let's say I have some program (that is not mine) XYZ that runs all day and prints things to stdout. I want to be able to start my C# application and start reading XYZ's output. Then close my C# application and restart it 2 hours later and continue reading the output (not necessarily where I left off but that would be a plus).
I know this can be achieved if XYZ is a child process of mine with
serverProcess = Process.Start(new ProcessStartInfo()
{
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
WorkingDirectory = WorkingDir,
Arguments = Args,
FileName = "XYZ",
UseShellExecute = false
});
serverProcess.OutputDataReceived += serverProcess_OutputDataReceived;
serverProcess.ErrorDataReceived += serverProcess_OutputDataReceived;
serverProcess.BeginOutputReadLine();
serverProcess.BeginErrorReadLine();
But this is not the case since XYZ is is completely independent of my application, and needs to keep running regardless of what I do to it.
It is worth noting that (if it helps) I can start XYZ myself (with those redirection parameters). It's just that my lifecycle needs to be completely independent of XYZ, and I need to be able to "reattach" after I relaunch.
I have seen some suggestions using Get/SetStdHandle but most of them claimed to not work, and I din't quite understand how to apply them anyways.
Thanks for any advice!

It is worth noting that (if it helps) I can start XYZ myself (with those redirection parameters). It's just that my lifecycle needs to be completely independent of XYZ, and I need to be able to "reattach" after I relaunch.
Here is a simple implementation of what I suggested in the comments (publishing data through an intermediary process).
basicaly the sample is composed of 3 main parts
1. Sample XYZ Process
which in our case, is a simple console application that spits a Hello from XYZ message every second.
class Program
{
static void Main(string[] args)
{
var count = 0;
while (true)
{
Console.WriteLine("Hello from XYZ "+count);
count++;
Thread.Sleep(1000);
}
}
}
2. Intermediary Process
which should start XYZ and redirect its output to itself and then publish it through. for publishing part we use ZeroMQ library here that can be obtained easily from nuget.
class Program
{
private static Socket _pub;
static void Main(string[] args)
{
using (var context = new Context())
{
_pub = context.Socket(SocketType.PUB);
_pub.Bind("tcp://*:2550");
StartXyz();
Console.WriteLine("Press any key to close middle process...");
Console.ReadKey();
}
}
private static void StartXyz()
{
var serverProcess = Process.Start(new ProcessStartInfo
{
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
Arguments = string.Empty,
FileName = "XYZ.exe",
UseShellExecute = false
});
serverProcess.OutputDataReceived += serverProcess_OutputDataReceived;
serverProcess.ErrorDataReceived += serverProcess_OutputDataReceived;
serverProcess.BeginOutputReadLine();
serverProcess.BeginErrorReadLine();
}
private static void serverProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
_pub.Send(e.Data, Encoding.UTF8);
Console.WriteLine(e.Data + " pushed.");
}
}
3. Consumer Process
and finally we have our process, which consumes redirected data from XYZ.
class Program
{
static void Main(string[] args)
{
using (var context = new Context())
{
var sub = context.Socket(SocketType.SUB);
sub.Connect("tcp://localhost:2550");
sub.Subscribe(string.Empty, Encoding.UTF8);
while (true)
{
var data = sub.Recv(Encoding.UTF8);
Console.WriteLine(data);
}
}
}
}

Related

Forcing timeout on a process in .NET 6

I come from a C background, and my knowledge of C# and .NET is very very limited.
Now, I am running an executable in my code as a Process, in a .NET 6.0 application. This is what the code looks like
static string ExternalExe(string input)
{
string output = string.Empty;
ProcessStartInfo info = new()
{
FileName = "path_here",
Arguments = input,
RedirectStandardOutput = true,
UseShellExecute = false
};
using (Process? process = Process.Start(info))
{
if (process != null)
{
output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
}
else
{
throw new CustomException();
}
}
return output;
}
I want to implement a timeout for this. As in, if the process takes, for example, more than 1 second to exit, it should generate an exception or something like that. How would I implement that?
Any help would be much appreciated, and thanks in advance. :)
You could use the WaitForExit(TimeSpan) but this will block your UI thread. Since we're in .NET6 I would use the asynchronous version with a CancellationTokenSource set to automatically cancel after x ms.
await process.WaitForExitAsync(new CancellationTokenSource(3000).Token);

Use ProcessExecution to start 2 applications in order [duplicate]

This question already has answers here:
How to make GUI wait for windows service?
(5 answers)
Closed 3 years ago.
I want to start 2 related applications. First, I want to start my "Service" application and then my "Client" application. It does not consistently work. Sometimes the client starts up too fast and ends up not being connected to the service. Can someone show me where I need to change my code to work correctly and have the client only start after the service has completely started?
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Starting Service");
StartService();
if (IsServiceRunning())
{
Console.WriteLine("Starting Client");
StartClient();
}
Console.ReadLine();
}
private static void StartClient()
{
ProcessStartInfo startInfo = new ProcessStartInfo()
{
WorkingDirectory = #"C:\Client",
FileName = "Client.exe"
};
Process.Start(startInfo);
}
private static bool IsServiceRunning()
{
Console.WriteLine("Check to see is running...");
Process[] pname = Process.GetProcessesByName("MyCommonService");
int runningCheck = 0;
if (pname.Length == 0 || runningCheck < 10)
{
Console.WriteLine("Did not find the process. Check again...");
runningCheck += 1;
Thread.Sleep(250);
IsServiceRunning();
}
Thread.Sleep(1000);
return true;
}
private static void StartService()
{
Console.WriteLine("Starting Service");
ProcessStartInfo startInfo = new ProcessStartInfo()
{
WorkingDirectory = #"C:\Service",
FileName = "MyCommonService.exe"
};
Process.Start(startInfo);
}
}
Looks like that'll always depend on how long it takes for the service to "completely start". If the service needs to do network operations to "completely start", you might have no guarantee of when the service has "completely started". Instead of simply waiting what it looks like 3,500 milliseconds, you can use interprocess communication.
What is the simplest method of inter-process communication between 2 C# processes?
Basic idea is to get your service process to communicate back to your program, and if it gives back a string that shows the service has started, only then start your client process.

NET VIEW behaves differently when used with Process.Start()

I use net.exe in my program to view all computers in a workgroup.
The code is as follows:
var net = new Process();
net.StartInfo.FileName = "net.exe";
net.StartInfo.CreateNoWindow = true;
net.StartInfo.Arguments = #"VIEW /DOMAIN:my-workgroup";
net.StartInfo.RedirectStandardOutput = true;
net.StartInfo.UseShellExecute = false;
net.StartInfo.RedirectStandardError = true;
net.Start();
The command works fine when I execute in a shell, but when I use the shown code, the command returns the device is not connected.
I also tried running the program as administrator, that makes no difference.
The domain name specified is actually a workgroup.
For net.exe running in the shell specifying a workgroup works fine.
Furthermore the code also works when I try a net view for a different domain. So there must be some difference in the environment when I run the command from the shell or with Process.Start().
What would be the reasons for the command to behave differently in the shell and with Process.Start()?
I don't know if it will solve what you're looking for, but this works for me;
You need to hook into the capture the output to bring it to the console
class Program
{
static void Main(string[] args)
{
var net = new Process()
{
StartInfo = new ProcessStartInfo("net.exe", #"view /domain:domain")
{
RedirectStandardOutput = true,
UseShellExecute = false,
},
};
net.OutputDataReceived += WriteToConsole;
net.ErrorDataReceived += WriteToConsole;
net.Start();
net.BeginOutputReadLine();
net.WaitForExit();
Console.ReadLine();
}
private static void WriteToConsole(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
}

Sending message from one C# console application to another

First of all, I've read all related topics and they gave general idea but implementation doesn't work for me:
Send strings from one console application to another
How to send input to the console as if the user is typing?
Sending input/getting output from a console application (C#/WinForms)
I have a console application that is doing some actions in background until cancellation is requested. Typical usage scenario is :
1) Execute application
2) Enter input data
3) Issue start command
4) After some time passes, enter stop command
5) Exit application
Child application Program.cs :
static void Main()
{
Console.WriteLine("Enter input parameter : ");
var inputParameter = Console.ReadLine();
Console.WriteLine("Entered : " + inputParameter);
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Stopping actions");
return;
}
// Simulating some actions
Console.Write("*");
}
}, token);
if (Console.ReadKey().KeyChar == 'c')
{
tokenSource.Cancel();
Console.WriteLine("Stop command");
}
Console.WriteLine("Finished");
Console.ReadLine();
}
What I'm looking for is some sort of host utility to control this application - spawn multiple instances and perform required user actions on each instance.
Host application Program.cs :
static void Main()
{
const string exe = "Child.exe";
var exePath = System.IO.Path.GetFullPath(exe);
var startInfo = new ProcessStartInfo(exePath)
{
RedirectStandardOutput = true,
RedirectStandardInput = true,
WindowStyle = ProcessWindowStyle.Hidden,
WindowStyle = ProcessWindowStyle.Maximized,
CreateNoWindow = true,
UseShellExecute = false
};
var childProcess = new Process { StartInfo = startInfo };
childProcess.OutputDataReceived += readProcess_OutputDataReceived;
childProcess.Start();
childProcess.BeginOutputReadLine();
Console.WriteLine("Waiting 5s for child process to start...");
Thread.Sleep(5000);
Console.WriteLine("Enter input");
var msg = Console.ReadLine();
// Sending input parameter
childProcess.StandardInput.WriteLine(msg);
// Sending start command aka any key
childProcess.StandardInput.Write("s");
// Wait 5s while child application is working
Thread.Sleep(5000);
// Issue stop command
childProcess.StandardInput.Write("c");
// Wait for child application to stop
Thread.Sleep(20000);
childProcess.WaitForExit();
Console.WriteLine("Batch finished");
Console.ReadLine();
}
When I run this tool, after first input it crashes with "has stopped working" error and prompt to send memory dump to Microsoft. Output window in VS shows no exceptions.
Guess this problem occurs somewhere between applications and may be because of output stream buffer overflow (child app is writing a lot of stars each second which mimics real output which may be huge) and I yet have no idea how to fix it. I don't really need to pass child's output to host (only send start-stop commands to child), but commenting RedirectStandardOutput and OutputDataReceived doesn't fix this problem. Any ideas how to make this work?
I would recommend using NamedPipeServerStream and NamedPipeClientStream, which allows you to open a stream which will communicate between processes on a given machine.
First, this will create a pipe server stream and wait for someone to connect to it:
var stream = new NamedPipeServerStream(this.PipeName, PipeDirection.InOut);
stream.WaitForConnection();
return stream;
Then, this will connect to that stream (from your other process), allowing you to read / write in either direction:
var stream = new NamedPipeClientStream(".", this.PipeName, PipeDirection.InOut);
stream.Connect(100);
return stream;
Another alternative is to use MSMQ, you can find a good tutorial here
I would advise to look to the Working with memory mapped files in .NET 4
http://blogs.msdn.com/b/salvapatuel/archive/2009/06/08/working-with-memory-mapped-files-in-net-4.aspx
It's fast and efficient.

C# Process StandardOutput. How to send Output from the ran EXE?

I have two programs, one is a game and one is a launcher for the game. I created the launcher, in the first place, to receive basic information from the game and detect any kind of exit (crashes, Task Manager process stop, etc)
I will attach my current code for the process runner, it seems like all solutions on the internet, but what I can't figure out is how to make the game send information to the launcher. I tried Console.WriteLine("login=..."); but it doesn't seem to send anything.
private void button1_Click(object sender, EventArgs e)
{
using (Process exeProcess = Process.Start(new ProcessStartInfo() { UseShellExecute = false,
FileName = "Game.exe",
WorkingDirectory = Environment.CurrentDirectory,
RedirectStandardOutput = true}))
{
string output = "";
while (!exeProcess.HasExited)
{
try
{
output += exeProcess.StandardOutput.ReadToEnd() + "\r\n";
}
catch (Exception exc)
{
output += exc.Message + "::" + exc.InnerException + "\r\n";
}
}
MessageBox.Show(output);
}
}
With respect to your code, by adding the following line you can obtain error messages that were thrown by the game.
RedirectStandardError = true,
If you are developing your game in .NET you can return appropriate error codes as follows. Based on the error code you can then display appropriate messages in you launcher
enum GameExitCodes
{
Normal=0,
UnknownError=-1,
OutOfMemory=-2
}
//Game Application
static void Main(string[] args)
{
try
{
// Start game
Environment.ExitCode = (int)GameExitCodes.Normal;
}
catch (OutOfMemoryException)
{
Environment.ExitCode = (int)GameExitCodes.OutOfMemory;
}
catch (Exception)
{
Environment.ExitCode = (int)GameExitCodes.UnknownError;
}
}
NOTE: You can take a look at this open source game launcher developed in C# as a reference or modify it as per your needs.
EDIT: Added info as per comment
There are multiple ways to enable communication between between 2 .NET processes. They are
Anonymous Pipes
Named Pipes
Using Win32 WM_COPYDATA
MSMQ

Categories