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

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

Related

System.Diagnostics.Process.HasExited always returns true and Process.Exited event handler always hits when process is still open

I am currently having issues with a piece of my code. This code works perfectly fine (even without some of the extra if statements) on my machine when I run it both through Visual Studio 2013 and when I publish using ClickOnce. On the client's machine, the event handler for the process exiting catches, the process ID is correct, and the process.hasExited returns true all while the process is still open. The process opens a pdf for them to sign and waits for them to save it and close it before proceeding. It uses Adobe Acrobat Reader DC.
The error message received is from the catch block of the event handler method:
System.IO.IOException: The process cannot access the file because it is being used by another process.
This occurs at the call for getReaders_NewHire(emp).
This is the method that opens the process:
private void pdfAndEmail(Employee employee, Requirements requirements)
{
try
{
PDFUtility pdfu = new PDFUtility();
pdfu.createPDFMG_NewHire(employee, requirements);
emp = employee;
process = new System.Diagnostics.Process();
string path = System.IO.Path.GetTempPath() + "\\ITResources\\Manager\\NewHire_" + employee.m_name.Replace(" ", "") + ".pdf";
Uri pdf = new Uri(path, UriKind.RelativeOrAbsolute);
process.StartInfo.FileName = pdf.LocalPath;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(process_Exited);
process.Start();
pid = process.Id;
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
and this is the event handling method:
private void process_Exited(object sender, EventArgs e)
{
try
{
if (process.Id == pid)
{
if (process.HasExited)
{
PDFUtility pdfu = new PDFUtility();
pdfu.getReaders_NewHire(emp);
Emailer send = new Emailer();
send.SendAfterMG_NewHire();
}
else
MessageBox.Show("Process has not exited.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
The getReaders method in the event handler needs to read the file that the client has to sign so it must be closed beforehand. I cannot force a close because I will never know how long it will take them to sign and I can't have the email being sent out before they've signed and saved the pdf.
I've already tried using the WaitForExit() method and it skips completely (only on my client's computer). I am running Windows 10, but the client is running Windows 7. I have not been able to find any documentation about these methods not working on Win7.
Note: I understand that the if statements in the event handler method are a bit redundant, but I was desperate to find where it was catching.
Any help would be greatly appreciated.
FIXED: I ended up have a Message Box pop up after my PDF was signed to assist the WaitForExit() in the Background Worker thread. After this message box's OK button is pressed (because they pause the application and wait for a response), it then reads the pdf files.
It uses Adobe Acrobat Reader DC.
This is your advantage I think, and my approach assumes this. Consider the following:
private static void Main()
{
var process = Process.GetProcessesByName("AcroRd32").FirstOrDefault();
if (process != null)
{
Process.Start(#"C:\mvvm.pdf");
}
else
{
process = Process.Start(#"C:\mvvm.pdf");
}
process.EnableRaisingEvents = true;
process.Exited += (sender, args) =>
{
Console.WriteLine("Exited");
};
Console.ReadLine();
}
First we check if Adobe process is already running, if so we keep the reference and hook the event handler to that existing process. If not we do what you have already done. Some validation code is omitted.
I have tested with the process running and not running and it works.
NOTICE: It will not work as expected if the user has another application configured as a predefined PDF reader

WPF programme not running correctly when run silently

I have written a small programme to perform a quick configuration on a client machine and it needs to be able to run with a GUI and silently from the command line. If I run it with the GUI then it works perfectly, if however I try to run it without then it just hangs.
I have traced the problem to this section of code:
string arg = "/C:\"setup.exe /qn ADD_OPINSIGHTS_WORKSPACE=1 OPINSIGHTS_WORKSPACE_ID=" + workSpaceID + " OPINSIGHTS_WORKSPACE_KEY=" + workSpaceKey + " AcceptEndUserLicenseAgreement=1\"";
log.Info(arg);
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "MMASetup-AMD64.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = arg;
try
{
log.Info("try entered");
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
log.Info("Install started");
exeProcess.WaitForExit(30000);
log.Info("Install exit code: " + (exeProcess.ExitCode).ToString());
return (exeProcess.ExitCode).ToString();
}
}
catch (Exception e)
{
log.Error("MMA install threw an error: ", e);
return e.Message;
}
This method is in a seperate class to the GUI and silent code and is run in exactly the same way yet only reaches "Install started" when run silently. I know that the exe does finish so I have tried using the code in this solution but had the same problem:
ProcessStartInfo hanging on "WaitForExit"? Why?
I had the same Problem.
I made a startup class:
public partial class Startup {
// WPF App
private App _app;
[STAThread]
public static void Main(string[] args) {
try {
//Do what you need
//Check the args
//Start your setup silent
//start the WPF App if need it
this._app = new App();
this._app.InitializeComponent();
this._app.Run();
} catch (Exception ex) {
//Logging ex
}
}
After that you must change your Application Startup Object to the Startup Class.
I was running all of my work asynchronously and because I was not loading the GUI thread Windows was treating the application like a console app. Whereas a GUI thread would call other asynchronous methods and wait for them to finish a console application calls the methods and then closes because it has nothing left to do. The solution was to explicitly make the main thread wait like this:
public static void Main(string[] args)
{
try
{
Install().Wait();
}
catch (Exception ex)
{
}
}
private static async Task Install()
{}

Out of memory when starting process from c# and mono

I am working with an embedded linux board (Atmel AT91SAM) with Debian installed. There is a mono application running and its switching an output from GPIO on/off periodicly (like every 500ms) by calling cat by starting a new System Process with bash. After some time I get an "Out of memory" exception and output is not set for a time, after it "recovers itself" after some seconds.
Is this a bad idea to set the pioC10 this way? If so, how else should
I set the value file?
Could this memory exception occur, because of
not being able to access the GPIO at the moment?
Is there a clean way to dispose the process, or is it done when method is closed automatically?
private void OnTimer1Event(object source, ElapsedEventArgs e)
{
alternateBlinkTimer1.Stop();
alternateBlinkTimer2.Start();
try
{
var processStartInfo = new ProcessStartInfo { FileName = "/bin/bash", Arguments = "-c \"echo 1 > /sys/class/gpio/pioC10/value\"" };
System.Diagnostics.Process process = new Process();
process.StartInfo = processStartInfo;
process.Start();
}
catch (Exception err)
{
log.Error(err.Message);
}
}
You can try skipping the Process Class and any memory management/garbage collection associated to it by directly use libc's system.
In your Class that you are defining the OnTimer1Event method, add a definition to system:
[DllImport ("libc")]
private static extern int system (string exec);
Then you can:
private void OnTimer1Event(object source, ElapsedEventArgs e)
{
alternateBlinkTimer1.Stop();
alternateBlinkTimer2.Start();
system("echo 1 > /sys/class/gpio/pioC10/value\");
}

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

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);
}
}
}
}

Killing Java Process from C# Console App

I posted about this a little while ago, but I resolved the other issue and ran into one more. I am about to deploy this program to 28 hosting machines so I want to make sure this is working before I do so.
I wrote a little c# NET application that is basically a wrapper for a Java application, when my app starts, the Java app starts, when my app closes, it closes, and so on.
Everything works properly except that when I close my application, the Java application continues to run. When I create the process, I store the Process var in a variable outside of the methods, and then use that when my application goes to shutdown. For whatever reason though it is not terminating the Java application.
class Program
{
private static Process minecraftProcess;
public static void LaunchMinecraft(String file, String memoryValue)
{
String memParams = "-Xmx" + memoryValue + "M" + " -Xms" + memoryValue + "M ";
String args = memParams + "-jar " + file + " nogui";
ProcessStartInfo processInfo = new ProcessStartInfo("java.exe", args);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
try
{
//using (Process minecraftProcess = Process.Start(processInfo))
using (minecraftProcess = Process.Start(processInfo))
{
minecraftProcess.WaitForExit();
}
}
catch
{
// Log Error
}
}
static void Main(string[] args)
{
Arguments CommandLine = new Arguments(args);
// Hook ProcessExit Event
AppDomain.CurrentDomain.ProcessExit += new EventHandler(Current_ProcessExit);
if (CommandLine["file"] != null && CommandLine["memory"] != null)
{
// Launch the Application (Command Line Parameters)
LaunchMinecraft(CommandLine["file"], CommandLine["memory"]);
}
else
{
// Launch the Application (Default Parameters)
LaunchMinecraft("minecraft_server.jar", "1024");
}
}
static void Current_ProcessExit(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(10000);
// If we have an active Minecraft Service, Shut it down
if (minecraftProcess != null)
{
minecraftProcess.Kill();
}
}
}
You can't Sleep in a ProcessExit handler.
The documentation states:
The total execution time of all
ProcessExit event handlers is limited,
just as the total execution time of
all finalizers is limited at process
shutdown. The default is two seconds.
An unmanaged host can change this
execution time by calling the
ICLRPolicyManager::SetTimeout method
with the OPR_ProcessExit enumeration
value.
Nevermind, I just realized the minecraftProcess variable is static.
Don't know if you did not solve this issue by yourself but:
You should be aware that there are Start methods for instances (returning bool) and static (returning a object).
You should not use using with something other than using-local variables!
Just this should work fine:
minecraftProcess = Process.Start(processInfo)
minecraftProcess.WaitForExit();

Categories