35

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 log file just before it terminates and thus I need to be absolutely sure that this log file 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,
                               // Must be false to redirect IO
                               UseShellExecute = false,
                               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 polling 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 ) )
{
    // But this exception is thrown occasionally, why?
    throw new Exception("xml file not found");
}
sharptooth
  • 167,383
  • 100
  • 513
  • 979
johnrl
  • 873
  • 3
  • 14
  • 18
  • How is testprogram.exe exiting? Are you calling a Kill() on it, or otherwise ending it prematurely? Is it exiting normally after finishing all its writes? – Tanzelax Mar 25 '10 at 22:36
  • I don't know. It's an external program I am running and I don't have the source code for it. All I know is that it's wring a file (exporting its results) just before it closes because thats the behavior I have observed from it. The return value is 0 so I would guess it's exiting normally. Nothing has shown otherwise. – johnrl Mar 26 '10 at 13:11
  • @johnrl have you ever solve that problem can you share your code – GowthamanSS Sep 27 '12 at 05:49
  • If what you're ultimately after is the outputted xml file, have you considered a FileSystemWatcher? http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx – Fred Jan 17 '13 at 04:13
  • @johnrl I'm wondering if everything goes as you think they go... I never had trouble with managing processes like this. Perhaps it starts a child process and exits the main, or the pid changes for some reason, or dumping is done in another process as it works in windows error reporting or... there's just too many variables. Can you use procmon (sysinternals) to get more information about what process does what and check if pid's in process explorer (procexp from sysinternals) please? – atlaste Jan 18 '13 at 07:20

11 Answers11

16

I realize this is an old post, but in my quest to find out why my app running the Exited event before the app had even opened I found out something that I though might be useful to people experiencing this problem in the future.

When a process is started, it is assigned a PID. If the User is then prompted with the User Account Control dialog and selects 'Yes', the process is re-started and assigned a new PID.

I sat with this for a few hours, hopefully this can save someone time.

user2864740
  • 60,010
  • 15
  • 145
  • 220
Dave
  • 351
  • 3
  • 12
10

I would suggest you to try this way:

process.Start();

while (!process.HasExited)
{
    // Discard cached information about the process.
    process.Refresh();

    // Just a little check!
    Console.WriteLine("Physical Memory Usage: " + process.WorkingSet64.ToString());

    Thread.Sleep(500);
}

foreach (Process current in Process.GetProcessesByName("testprogram"))
{
    if ((current.Id == process.Id) && !current.HasExited)
        throw new Exception("Oh oh!");
}

Anyway... in MSDN page of HasExited I'm reading the following hightlighted note:

When standard output has been redirected to asynchronous event handlers, it is possible that output processing will not have completed when this property returns true. To ensure that asynchronous event handling has been completed, call the WaitForExit() overload that takes no parameter before checking HasExited.

That could be somehow linked to your problem as you are redirecting everything.

Tommaso Belluzzo
  • 23,232
  • 8
  • 74
  • 98
9

I know, this is an old post but maybe I can help someone.
The Process class may behave unexpectedly. HasExited will return true if the process has exited or if the process runs with administrator privileges and your program only has user privileges.

I have posted a question regarding this a while back here, but did not receive a satisfiable answer.

plainerman
  • 435
  • 8
  • 18
5

First off, are you sure testprogram does not spawn a process of its own and exit without waiting for that process to finish? We're dealing with some kind of race condition here, and testprogram can be significant.

Second point I'd like to make is about this - "I need to be absolutely sure that this logfile exists". Well, there is no such thing. You can make your check, and then the file is gone. The common way to address this is not to check, but rather to do what you want to do with the file. Go ahead, read it, catch exceptions, retry if the thing seems unstable and you don't want to change anything. The functional check-and-do does not work well if you have more than one actor (thread or whatever) in the system.

A bunch of random ideas follows.

Have you tried using FileSystemWatcher and not depending on process completion?

Does it get any better if you try reading the file (not checking if it exists, but acting instead) in the process.Exited event? [it shouldn't]

Is the system healthy? Anything suspicious in the Event Log?

Can some really aggressive antivirus policy be involved?

(Can't tell much without seeing all the code and looking into testprogram.)

Mikhail
  • 170
  • 1
  • 9
4

So just for a further investigation into the root cause of the problem you should maybe check out what's really happening by using Process Monitor. Simply start it and include the external program and your own tool and let it record what happens.

Within the log you should see how the external tool writes to the output file and how you open that file. But within this log you should see in which order all these accesses happen.

The first thing that came to my mind is that the Process class doesn't lie and the process is really gone when it tells so. So problem is that at this point in time it seems that the file is still not fully available. I think this is a problem of the OS, cause it holds some parts of the file still within a cache that is not fully written onto the disk and the tool has simply exited itself without flushing its file handles.

With this in mind you should see within the log that the external tool created the file, exited and AFTER that the file will be flushed/closed (by the OS [maybe remove any filters when you found this point within the log]).

So if my assumptions are correct the root cause would be the bad behavior of your external tool which you can't change thus leading to simply wait a little bit after the process has exited and hope that the timeout is long enough to get the file flushed/closed by the OS (maybe try to open the file in a loop with a timeout till it succeeded).

Oliver
  • 43,366
  • 8
  • 94
  • 151
3

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
  • 33,512
  • 4
  • 61
  • 92
  • 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 Mar 25 '10 at 22:10
  • 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 Mar 26 '10 at 09:01
  • 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 Mar 26 '10 at 13:05
1

If you have web application, and your external program/process is generating files (write to disk) check if your IIS have rights to write to that folder if not on properties security add permission for your IIS user, that was the reason in my case, i was receiving process.HasExited =true, but produced files from the process was not completed, after struggling for a while i add full permissions to the folder where process was writhing and process.Refresh() as Zarathos described from above and everything was working as expected.

No Name
  • 719
  • 6
  • 14
1

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
  • 13,028
  • 1
  • 32
  • 34
  • 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 Mar 25 '10 at 22:03
  • 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 Mar 26 '10 at 03:35
1

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
  • 5,406
  • 2
  • 25
  • 28
1

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
  • 648
  • 9
  • 17
1

Use process_name.Refresh() before checking whether process has exited or not. Refresh() will clear all the cached information related to the process.

Olle Sjögren
  • 5,315
  • 3
  • 31
  • 51
  • I see this was downvoted - any insights as to why? I'm having a similar issue to OP and am just trying to sort through all these answers... – Kevin Holt Nov 22 '16 at 18:28