Gotchas at Wait for a shelled app to finish (with/out timeout) with .NET
I came across a useful Microsoft Support sample called “How to wait for a shelled application to finish by using Visual Basic 2005 or Visual Basic .NET” at http://support.microsoft.com/kb/305368
However, note that there are several gotchas with the code supplied there (just informed Microsoft on that, hope they take notice). Also the article points to C# and to C++ versions of the sample that obviously need the same fixes
1) there’s an issue in both the first sample (wait indefinitely) and the 2nd one (wait with timeout)
‘Wait for the process window to complete loading.
‘Wait for the process to exit.
Why wait for input idle first? The process might never enter idle state and exit before that. According to http://msdn.microsoft.com/en-us/library/8d7363e2(v=VS.90).aspx you might get exception from WaitForInputIdle:
|InvalidOperationException||The process does not have a graphical interface.
An unknown error occurred. The process failed to enter an idle state.
The process has already exited.
No process is associated with this Process object.
I suppose it’s best to avoid calling WaitForInputIdle at all since you just care for WaitForExit there.
2) even WaitForExit can throw exceptions that the code should check for according to http://msdn.microsoft.com/en-us/library/fb4aw7b8.aspx
|Win32Exception||The wait setting could not be accessed.|
|SystemException||No process Id has been set, and a Handle from which the Id property can be determined does not exist.
There is no process associated with this Process object.
You are attempting to call WaitForExit for a process that is running on a remote computer. This method is available only for processes that are running on the local computer.
3) The support article doesn’t mention what WaitForExit(timeout) doc (http://msdn.microsoft.com/en-us/library/fb4aw7b8.aspx) says about “infinite” timeout:
In the .NET Framework version 3.5 and earlier versions, the WaitForExit overload waited for MaxValue milliseconds (approximately 24 days), not indefinitely. Also, previous versions did not wait for the event handlers to exit if the full MaxValue time was reached.
Also it seems the documentation for “WaitForExit(timeout)” doesn’t mention there’s a Timeout.Infinite constant that has the value –1 to use for such infinite timeouts (found it from the doc of Thread.Join): http://msdn.microsoft.com/en-us/library/system.threading.timeout.infinite(v=VS.90).aspx
4) The sample fails to call Close and thus keeps on spending resources for handle tracking (and “locking” those handle ids obviously although it’s not as easy as in old CPUs and OS versions to run out of handles I hope).
When an associated process exits (that is, when it is shut down by the operation system through a normal or abnormal termination), the system stores administrative information about the process and returns to the component that had called WaitForExit. The Process component can then access the information, which includes the ExitTime, by using the Handle to the exited process.
Because the associated process has exited, the Handle property of the component no longer points to an existing process resource. Instead, the handle can be used only to access the operating system’s information about the process resource. The system is aware of handles to exited processes that have not been released by Process components, so it keeps the ExitTime and Handle information in memory until the Process component specifically frees the resources. For this reason, any time you call Start for a Process instance, call Close when the associated process has terminated and you no longer need any administrative information about it. Close frees the memory allocated to the exited process.
Also note that it could have a “Using” clause when defining the new process object (instead of Dim) instead of explicitly having to call “Process.Close” at the end to free up resources, as noted at http://msdn.microsoft.com/en-us/library/system.diagnostics.process.close(v=VS.90).aspx
The Close method causes the process to stop waiting for exit if it was waiting, closes the process handle, and clears process-specific properties. Close does not close the standard output, input, and error readers and writers in case they are being referenced externally.
5) Other issue is at the 2nd sample (wait with timeout). It doesn’t mention having to call WaitForExit() again with no params after the WaitForExit(timeout) at the case where standard output has been redirected to async event handlers (should mention this case for completeness)
When standard output has been redirected to asynchronous event handlers, it is possible that output processing will not have completed when this method returns. To ensure that asynchronous event handling has been completed, call the WaitForExit overload that takes no parameter after receiving a true from this overload. To help ensure that the Exited event is handled correctly in Windows Forms applications, set the SynchronizingObject property.