C#-C Interprocess communication - c#

Okay, so I have a program written in c# that starts up a process running another program, this one written in C. Now I already worked out redirecting the stdout from the C program and picking it up on the C# side, and it works great. But I need a way for the C# program to tell the C program to end. My first thought, was that since the C program originally was controlled through testing kbhit from conio.h, that I could just redirect stdin and streamwrite something and it would detect a key hit, but that didn't work (I'm guessing kbhit doesn't lend itself to redirection), so I searched around and a lot of people kept suggesting PeekConsoleInput(), because I'm writing this program exclusively for windows (in fact a specific machine). So I changed the c program to use PeekConsoleInput instead of kbhit and redirected stdin and it still didn't detect anything I sent to it. For example in C...
#include <stdio.h>
#include <windows.h>
int main() {
BOOL peeked = 0;
HANDLE input_handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events = 0; // how many events took place
INPUT_RECORD input_record; // a record of input events
DWORD input_size = 1; // how many characters to read
printf("Hello World\n");
fflush(stdout);
while (1) {
FlushConsoleInputBuffer(input_handle);
peeked = PeekConsoleInput(input_handle, &input_record, input_size, &events);
if (peeked && events>0) {
break;
}
}
printf("Hit Detected\n");
return 0;
}
and a snippet from the C# code...
ProcessStartInfo info = new ProcessStartInfo("CTest.exe");
info.CreateNoWindow = true;
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardInput = true;
pro = new Process();
pro.StartInfo = info;
pro.Start();
StreamWriter sw = pro.StandardInput;
sw.AutoFlush = true;
pro.BeginOutputReadLine();
pro.OutputDataReceived += new DataReceivedEventHandler(pro_OutputDataReceived);
sw.WriteLine("Q");
The C program executes by itself, but when the C# program runs it, it never reaches "Hit Detected". I also delayed the s.WriteLine("Q") by a few seconds to see if it was a timing issue and it still didn't work.
Ideally, I wanted the C program to be able to run by itself, or be run by the C# program, but even if you couldn't run the C program by itself, that wouldn't be so bad.
One thing I tried that worked, was to have the C# program just write a file, and have the C program just poll with fopen's until it successfully opens it, but one of the main purposes of this C program is to write to data to disk really fast, and I worry that polling the disk may be slowing it down.
Another thing that kinda worked was just closing the process. But that's messy because the C program needs to clean up some stuff before it closes (unless there's some way to get the C program to execute some code before it shuts down, but I'm not sure if you can do that).
Other ways to get this done would be sockets, pipes, etc. but it seems like a lot of work for just a one bit signal. Also, all the examples I could find about how to do that all seemed to be how to get two C# programs to communicate or how to get two C programs to communicate, never two different programs in two different languages.
So, firstly, is there any way to get this stdin redirection thing working? and if not, what's the easiest solution to tell the C process that it needs to exit?

I ran into the same problem and was able to get it to work using SendKeys. Just add a reference to System.Windows.Forms if you have a WPF app.
For the processes StartInfo I used:
newProcess.StartInfo.UserShellExecute = false;
newProcess.StartInfo.CreateNoWindow = false;
newProcess.StartInfo.RedirectStandardInput = false;
newProcess.StartInfo.RedirectStandardOutput = true;
newProcess.StartInfo.RedirectStatndardError = true;
newProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
newProcess.OutputDataReceived += new DataReceivedHandler(gotData);
The reason for setting CreatNoWindow to false is that you can find the Window in Spy++. ProcessWindowStyle.Hidden hides the window after it pops up for a brief moment.
RedirectStatndardInput must be false because we are using SendKeys to simulated keyboard input to the hidden window.
Using SendKeys.waitSend("command{ENTER}"); I was able to send a command to the console app that uses _kbhit() and getc() for input.

Have you tried to see if and by how much the C program is faster at writing files?
There are other ways (in Windows) to do interprocess communication.
Named Pipes (similar to TCP)
Memory mapped files (might be a good option here - similar to your
other experiment which worked)
TCP/IP (as you've suggested.)
As far as TCP/IP is concerned it is not constrained to works with software build using the same language and tool. Your C program will be the TCP server and your C# program will be the TCP client. If you know how to do this in either of the languages you've got your answer.

You could use the Process object to kill the spawned process:
(assuming that the C program has a handler to do its cleanup after receiving the "CloseMainWindow()" signal)
/// <summary>
/// Close the shelled process, if there is any
/// </summary>
/// <param name="reason">why closing?</param>
public static void shellTerminate(string reason)
{
Process p = shelledProcess;
try
{
if (p == null)
{
o3(reason + ". No shelled process.");
}
else if (p.HasExited)
{
o3(reason + ". Process has exited already.");
}
else
{
//Process is still running.
//Test to see if the process is hung up.
if (p.Responding)
{
//Process was responding; close the main window.
o3(reason + ". Process is being closed gracefully.");
if (p.CloseMainWindow())
{
//Process was not responding; force the process to close.
o3(reason + ". Process is being killed.");
p.Kill();
p.Close();
p = null;
}
}
if (p != null)
{
//Process was not responding; force the process to close.
o3(reason + ". Process is being killed.");
p.Kill();
}
p.Close();
}
if (shellOut != null)
{
shellOut.Close();
shellOut = null;
}
shelledProcess = null;
}
catch (Exception ex)
{
o("Exception in shellTerminate: " + ex.Message);
}
}

Related

How to capture the output of a process (from OR Tools solver) and display it through console

I have a C# winform application in which an optimization model is solved by OR-Tools. The optimization solver has the capability of sending the whole optimization process as stdout.This is done by:
Slvr.EnableOutput();
Solver.ResultStatus restatus = Slvr.Solve();
However, the solver does not automatically open up the console.
Currently, what I have done is:
Projects properties --> Application --> Output type --> Console Application
and the console is ready from the beginning till the end of the application run. Hence, that process stdout is automatically displayed.
What I want is to open the console exactly when the above part of code is run and display the stdout from the solver. Then wait for a key from the user to close the console and continue with the main application.
I guess your problem is you are trying to run the solver as part of the Winforms application, inside the GUI process right? But Console output is usually disabled in a Winforms application. You have basically two options:
use one of the options described here in this older SO answer to attach a console window for output to a Winforms application
split the application into two exe files: one command line program which runs the solver, and a Winforms part, just containing the UI. Then run the command line part as a separate process by System.Diagnostics.Process.Start, which allows finegrained control about output redirection. You may need the UI to pass parameters to the command line program, for example, by using a temporary file.
The second option is more work, especially for the communication between the GUI and the command line tool, but can be implemented easier in a way the GUI is not blocked, is more robust against bugs / program crashes in the solver part and performs usually better in case you want to introduce parallelization / run multiple solver processes at once.
Doc Brown has already answered your question, I'm only adding this to provide some code of how we implemented it here-- it's exactly what he suggests. We have a separate testPlugin.exe that get's started here. The communication is via files read and written on the file system. The console output gets captured in the "output handlers"
using System;
using System.Diagnostics;
using System.IO;
...
private void startTest()
{
int result = 2;
setFormStatus("working..."); // My method to inform the user with the form to wait.
getFormData(); // My method to get the data from the form
string errorMessage = null;
System.Diagnostics.Process testPlugInProcess = new System.Diagnostics.Process();
try
{
using (testPlugInProcess)
{
testPlugInProcess.StartInfo.UseShellExecute = false;
testPlugInProcess.StartInfo.FileName = System.IO.Path.Combine(assemblyDirectory, TestPlugInExe); // The name of the exe file
testPlugInProcess.StartInfo.CreateNoWindow = false;
testPlugInProcess.StartInfo.Arguments = getModelTestCommandLineArgs(); // My method to create the command line arguments
testPlugInProcess.StartInfo.RedirectStandardError = true;
testPlugInProcess.StartInfo.RedirectStandardOutput = true;
testPlugInProcess.OutputDataReceived += pluginTestOutputHandler;
testPlugInProcess.ErrorDataReceived += pluginTestOutputHandler;
testPlugInProcess.Start();
testPlugInProcess.BeginErrorReadLine();
testPlugInProcess.BeginOutputReadLine();
testPlugInProcess.WaitForExit();
result = testPlugInProcess.ExitCode;
}
setFormStatus("");
}
catch (Exception ex)
{
errorMessage = ex.Message;
}
testPlugInProcess = null;
}
Both the console and error output get written to the same file here, but you could separate them.
The plug-in handler looks like this:
private static void pluginTestOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
for (int i = 0; i < numberOfTriesForWriting; i++)
{
try
{
using (StreamWriter sw = File.AppendText(lastPlugInTestTraceFilePath)) // The file name where the data is written.
{
sw.WriteLine(outLine.Data);
sw.Flush();
return;
}
}
catch (IOException)
{
System.Threading.Thread.Sleep(msToWaitBetweenTries);
}
}
}
}

How to read console output of a process without redirecting standard output?

I'm writing a GUI for a third-party console application and I wanted it to capture the output of the console window and add it to a text box in the GUI. This seemed like it was simple, all I had to do was to redirect the output stream of the target process.
But, when I do that the console application throws the error:
CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents
The current code I have which causes this error is this:
// This gets called once after the application has initialized.
private void StartServer()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = srcdsExeFile;
processStartInfo.UseShellExecute = false;
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
serverProcess = Process.Start(processStartInfo);
serverProcess.EnableRaisingEvents = true;
serverProcess.Exited += new EventHandler(Server_Exited);
serverProcess.OutputDataReceived += ServerProcess_OutputDataReceived;
serverProcess.ErrorDataReceived += ServerProcess_ErrorDataReceived;
serverProcess.BeginOutputReadLine();
serverProcess.BeginErrorReadLine();
}
// This is (like seen above) an event handler for serverProcess.ErrorDataReceived.
private void ServerProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine("\n\nServer Error: " + e.Data + "\n\n");
}
// This is (like seen above) an event handler for serverProcess.OutputDataReceived.
private void ServerProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.Output.WriteLine(e.Data);
}
The above code does work for a minute or so while the external application is doing its initialization, but crashes after a specific point in the initialization process.
After doing some research it turns out that the third-party console application relies on the output stream to be a console, which is why it crashes when I try to redirect it. Trying to access the output stream without redirecting it also causes an error saying I have to redirect it first.
Which brings me to my actual question:
Is it possible to read the output of the console application without redirecting the output stream?
So this has been asked several times in the past couple of years.
I just run into the same issue and solved it in C++ but the same technique should apply to any other programming language since this is a WinAPI specific problem.
I've describe a solution for anyone that wishes to create an srcds server using CreateProcess and redirect input & output on windows.
This github repo put together how Console Handls & Standard Handles work together inside windows.
https://github.com/rprichard/win32-console-docs
Also the microsoft documentation of
https://learn.microsoft.com/en-us/windows/console/creation-of-a-console
I highly recommend to read about this as this makes it very obvious why srcds fails when redirecting the standard input.
The problem
a) Windows Console Handle is not equal to StandardInput and Output Handles.
b) And in windows there is no way to redirect Console handles.
c) GetNumberOfConsoleInputEvents requires a valid console handle with an input handle that is not a file, pipe. It must be a ConsoleHandle!
Since no one actually looks at why GetNumberOfConsoleInputEvents fails while it should be obvious after reading the documentation.
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
It clearly states:
hConsoleInput [in]
A handle to the console input buffer. The handle must have the
GENERIC_READ access right. For more information, see Console Buffer
Security and Access Rights.
But here
https://github.com/rprichard/win32-console-docs#allocconsole-attachconsole-modern
it's explained that when you redirect the pipe it pretty much breaks the console input buffer. So you have to actually work with the console input buffer and not with the StdHandles.
The solution
Luckily the WinAPI provides several options for us to get access to an existing process's std handles. It's just very tricky and not well documented!
You can attach to a Console and grab the STDHandles. Duplicate them and do whatever you like.
Note that AttachConsole(ProcessId) requires you that the current process has no console attached so you must call FreeConsole();
Here's a code how to send a single letter to an another application's console using WinAPI. You can also grab the console handle and write to it any time Using GetStdHandle.
int ProcessId = GetProcessId(ProcessInfo.hProcess);
if (ProcessId <= 0)
{
printf("Process terminated.\n");
break;
}
printf("Process Id: %d\n", ProcessId);
FreeConsole();
if (!AttachConsole(ProcessId))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
INPUT_RECORD ir[2];
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = FALSE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = 'u';
ir[1].Event.KeyEvent.wRepeatCount = 1;
ir[1].Event.KeyEvent.wVirtualKeyCode = 'U';
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('U', MAPVK_VK_TO_VSC);
DWORD dwTmp = 0;
WriteConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), ir, 2, &dwTmp);
FreeConsole();
if (!AttachConsole(ATTACH_PARENT_PROCESS))
{
printf("Attach failed with error: %d\n", GetLastError());
exit(1);
}
So the solution is simply to to write to the Console Input Buffer by attaching to the console of an SRCDS process.
Simply call AttachConsole and FreeConsole. and WriteConsoleInput.
To read the ouptut you can just call ReadConsoleOutput
For further reading visit the documentation:
GetNumberOfConsoleInputEvents
https://learn.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents
AttachConsole
https://learn.microsoft.com/en-us/windows/console/attachconsole
FreeConsole
https://learn.microsoft.com/en-us/windows/console/freeconsole
WriteConsoleInput
https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
ReadConsoleOutput
https://learn.microsoft.com/en-us/windows/console/readconsoleoutput

Executing java program and reading its output programmatically. Without executing java.exe(java application launcher)

we have a web application, which helps learners to learn by practice. So there is a module for java as well. For this we require executing the java program written by user and reading its output and displaying it to user.
Currently we use a C# service to do this. We save the java program in a .java file on server and compile it by executing javac.exe command and then run it by executing java.exe and read the output from console and transferring it back to user.
public String ExecuteProgramFetchOutput(String classpath, String filename){
String output = "";
String command = "java -classpath " + classpath + " " + filename;
System.Diagnostics.Process _objProcess = new System.Diagnostics.Process();
try
{
System.Diagnostics.ProcessStartInfo _objProcessInfo = new System.Diagnostics.ProcessStartInfo();
_objProcessInfo.FileName = "cmd.exe";
_objProcessInfo.RedirectStandardInput = true;
_objProcessInfo.RedirectStandardOutput = true;
_objProcessInfo.RedirectStandardError = true;
_objProcessInfo.CreateNoWindow = true;
_objProcessInfo.UseShellExecute = false;
_objProcess.StartInfo = _objProcessInfo;
_objProcess = System.Diagnostics.Process.Start(_objProcessInfo);
_objProcess.StandardInput.AutoFlush = true;
_objProcess.StandardInput.WriteLine(command);
_objProcess.WaitForExit(3000);
if (!_objProcess.HasExited)
{
_objProcess.Kill();
}
output = _objProcess.StandardOutput.ReadToEnd();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
output = ex.ToString();
}
finally
{
if (_objProcess != null)
{
if (!_objProcess.HasExited)
{
_objProcess.Kill();
}
_objProcess.Dispose();
}
}
return ouput;
}
I have only given the code for execution similar kind of code is there for compilation. The only problem with the above approach is that when someone runs the java program with an infinite loop there is a java.exe running on the server and taking CPU percentage of 50% and more, After that any request which comes fails!!
We employed a solution to that as well by running a separate thread which checks in the system what and all "java.exe"s running for more than 3 secs and terminating them.
Still this approach will start failing as the number of users will start increasing. As if 10 users simultaneously execute a program the server will not be able to handle it.
I am looking for a solution which can compile and run the java program without running the java.exe. The solution can be either in java or c# (preferably c#). I found some code for compiling using java by using JavaCompiler class, I also found code for executing the program using reflection API, but I could not find how to read the output after executing.
Please help!! Thanks in advance...
I agree with user2613329, but you could still follow the java.exe approach. You could queue user execution and limit the number of running executions.
You can write your own Java application that will periodically load incoming .java files, compile them, and then run their main methods in separate threads. You can then limit these threads in execution time, for example using an ExecutorService (see Killing thread after some specified time limit in Java).

Reading StdOut after process has been abnormally terminated using Process.kill

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.

programmatically kill a process in vista/windows 7 in C#

I want to kill a process programmatically in vista/windows 7 (I'm not sure if there's significant problems in the implementation of the UAC between the two to make a difference).
Right now, my code looks like:
if(killProcess){
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
myProcess = System.Diagnostics.Process.Start(psi);
}
I have to do this because I need to make sure that if the user crashes the program or exits abruptly, this secondary process is restarted when the application is restarted, or if the user wants to change the parameters for this secondary process.
The code works fine in XP, but fails in Windows 7 (and I assume in Vista) with an 'access is denied' message. From what the Almighty Google has told me, I need to run my killing program as administrator to get around this problem, but that's just weak sauce. The other potential answer is to use LinkDemand, but I don't understand the msdn page for LinkDemand as it pertains to processes.
I could move the code into a thread, but that has a whole host of other difficulties inherent to it that I really don't want to discover.
You are correct in that it's because you don't have administrative priveleges. You can solve this by installing a service under the local system user and running a custom command against it as needed.
In your windows form app:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "SERVICE_NAME");
scp.Assert();
System.ServiceProcess.ServiceController serviceCon = new System.ServiceProcess.ServiceController("SERVICE_NAME", Environment.MachineName);
serviceCon.ExecuteCommand((int)SimpleServiceCustomCommands.KillProcess);
myProcess = System.Diagnostics.Process.Start(psi);
In your service:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
protected override void OnCustomCommand(int command)
{
switch (command)
{
case (int)SimpleServiceCustomCommands.KillProcess:
if(killProcess)
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
}
break;
default:
break;
}
}
I'll add the code for Simon Buchan's suggestion. It makes sense and should work as well, assuming your windows form is what launched the process in the first place.
Here's where you create the process. Notice the variable myProc. That's your handle on it:
System.Diagnostics.Process myProc = new System.Diagnostics.Process();
myProc.EnableRaisingEvents=false;
myProc.StartInfo.FileName="PATH_TO_EXE";
myProc.Start();
Later, just kill it with:
myProc.Kill();

Categories

Resources