I'm trying to launch a game server (Game: Ark Survival Evolved) via a C# application. (Some kind of wrapper).
This game server is a console application with some output about the current state of the server.
I want to read the output in order to react to it. That's why the reading must happen asynchronously. (I can't wait untill the server has stopped)
My current approach is the following:
public void RunServer()
{
if (Process.GetProcessesByName("ShooterGameServer").Length <= 0)
{
Process p = new Process();
// redirect output stream
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += (sender, args) => Display(args.Data);
// set Server.exe path
p.StartInfo.FileName = path_server;
// run server
p.Start();
// reading the console
p.BeginOutputReadLine();
p.WaitForExit(); // I've tried it without this line but it doesn't really help
}
}
// Show the console output
void Display(string output)
{
Console.Out.WriteLine(output);
}
The server exe starts perfectly fine, but it stops writing to the console, as soon as the StdOut should appear. (there are some StdErr messages before the StdOut messages which are still shown in the console window).
This is understandable, because I've only enabled p.StartInfo.RedirectStandardOutput = true in my code, so the StdErr channel is not affected by it.
The problem is that the redirected Output never shows up in the Display function. It just vanishes into thin air.
If I put a breaking point into the Display function it never gets called untill I close the server.exe. After that it gets called, but the parameter is null.
Unfortunately I don't have any further knowledge about the game server exe.
What am I missing ?
Edit: Turns out that redirecting the output of other exe files (e.g. cmd exe) works just fine. Is there another way I could read the console in my C# application? Maybe just "copy" the output instead of redirecting it completely?
Related
I have a C# Console application which I am trying to execute from another WinForm application just like batch runner by giving the console application's .exe file like below.
Process.Start("Path of Console application exe to execute")
However I need to wait and handle the output and display the output in WinForm's richtextbox from console application once it has completed the execution. How can I achieve this?
Update
I have changed the code to Start a Process and Read using StandardOutput and BeginOutputReadLine() to Read the output asynchronously, but not able to see output in console window, instead console window is getting closed. Not sure how to solve this.
p.StartInfo.UseShellExecute = false;
// p.StartInfo.CreateNoWindow = True
p.StartInfo.RedirectStandardOutput = true;
string #out = null;
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
#out += e.Data;
});
p.StartInfo.FileName = currentTest;
p.Start();
p.BeginOutputReadLine();
// string output = p.StandardError.ReadToEnd();
// p.WaitForExit()
while (!p.HasExited)
Application.DoEvents();
//Console.WriteLine($#"Output \n'{output.Substring(output.Length - 50)}'");
Console.WriteLine($#"\n Error stream: {#out}");
Console.ReadLine();
You need to redirect stdout (and probably stderr) so that any output comes to you, instead of a console; you may also want to redirect stdin. All of these things are available via ProcessStartInfo, with an example on MSDN. Note that if you want to display updates while the exe is running, you may need a worker thread to read incrementally from StandardOutput and StandardError, rather than ReadToEnd() - which won't return anything at all until the associated output pipe is closed.
However! If the console exe is "yours", it may be simpler to just expose the functionality you want in a library, and invoke it directly in-process. There are times when out-of-process is actively preferred, such as when you need to allow that process to go catastrophically wrong in some scenarios - but usually in-process is preferable, given free rein.
i am invoking an exe via c# Diagnostics.Process class and read output from it's StdOut. The process is forcefully terminated in case it doesn't automatically terminates in a specified time, something like:
process.StartInfo.FileName = #"D:\t.exe";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.WaitForExit(500);
if (!process.HasExited)
{
process.Kill();
process.WaitForExit();
}
string stdOutContents = process.StandardOutput.ReadToEnd();
Now the problem is the code works succesfully when the exe terminates normally. But in case it fails to terminate normally (usually the exe gets stuck in an infinite loop somewhere), stdOutContents is read as an empty string.
How can i read StdOut after the process is killed (without using process.OutputDataReceived event technique)? (It has been verified that the exe-in-question does always writes something onto StdOut even if it gets stuck somewhere).
Update 1
Details about Exe which is being invoked (refereed as 'native app' across this question)
It is a small utility implemented in c language and compiled using MS C++ compiler. It does its job while simultaneously outputting status information onto the StdOut (using putchar).
There are only two possible cases of operation:
It will run successfully while simultaneously printing some data onto the StdOut.
It will run normally to a certain point (simultaneously outputting data on StdOut) and then get stuck in an infinite loop. (This is an acceptable behavior).
Both scenarios have been verified using cmd.
Details about new attempts
i wrote a c# app (referred as dummy app) which mimics the native app behavior and this code works fine. However when run for the native app, i get nothing at all.
i don't understand why the code cant read the contents outputted by the native app?
i also tried using event handler for OutputDataReceived. It gets called only once with args.Data = null when the code tries to kill the process. Inspecting the behavior for dummy app revealed that when process.kill is called, the handler is invoked with args.Data = null. So this seems to be a standard behavior of sorts for both apps.
i also tried changing the newline characters for native app. Since it is implemented in c language, it uses \n for newline. i tried using both \r\n pair for newline but StdOut is still blank (for case 2).
I had the same interrogation and the doc of Process.Kill says
Data edited by the process or resources allocated to the process can be lost if you call Kill.
Which seems to indicate that you cannot rely on reading the StandardOutput of a process, although it is not clearly stated that the output / error streams are disposed.
I finally got inspired by this answer
How to spawn a process and capture its STDOUT in .NET?
and I use the following code :
var info = new ProcessStartInfo("some.exe");
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
using (var p = new Process())
{
p.StartInfo = info;
var output = new StringBuilder();
p.OutputDataReceived += (sender, eventArgs) =>
{
output.AppendLine(eventArgs.Data);
};
p.Start();
p.BeginOutputReadLine();
if (!p.WaitForExit(5000))
{
Console.WriteLine("Taking too long...");
p.Kill();
Console.WriteLine("Process killed, output :\n" + output);
}
}
Same pattern can be used with the ErrorDataReceived
Note that one could miss some unflushed output from the child process, however in my case I don't expect much from a process that requires to be killed, at most some information for debugging purposes.
I've been reading on how to execute Win32 command-line programs from within the C# ASP.NET application, and for some reason, this code below never completes during execution, being stuck indefinitely on output = p.StandardOutput.ReadToEnd(); line.
The code works well when executed from a local debugging server (pressing F5 from Visual Studio), but when it's accessed through the browser via full URL (myexample.com/testapp), debugger never moved past the output=... line.
ProcessStartInfo info = new ProcessStartInfo(#"FetchIntranetPageSpecial.exe");
info.Arguments = "hostname";
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.CreateNoWindow = true;
Process p = System.Diagnostics.Process.Start(info);
p.Start();
// Capture the output
output = p.StandardOutput.ReadToEnd(); // debugger does not move past this line
p.Close();
The .exe is a straightforward http request app, does not access to local files or anything, outputs result to console.
I thought this could be a permissions issue at first, so for debugging purposes set FetchIntranetPageSpecial.exe permissions to "EVERYONE, EVERYTHING" - still works fine when accessed through localhost, but still hangs when accessed remotely.
Any pointers on what I could try next?
EDIT: I also read this page Program doesn’t terminate when using processes, but in that case, the debugger goes into indefinite "waiting" state on this line:
while (read.Peek() >= 0) // stuck on this line
Console.WriteLine(read.ReadLine());
there are a lot of examples that show how to redirect the stdout of another application. However I would like to let the application keep its stdout and only retrieve a copy of the stdout in my parent process. Is this possible?
My scenario: I have some tests (using Visual Studio Test Runner) which start an external process (server) to do their testing. The server outputs a lot of useful debug information in its stdout which I would like to include in my test results.
I can capture the process output and output it via Trace.WriteLine to let it show up in the test details later. However it would be nice to see the server window with its output while the test is running to see the current progress (test can be running a long time).
So I'm looking for ways to copy this information instead of simply redirecting it.
Any ideas?
Would this work for you?
var outputText = new StringBuilder();
var errorText = new StringBuilder();
using (var process = Process.Start(new ProcessStartInfo(
#"YourProgram.exe",
"arguments go here")
{
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false
}))
{
process.OutputDataReceived += (sendingProcess, outLine) =>
{
outputText.AppendLine(outLine.Data); // capture the output
Console.Out.WriteLine(outLine.Data); // echo the output
}
process.ErrorDataReceived += (sendingProcess, errorLine) =>
{
errorText.AppendLine(errorLine.Data); // capture the error
Console.Error.WriteLine(errorLine.Data); // echo the error
}
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
// At this point, errorText and outputText StringBuilders
// have the captured text. The event handlers already echoed the
// output back to the console.
}
What about writing a small program that forwards STDIN to STDOUT, while at the same time doing something else with it as well?
You could then replace the command that starts the server process with one that starts it and pipes its output to the above utility. That way you 'll have both programmatic access to the output and see it in real time in the output window.
I have an issue with getting some console standard output from a small long running console app spawned by my application.
My app starts the console app and the console app will stay alive listening on a port for the duration of my applications lifetime (or until explicitly killed).
When the console app starts, it outputs a port number that it is listening on, I need to asyncronously grab that port number and use it elsewhere in the app. Problem is, my event handler to get the output data is never called. I am sure there must be something trivial I am forgetting to do.
ProcessStartInfo si = new ProcessStartInfo();
si.FileName = theFileName;
si.Arguments = theargs;
si.UseShellExecute = false;
si.RedirectStandardOutput = true;
si.CreateNoWindow = true;
_process = Process.Start(si);
_process.OutputDataReceived += (sender, args) =>
{
//Parse the args.Data string for the port number.
};
_process.BeginOutputReadLine(); // My handler never gets called.
// I don't want to call _process.WaitForExit() here as this is a long running process.
Ok in my particular case the issue was caused by the console app not flushing stdout.
The console app was written in python (then made runnable in windows by py2exe), it required...
sys.stdout.flush()
To be called before we could get any output prior to app exit.