views:

846

answers:

4

I have been observing that Process.HasExited sometimes returns true even though the process is still running.

My code below starts a process with name "testprogram.exe" and then waits for it to exit. The problem is that sometimes I get thrown the exception; it seems that even though HasExited returns TRUE the process itself is still alive in the system - how can this be??

My program writes to a logfile just before it terminates and thus I need to be absolutely sure that this logfile exists (aka the process has terminated/finished) before reading it. Continuously checking for it's existence is not an option.

        // Create new process object
        process = new Process();

        // Setup event handlers
        process.EnableRaisingEvents = true;
        process.OutputDataReceived += OutputDataReceivedEvent;
        process.ErrorDataReceived += ErrorDataReceivedEvent;
        process.Exited += ProgramExitedEvent;

        // Setup start info
        ProcessStartInfo psi = new ProcessStartInfo
                                   {
                                       FileName = ExePath,
                                       UseShellExecute = false, // Must be false to redirect IO
                                       RedirectStandardOutput = true,
                                       RedirectStandardError = true,
                                       Arguments = arguments
                                   };

        process.StartInfo = psi;

        // Start the program
        process.Start();


        while (!process.HasExited)
            Thread.Sleep( 500 );

        Process[] p = Process.GetProcessesByName( "testprogram" );

        if ( p.Length != 0 )
            throw new Exception("Oh oh");

UPDATE: I just tried waiting with process.WaitForExit() instead of the poll loop and the result is the exact same.

Addition: The above code was only to demonstrate a 'clearer' problem alike. To make it clear; my problem is NOT that I still can get a hold of the process by 'Process.GetProcessesByName( "testprogram" );' after it set HasExited to true.

The real problem is that the program I am running externally writes a file -just before- it terminates (gracefully). I use HasExited to check when the process has finished and thus I know I can read the file (because the process exited!), but it seems that HasExited returns TRUE even sometimes when the program has NOT written the file to disk yet. Here's example code that illustrates the exact problem:

    // Start the program
    process.Start();


    while (!process.HasExited)
        Thread.Sleep( 500 );
    // Could also be process.WaitForExit(), makes no difference to the result

    // Now the process has quit, I can read the file it has exported
     if ( !File.Exists( xmlFile ) )
        {
            throw new Exception("xml file not found"); // But this exception is thrown occasionally, why?
        }
+1  A: 

For a start, is there an issue with using Process.WaitForExit rather than polling it?

Anyway, it is technically possible for the process to exit from a usable point of view but the process still be around briefly while it does stuff like flush disk cache. Is the log file especially large (or any operation it is performing heavy on disk writes)?

tyranid
The logfiles absolute max size is around 1mb. The reason I use polling is that I need to be able to abort the wait task at any time and with WaitForExit I am blocked at least until timeout.In your opinion it is indeed possible that the program might be flushing it's streams to disk even though HasExited returns true? I just find it strange that the process is not completely dead/gone in the process list when HasExited is true. It doesn't mention any special circumstances in the documentation as far as I can tell.
johnrl
It is something I have seen before, but not for sizes that small. As pointed out by another commenter though the Process class will be maintaining a reference to the process, you might want to call Dispose on the Process instance after checking for Exit but before you try and find it in the process list.
tyranid
+1  A: 

There's two possibilities, the process object continues to hold a reference to the process, so it has exited, but it hasn't yet been deleted. Or you have a second instance of the process running. You should also compare the process Id to make sure. Try this.

    ....

    // Start the program
    process.Start();


    while (!process.HasExited)
        Thread.Sleep( 500 );

    Process[] p = Process.GetProcessesByName( "testprogram" );
    if ( p.Length != 0 && p[0].Id == process.id && ! p[0].HasExited)
        throw new Exception("Oh oh");
John Knoeller
I only have a single instance running and I. I tried keeping the Windows Task Manager open and watching the process list (also checking the ID's, they match), and indeed when my code throws the exception the process is still in the list. Sometimes it quits almost instantly when the exception is thrown, other times it may take a second or two to disappear.I'm not sure if Task Manager can be trusted (delay maybe?) but the fact is my code throws the exception - not every time but around 1/10th of the runs it does.
johnrl
Once process.HasExited is true, then the process _has_ exited. It's dead. It may be a zombie for some period of time, especially if there are open handles to the process object. The fact that GetProcessByName() might sometimes return that process means nothing other than the process is still a zombie. You should `process.Dispose()` to make sure that that handle is released. But your "test" using GetProcessByName is simply not a reasonable thing to do. process.HasExited is the only trustworthy test you can make, and you already made it.
John Knoeller
Sounds reasonable. But I don't understand how come that the file 'testprogram' writes (which is does just before it closes), isn't written to disk when HasExited is true. How can this be if the process is a zombie (then it should have flushed everything to disk, right?). Disposing the process object has no effect on the program run by the process as far as I know so how would this help?
johnrl
+1  A: 

As per MSDN documentation for HasExited.

If a handle is open to the process, the operating system releases the process memory when the process has exited, but retains administrative information about the process, such as the handle, exit code, and exit time.

Probably not related, but it's worth noting.

If it's only a problem 1/10 of the time, and the process disappears after a second anyway, depending on your usage of HasExited, try just adding another delay after the HasExited check works, like

while (!process.HasExited)
    DoStuff();
Thread.Sleep(500);
Cleanup();

and see if the problem persists.

Personally, I've always just used the Exited event handler instead of any kind of polling, and a simplistic custom wrapper around System.Diagnostics.Process to handle things like thread safety, wrapping a call to CloseMainWindow() followed by WaitForExit(timeout) and finally Kill(), logging, et cetera, and never encountered a problem.

Tanzelax
+1  A: 

Maybe the problem is in the testprogram? Does this code nicely flush/close etc.? It seems to me if testprogram writes a file to disk, the file should at least be available (empty or not)

Roy