Desaware Home
Products    Purchase    Publishing    Articles   Support    Company    Contact    



Contact Desaware and order today

Sign up for Desaware's Newsletter for the latest news and tech tips.

How to Find Out When a Shelled Application is Closed Under Win32

One of the most common Win32 questions that we've dealt with recently concerns the question of determining when a shelled application has closed under Win32. Under 16 bit operating systems, you may recall that the Shell function returns an instance handle (or module handle) that uniquely identifies the newly launched application. You could then use the GetModuleUsage API function to watch the module count for the application in a DoEvents loop -- when the module count goes back to zero, it indicates that the application has closed.

If you have tried this under Win32 you may have noticed a few minor problems starting with the fact that the GetModuleUsage function does not even exist under Win32!

The reason it doesn't exist has to do with the underlying architecture of Win32 -- an instance handle is fundamentally a reference to a block of memory, and under Win32 memory is no longer shared among applications. This means that an instance handle cannot uniquely identify a running task. Even if GetModuleUsage still existed, it would have no meaning because it would only be able to obtain the module count for its own instance. It couldn't possibly work on a module handle from a different application.

Ok, this is all very interesting, but knowing why GetModuleUsage won't work doesn't help solve the problem. How do you detect that an application has terminated under Win32?

The secret is to look at what methods Win32 does provide to uniquely identify a task in the system (by the way, tasks will henceforth be referred to by their Win32 name: processes).

Every Win32 process has a unique identifier called a process ID. This process ID is, in fact, the value returned by a successful Visual Basic "Shell" function call. But the process ID by itself is not very useful. In order to work with the process you need to obtain a handle to a process. A process handle is a 32 bit number that refers to the specific process. A process handle is obtained using the OpenProcess function and is valid only within the application that called the OpenProcess function. The code used to obtain a process handle is shown below. Just place a command button on a form and set its Name property to cmdWatch. You'll also need a program to launch (in this case we use project1.exe):

Private Sub cmdWatch_Click()
   Dim ProcessId&
   Dim hProcess&
   Dim ExitCode&
   ProcessId = Shell("project1.exe", vbNormalFocus)
   hProcess = OpenProcess(PROCESS_QUERY_ INFORMATION, _
   False, ProcessId)
   cmdWatch.Enabled = False
      Call GetExitCodeProcess(hProcess, ExitCode)
   Loop While (ExitCode = STILL_ACTIVE)
   cmdWatch.Enabled = True
   Call CloseHandle(hProcess)
End Sub

The PROCESS_QUERY_INFORMATION flag in the OpenProcess command tells Windows that you wish permission to query the process for its current status -- there are many other possible permissions that you may request.

We disable the command button to prevent reentrancy problems, then enter a DoEvents loop. This loop repeatedly queries the process to obtain an exit code. As long as it's STILL_ACTIVE, the process is still running. Finally, we re-enable the command button and close the process handle. Don't forget to close the process handle -- until it is closed, the other application will not completely unload (even though it will be closed from the user's point of view, not all of its internal memory structures will be freed).

This code closely resembles the traditional DoEvents loop using GetModuleUsage, but it's still not an optimal approach. It burns up a great many CPU cycles with repeated calls to GetExitCodeProcess and DoEvents -- cycles that are wasted.

Have you ever wondered if there might be a more efficient way? 

One excellent way is described in the article "Waiting with Threads" on this site.

Another technique, which combines Win32 synchronization functions with OLE automation techniques, can be found in the Launch.vbp example on pages 1119-1135 of the Visual Basic Programmer's Guide to the Win32 API (along with a much more thorough discussion of instance handles, process identifiers, process handles and Win32 objects in general).


For notification when new articles are available, sign up for Desaware's Newsletter.

Related Products:
Products    Purchase    Articles    Support    Company    Contact
Copyright© 2012 Desaware, Inc. All Rights Reserved.    Privacy Policy