views:

630

answers:

6

I have a self upgrade process implemented where my main application exe launches an updater exe passing a handle to itself on the command line. The application exe then calls ExitProcess to exit, and the updater calls WaitForSingleObject on the passed in handle to wait for the application exe to terminate.

The WaitForSingleObject does wait. Until the application calls ExitProcess the updater does stall.

However, sometimes, when the updater tries to overwrite the application dlls with the new versions I get a file-locked error which the current version of my updater treats as an unrecoverable error and terminates. It seems that including an arbitrary sleep(100) is sufficient to bypass this "issue" but I really hate code like that. really hate.

It seems very odd to me that the process handle can be signalled while the main application is still alive enough to have dll files locked.

+1  A: 

The process signals when the application code exits. It may take the OS a little more time to unload the process completely. The point of signalling is to say "I'm done doing what I need to do", its more effecient to release other code that may have really useful stuff to do instead of making that code wait while the OS does some housekeeping.

AnthonyWJones
It seems that - practically - the process handle becomes signaled as soon as there is a valid exit code to return from GetExitCode(). But, in terms of common use cases, the apps most likley to be waiting on process handles are installers/updaters that need to know when they are able to delete stuff.
Chris Becke
A: 

It could be that the DLL's are locked by some other process at the time. One way to test this would be to generate a report of whatever is holding onto the DLL when this happens.

Daemin
A: 

Saw a rash of these with some Antivirus software about six months ago. Try without AV, and at the very least, ensure the AV is up to date.

Tristank
A: 

If any of the DLL's you are using are using threads, they may not terminate (or join) fast enough if you do not explicitly unload them- which would of course occur if you had explicitly loaded them using LoadLibrary

Take a look here: http://msdn.microsoft.com/en-us/library/ms682596(VS.85).aspx

Specifically this line:

... The DLL is unloaded when the process terminates or calls the FreeLibrary function and the reference count becomes zero. If the process terminates as a result of the TerminateProcess or TerminateThread function, the system does not call the DLL entry-point function.

Klathzazt
A: 

a possible fix for you ... while you cannot replace dll that is use, you can rename it. so if you have a dll that you need to replace, but its in use for some reason, rename it to .delete or something like that. do the update, then have your main program search for any .delete files and remove them when it starts up.

-don

Don Dickinson
+1  A: 

As another answer points out, the process handle gets signalled when the process has stopped execution, and the operating system might take a bit longer to release DLLs.

You are right that relying on Sleep(100) is a bad idea. You should rather wrap overwriting your DLLs in a loop like this:

BOOL UpdateDll(LPCTSTR dll_name, WHATEVER whatever) {
  int tries = 150;
  while (tries--) {
    if (TryUpdateDll(dll_name, whatever))
      return TRUE;
    Sleep(200);
  }
  return FALSE;
}

This keeps trying to unload your DLL for 30 seconds and then gives up. 30 seconds should be enough even when the system is under heavy load, but still will protect your updater from hanging forever. (In case UpdateDll returns FALSE, be sure to present a meaningful error message to your user, stating the name of offending DLL.)

If you are messing with COM, a call to CoFreeUnusedLibraries before quitting might also be helpful. (http://msdn.microsoft.com/en-us/library/ms679712.aspx) Frankly I don't know if COM might hold on to DLLs even after your process exits, but better be safe.

Bottom line is that there's a lot of strangeness in Win32 API. You don't have to deal with every case as long as you can find an acceptable solution. Obviously Sleep(100) might break, but a 30-second polling loops seems acceptable to me.

Andrey Tarantsov