Killing a C# process gracefully not working - c#

I have an application that spawns multiples threads, one of which runs an iPerf executable which is used to monitor network reliability. This process will run indefinitely, until the user attempts to close the window. This is where the issue comes in. I am trying to shut that process down gracefully so that the iPerf server does not get hung up, but I can not seem to get this working. I can shut it down just fine if I run the command manually from a command prompt and press Ctrl+c, but this does not seem to be easily done programmatically.
I have tried multiple things, including process.Kill(); or process.StandardInput.Close() or even process.StandardInput.WriteLine("\x3"); but none of these seem to send a graceful shutdown message to the process. process.Kill(); causes the server to hang or fail to start up the next time, and the other two options do not stop the server at all. But the manual ctrl+c works just fine.
Here is a snippet of my code:
iperf_proc = new Process();
iperf_proc.StartInfo.FileName = Application.StartupPath + ".\\iperf3.exe";
String argumentStr = " -c " + test_data.host + " -t 0";
iperf_proc.StartInfo.Arguments = argumentStr;
iperf_proc.StartInfo.UseShellExecute = false;
iperf_proc.StartInfo.RedirectStandardOutput = true;
iperf_proc.StartInfo.RedirectStandardInput = true;
iperf_proc.StartInfo.RedirectStandardError = true;
iperf_proc.Start();
iperfRunning = true;
iperf_proc.BeginOutputReadLine();
iperf_proc.BeginErrorReadLine();
while (false == iperf_proc.HasExited)
{
if (true == processCancelled)
{
iperf_proc.StandardInput.Close(); // Doesn't Work!
iperf_proc.StandardInput.WriteLine("\x3"); // Doesn't Work!
iperf_proc.StandardInput.Flush(); // Doesn't Work!
}
}
iperf_proc.WaitForExit();
Any help is greatly appreciated. Thank you!
UPDATE:
Based on the suggestion from Hans in the comment, I tried adding some stuff to the code to get the ctrl+c event sent over.
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId);
private enum CtrlEvents
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1
}
private void closeBtn_Click(object sender, EventArgs e)
{
processCancelled = true;
//iperf_proc.CloseMainWindow();
bool succeeded = GenerateConsoleCtrlEvent((uint)CtrlEvents.CTRL_C_EVENT, (uint)iperf_proc.Id);
}
This did not work at all. The process is still running and it does the function added returns false. I did check that the process id being passed matches the id of the process in task manager. That all is fine, but the GenerateConsoleCtrlEvent function returns false. Any idea why this may be?

BeginOutputReadLine
Link describes how to do exactly what you are looking for in the example.
Why not use the built in Exited event to wait on so you are not blocking? Especially if you are spawning multiple threads. If you want to block then WaitForExit() is available as well.
if (true == processCancelled)
{
iperf_proc.StandardInput.Close(); // Doesn't Work!
iperf_proc.StandardInput.WriteLine("\x3"); // Doesn't Work!
iperf_proc.StandardInput.Flush(); // Doesn't Work!
}
If you close the standard input, how are you going to write/flush it?
After you have WaitForExit() you also need to Close()

Related

Cannot read STDOut from Process that is running in background

So, under Linux I have to connect to a bluetooth device via the command rfcomm connect hci0 xx:xx:xx:xx:xx:xx, which starts a bluetooth connection, but has to remain running in order to stay connected.
I have to write everything as an .NET Core Program
Running the command outputs the following lines after a few seconds:
Connected /dev/rfcomm0 to xx:xx:xx:xx:xx:xx on channel 1
Press CTRL-C for hangup
From that Output I have to get the /dev/rfcomm0 part, so I can read it with a SerialPortReader, and if something goes wrong, like, let's say there is no more data incoming, I have to kill the process and start anew, until I have a good connection.
Now my logic is something like this:
while(!Terminate)
{
string port = Connect();
ReadData(port);
BTProcess.Kill();
}
Don't bother with the ReadData(port); function, as my program never even comes near that.
The Connect() looks something like this:
while (!Connected)
{
Console.WriteLine("Configuring Process");
BTProcess = new Process();
BTProcess.StartInfo.FileName = "rfcomm";
BTProcess.StartInfo.Arguments = "connect hci0 xx:xx:xx:xx:xx:xx"
BTProcess.StartInfo.RedirectStandardOutput = true;
BTProcess.StartInfo.UseShellExecute = false;
Console.WriteLine("Starting Process");
BTProcess.Start();
StreamReader reader = _BTProcess.StandardOutput;
bool done = false;
Console.WriteLine("Reading STDOUT now.");
while (!done) // EDIT: If I do the while with !reader.EndOfStream then it won't even enter into the loop
{
Console.Write("-");
int c = reader.Read(); // Program stops in this line
if(c != -1)
{
port += (char)c;
}
Console.Write(c);
if (c == 0)
{
port = "";
done = true;
_BTProcess.Kill();
}
if (/* String Contains Logic blabla */)
{
port = /* The /dev/rfcomm0 stuff */
Connected = true;
done = true;
}
}
reader.Close();
}
return port;
I did check already if the Output isn't redirected to like STDErr or something, but no, it is 100% written in STDOut.
I have already tried a logic with like an EventHandler that handles StandardOutput Events, and a logic where I read it asynchronously, but both with no success. All had the same problem, they all block at the Read(); function. My guess is that maybe the internal buffer doesn't get flushed correctly.
Maybe someone here knows an answer to my problem.
P.S.: I know my code isn't the best or most optimized, but it should nevertheless work, as I have tried it already with another blocking command under Windows and it worked.
Thanks in advance for every help I get.
I've solved the problem by simply not running the command directly, because that throws buffer problems, so I now don't run the command rfcomm connect hci0 xx:xx:xx:xx:xx:xx, instead I run stdbuf -o0 rfcomm connect hci0 xx:xx:xx:xx:xx:xx, to avoid buffering problems.

c# wait until dialog of started process closes

How do I wait (block) my program until a specific dialog of my previous started process closes?
I'm starting pageant.exe to load a ssh key. Pageant is started with the class "Process". This works fine.
My ssh key has a passphrase. So my main program/process (this one started the process) has to wait until the user entered the ssh key passphrase.
I got an idea how to wait, but don't know how to do this in c#:
If pageant ask for the passphrase a dialog appears. So my main program/process can wait until the passphrase dialog is closed. Is it possible to do this in c#?
I got the idea from here.
EDIT: found a solution
// wait till passphrase dialog closes
if(WaitForProcessWindow(cPageantWindowName))
{ // if dialog / process existed check if passphrase was correct
do
{ // if passphrase is wrong, the passphrase dialog is reopened
Thread.Sleep(1000); // wait till correct passphrase is entered
} while (WaitForProcessWindow(cPageantWindowName));
}
}
private static bool WaitForProcessWindow(string pProcessWindowName)
{
Process ProcessWindow = null;
Process[] ProcessList;
bool ProcessExists = false; // false is returned if process is never found
do
{
ProcessList = Process.GetProcesses();
ProcessWindow = null;
foreach (Process Process in ProcessList)
{ // check all running processes with a main window title
if (!String.IsNullOrEmpty(Process.MainWindowTitle))
{
if (Process.MainWindowTitle.Contains(pProcessWindowName))
{
ProcessWindow = Process;
ProcessExists = true;
}
}
}
Thread.Sleep(100); // save cpu
} while (ProcessWindow != null); // loop as long as this window is found
return ProcessExists;
}
This might help you out, but doesn't give you entire control. I am not familiar with pageant, so I am not sure if it keeps running in the background or not. But in case the program automatically closes you could do this in your application.
So you could check in a loop if the Pageant application is open or not, once it is open you execute some code and once it is closed you enable the program again.
Execute this code in some background worker.
//Lets look from here if pageant is open or not.
while(true)
{
if (Process.GetProcessesByName("pageant").Length >= 1)
{
//block your controls or whatsoever.
break;
}
}
//pageant is open
while(true)
{
if (!Process.GetProcessesByName("pageant").Length >= 1)
{
//enable controls again
break;
}
}
//close thread

Sending CTRL+C to a process without killing my window

I have a console application, that spawns a "cmd mcast /recv... > somefile.txt"
(process.standardoutputstream does not work properly with mcast, if you want to contribute to this issue see redirecting standard output, event is not raised )
I need to send a ctrl+c to mcast and use this code:
[DllImport("kernel32.dll")]
static extern bool GenerateConsoleCtrlEvent(
uint dwCtrlEvent,
uint dwProcessGroupId);
void start(string path)
{
currentProcess = new Process();
currentProcess.StartInfo.FileName = #"C:\WINDOWS\system32\cmd.exe";
currentProcess.StartInfo.Arguments = "/C " + "mcast /recv /grps:239.255.0.1 /dump:3"
+ " > " + path;
currentProcess.StartInfo.UseShellExecute = false;
currentProcess.Start();
}
void Stop()
{
Process mcast = Process.GetProcessesByName("mcast")[0];
GenerateConsoleCtrlEvent(0 /*CTRL_C_EVENT*/, (uint)mcast.SessionId);
}
however mcast is sharing the console with my programm and mcasts sessionid is zero. so my program receives a CTRL+C too and terminates (which is really not what I want to do), any ideas?
EDIT: All my research suggests, that it is not possible to send a CTRL+C event to another process. Only WIN_CLOSE events can be send, which are ignored by console processes
You can try calling currentProcess.CloseMainWindow(), this should close cmd.exe, and cmd would, hopefully, close executing application properly.

how to make sure my process that i am starting will close of my application crash

in my application i m open Wireshark process and start capturing packts, in the UI i have Start button who start the capturing and Stop button who stop capturing and i am doing this by killing my wireshark process.
my question is if i am close my application in the middle of the capturing but nor with my Stop button my Wireshark process continue to run, how can i handle this situation and make sure that my process will close if my application crash or someone close it in the middle of capturing
public void startCapturing()
{
ThreadStart tStarter = delegate { openAdapterForStatistics(_device); };
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
ProcessStartInfo tsharkStartInfo = new ProcessStartInfo();
tsharkStartInfo.FileName = _tshark;
tsharkStartInfo.RedirectStandardOutput = true;
tsharkStartInfo.RedirectStandardError = true;
tsharkStartInfo.RedirectStandardInput = true;
tsharkStartInfo.UseShellExecute = false;
tsharkStartInfo.CreateNoWindow = true;
tsharkStartInfo.Arguments = string.Format(" -i " + _interfaceNumber + " -s " + _packetLimitSize + " -w " + _pcapPath);
_tsharkProcess.StartInfo = tsharkStartInfo;
_tsharkProcess.ErrorDataReceived += _cmdProcess_ErrorDataReceived;
_tsharkProcess.OutputDataReceived += tshark_OutputDataReceived;
_tsharkProcess.EnableRaisingEvents = true;
_tsharkProcess.Start();
_tsharkProcess.BeginOutputReadLine();
_tsharkProcess.BeginErrorReadLine();
FileInfo fileInfo = new FileInfo(_pcapPath);
string directoryName = fileInfo.DirectoryName;
DirectoryInfo directoryInfo = new DirectoryInfo(directoryName);
FileInfo[] dirs = directoryInfo.GetFiles();
_file = dirs.FirstOrDefault(f => f.Name.Equals(fileInfo.Name));
_tsharkProcess.WaitForExit();
}
Assuming you're handling exceptions using try-catch, you can make sure the Wireshark process is closed in the finally block at the end of your topmost try-catch statement. From the MSDN documentation:
"The finally block is useful for cleaning up any resources allocated in the try block. Control is always passed to the finally block regardless of how the try block exits."
For example:
try
{
// Your application's code.
}
catch( Exception )
{
// Handle, log, whatever.
}
finally
{
// Make sure Wireshark process is killed.
}
The code in the finally block will always get executed whether or not there was an exception.
You can't be 100% sure. If your application crashes in a 'civilized' manner, you can close the Wireshark process (much like #jebar8 suggested). Unfortunately, your application could crash in a way that doesn't run your finally code (if it's out of memory or your main thread is out of stack space, for example).
If you want to take that into account as well, you need a third process. Write up a small program that will launch your application and monitor it. If your application process disappears, your monitoring process can then kill Wirehsark is it's alive. Since the monitoring program is a short and simple one, the chance of it crashing unexpectedly are very slim.

Process started by Process.start() returns incorrect process ID?

I am starting an executable using this code:
Process proc = new Process();
proc.StartInfo.FileName = executablePath;
proc.Start();
proc.WaitForInputIdle();
after this calling proc.Id it gives me some integer, which is not real process ID. In the task manager there is another ID for this process and also I am using MS UI Automation to access this application, which also returns the same ID as in task manager. So my question is how can I get the real process ID of started process?
UPDATE
I found out that on Windows 7 it works fine and returns me the right ID, but not on Windows XP. What can be the reason?
SCENARIO
The scenario of the application is the following. I have a running embedded HTTP server, which is implemented not by me, (here is the source). The client connects to the web server and sends a request to run a program. In the request handler of my server I am just using Process.start() to start the requested application. As a web server the program creates threads for every client session connected to it (I assume so, as I didn't wrote it). Can this somehow help to identify the problem as it exists only on Windows XP X86 Service Pack 3?
An example of how I did it:
bool started = false;
var p = new Process();
p.StartInfo.FileName = "notepad.exe";
started = p.Start();
try {
var procId = p.Id;
Console.WriteLine("ID: " + procId);
}
catch(InvalidOperationException)
{
started = false;
}
catch(Exception ex)
{
started = false;
}
Otherwise, try using handles like this:
Using handlers
Getting handler
hWnd = (int) process.MainWindowHandle;
int processId;
GetWindowThreadProcessId(hWnd, out processId);
[DllImport("user32")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
Side note:
What happens if you get the array of process and iterate over them and compare the PIDs?
Process[] p = Process.GetProcessesByName( "testprogram" );
foreach(var proc in p)
Console.WriteLine("Found: "+proc.Id == myExpectedProcId);
This:
using (Process process = Process.Start("notepad.exe"))
{
process.WaitForInputIdle();
Console.WriteLine(process.Id);
}
Actually works for me:
http://pasteboard.s3.amazonaws.com/images/1350293463417532.png
Task Manager:
http://pasteboard.s3.amazonaws.com/images/1350293536498959.png
My thoughts:
Actually your process starts another process and you are trying to get ID of some kind of launcher. (It can start itself by the way).
Below also returns the PID of a process
Process[] p = Process.GetProcessesByName("YourProcessName");
Now you can get process Id by using p[i].Id;
I'm just trying to guess here, since it's difficult to understand what's really happening without seeing the real code. Anyway, you mentioned Trhreads in one of your comment. Is it possible that you have a single variable proc of type Process which is initialized in your main thread, and then the process is started in a different Thread?
If this is the case, maybe the process is started more than once, and you get the PID of just one of them. The only way I was able to reproduce your case is this one:
private Process proc;
private List<int> pids = new List<int>();
public void StartProc()
{
// this tries to simulate what you're doing. Starts the process, then
// wait to be sure that the second process starts, then kill proc
proc.Start();
pids.Add(proc.Id);
Thread.Sleep(300);
try
{
proc.Kill();
}
catch {}
}
// the method returns the PID of the process
public int Test()
{
proc = new Process();
proc.StartInfo.FileName = #"notepad.exe";
for (int i = 0; i < 2; i++)
{
Thread t = new Thread(StartProc);
t.Start();
Thread.Sleep(200);
}
Thread.Sleep(500);
return proc.Id;
}
When you executes Test, you should see a single active Notepad, and the PID returned by the method is different by the one showed by the Task Manager. But if you take a look at the pids List, you should see that the Task Manager PID is the first element in the list, and the one returned by the method is the second one.
Is it possible that you have done something similar?

Categories

Resources