views:

2013

answers:

4

I'm writing an application to start and monitor other applications in C#. I'm using the System.Diagnostics.Process class to start applications and then monitor the applications using the Process.Responding property to poll the state of the application every 100 milisecs. I use Process.CloseMainWindow to stop the application or Process.Kill to kill it if it's not responding.

I've noticed a weird behaviour where sometimes the process object gets into a state where the responding property always returns true even when the underlying process hangs in a loop and where it doesn't respond to CloseMainWindow.

One way to reproduce it is to poll the Responding property right after starting the process instance. So for example

_process.Start();
bool responding = _process.Responding;

will reproduce the error state while

_process.Start();
Thread.Sleep(1000);
bool responding = _process.Responding;

will work. Reducing the sleep period to 500 will introduce the error state again.

Something in calling _process.Responding too fast after starting seems to prevent the object from getting the right windows message queue handler. I guess I need to wait for _process.Start to finish doing it's asynchronous work. Is there a better way to wait for this than calling Thread.Sleep ? I'm not too confident that the 1000 ms will always be enough.

+4  A: 

I think it may be better to enhance the check for _process.Responding so that you only try to stop/kill the process if the Responding property returns false for more than 5 seconds (for example).

I think you may find that quite often, applications may be "not responding" for a split second whilst they are doing more intensive processing.

I believe a more lenient approach will work better, allowing a process to be "not responding" for a short amount of time, only taking action if it is repeatedly "not responding" for several seconds (or however long you want).

Further note: The Microsoft documentation indicates that the Responding property specifically relates to the user interface, which is why a newly started process may not have it's UI responding immediately.

Jarod Elliott
I only take action if the process has been unresponsive for more than a minute. But I want to check often though to know if the program has been unresponsive for all that time. If I check once a minute and responsive is false I still don't know if this is only for a short time or not.
Mendelt
If that's the case, a simple counter against each process should suffice. Just increment the counter every time the process isn't responding and reset the counter if it is responding.If you did your check every second, you just need to take action once the counter hits 60.
Jarod Elliott
That's exactly what I'm doing. The problem is that if I poll Responding too soon after Process.Start it stops working. Responding always returns true after that and Process.CloseMainWindow stops working too.
Mendelt
+6  A: 

Now, I need to check this out later, but I am sure there is a method that tells the thread to wait until it is ready for input. Are you monitoring GUI processes only?

Isn't Process.WaitForInputIdle of any help to you? Or am I missing the point? :)

Update

Following a chit-chat on Twitter (or tweet-tweet?) with Mendelt I thought I should update my answer so the community is fully aware..

  • WaitForInputIdle will only work on applications that have a GUI.
  • You specify the time to wait, and the method returns a bool if the process reaches an idle state within that time frame, you can obviously use this to loop if required, or handle as appropriate.

Hope that helps :)

Rob Cooper
+2  A: 

Thanks for the answers. This

_process.Start();
_process.WaitForInputIdle();

Seems to solve the problem. It's still strange because Responding and WaitForInputIdle should both be using the same win32 api call under the covers.

Some more background info
GUI applications have a main window with a message queue. Responding and WaitForInputIdle work by checking if the process still processes messages from this message queue. This is why they only work with GUI apps. Somehow it seems that calling Responding too fast interferes with getting the Process getting a handle to that message queue. Calling WaitForInputIdle seems to solve that problem.

I'll have to dive into reflector to see if I can make sense of this.

update
It seems that retrieving the window handle associated with the process just after starting is enough to trigger the weird behaviour. Like this:

_process.Start();
IntPtr mainWindow = _process.MainWindowHandle;

I checked with Reflector and this is what Responding does under the covers. It seems that if you get the MainWindowHandle too soon you get the wrong one and it uses this wrong handle it for the rest of the lifetime of the process or until you call Refresh();

update
Calling WaitForInputIdle() only solves the problem some of the time. Calling Refresh() everytime you read the Responding property seems to work better.

Mendelt
A: 

I too noticed that in a project about 2 years ago. I called .Refresh() before requesting certain prop values. IT was a trial-and-error approach to find when I needed to call .Refresh().

GregUzelac
I researched this further using reflector. The Hwnd and some other properties are stored after the first time you get them. With the Hwnd this can cause problems. I described this here http://blog.mendeltsiebenga.com/post/Process-weirdness-and-how-to-solve-it.aspx .
Mendelt