HOWTO: Detect All Program Terminations

ID: Q125689

The information in this article applies to:

SUMMARY

The processes for detecting program terminations fall into two categories:

MORE INFORMATION

Win32 processes can use one of the Wait functions to wait for a spawned process. By using CreateProcess() to launch a Win32 process, 16-bit process, or MS-DOS-based application, you can fill in a PROCESS_INFORMATION structure. The hProcess field of this structure can be used to wait until the spawned process terminates. For example, the following code spawns a process and waits for its termination:

  PROCESS_INFORMATION ProcessInfo;
  STARTUPINFO StartupInfo = {0};
  StartupInfo.cb = sizeof(STARTUPINFO);
  if (CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE,
        0, NULL, NULL, &StartupInfo, &ProcessInfo))
   {
       WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
       /* Process has terminated */ 
       ...
   }
   else
   {
       /* Process could not be started */ 
       ...
   }

If necessary, you can put this code into a separate thread to allow the initial thread to continue to execute.

NOTE: For a discussion of how the NTVDM WOW architecture may influence waiting on 16-bit applications, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q105676
   TITLE     : Starting and Terminating Windows-based Application

This synchronization method is not available to 16-bit processes. Instead, they must use the TOOLHELP NotifyRegister function to register a callback function to be called when a program terminates. This method will detect the termination of 16-bit processes and MS-DOS-based applications, but not Win32 processes.

The following code shows how to register a callback function with NotifyRegister():

   FARPROC lpfnCallback;

   lpfnCallback = MakeProcInstance(NotifyRegisterCallback, ghInst);
   if (!NotifyRegister(NULL, (LPFNNOTIFYCALLBACK)lpfnCallback,
                       NF_NORMAL))
   {
      MessageBox(NULL, "NotifyRegister Failed", "Error", MB_OK);
      FreeProcInstance(lpfnCallback);
   }

The next section of code demonstrates the implementation of the callback function:

   BOOL FAR PASCAL __export NotifyRegisterCallback (WORD wID,
                                                    DWORD dwData)
   {
      HTASK hTask;  // task that called the notification callback
      TASKENTRY te;

      // Check for task exiting
      switch (wID)
      {
         case NFY_EXITTASK:
            // Obtain info about the task that is terminating
            hTask = GetCurrentTask();
            te.dwSize = sizeof(TASKENTRY);
            TaskFindHandle(&te, hTask);

            // Check if the task that is terminating is our child task.
            // Also check if the hInstance of the task that is
            // terminating is the same as the hInstance of the task
            // that was WinExec'd by us earlier in the program.

            if (te.hTaskParent == ghtaskParent &&
                te.hInst == ghInstChild)
                PostMessage(ghwnd, WM_USER+509, (WORD)te.hInst, dwData);
            break;

         default:
            break;
      }
      // Pass notification to other callback functions
      return FALSE;
   }

The NotifyRegisterCallback() API is called by the 16-bit TOOLHELP DLL in the context of the process that is causing the event. Problems arising because of reentrancy and notification chaining makes the callback function subject to certain restrictions. For example, operations that cause TOOLHELP events cannot be done in the callback function. (See the TOOLHELP NotifyRegister function documentation in your Software Development Kit for events that cause TOOLHELP callbacks.)

There is no way a 16-bit process can be notified when a Win32 process exits. However, a 16-bit process can use TaskFirst() and TaskNext() to periodically walk the task list to determine if a Win32 process is still executing. This technique also works for 16-bit processes and MS-DOS-based applications. For example, the following code shows how to check for the existence of a process:

   BOOL StillExecuting(HINSTANCE hAppInstance)
   {
      TASKENTRY  te = {0};

      te.dwSize = sizeof(te);
      if (TaskFirst(&te))
          do
          {
             if (te.hInstance == hAppInstance)
                return TRUE;      // process found
          } while (TaskNext(&te));

      // process not found
      return FALSE;
   }

Refer to the TermWait sample for complete details on how to use NotifyRegister and implement a callback function. For additional information, please search in the Microsoft Knowledge Base using this word:

   TERMWAIT

Additional query words: end exit notification notify spawn terminate termination
Keywords          : kbcode kbprg kbKernBase kbThread kbGrpKernBase 
Version           : 95
Platform          : Win95
Issue type        : kbhowto

Last Reviewed: May 29, 1997