Members

Technology Zones

IBM Learning Center

Articles

Hosted By

MaximumASP

Info

Rated
Read 25,736 times

Contents

Related Categories

Process Management - When did it stop?

flounder

When did it stop?

Often you will want to launch a process, often a console application, and let it run until it completes. When it completes, you can then deal with its results. For example, I have a case where I spawn (of all things) a 16-bit compiler (it is written in assembly code, and no, I had nothing to do with it; I just had to use it in a client app). I spawn it with a commandline

  compilername inputfile, listingfile, outputfile

and I have to wait for it to complete before I can let the user examine the listing file or download the output file.

This is any easy one, because the compiler works with very tiny programs, and runs in under 5 seconds. So for this application, I just wait for it to complete. 

HANDLE process = launcher_of_your_choice(program, args);
if(process != NULL)
    { /* success */
     ::WaitForSingleObject(process, INFINITE);
     ::CloseHandle(process);
    } /* success */

However, not all programs have this property. In this case, you want to get an asynchronous notification of the completion. I do this by what appears to be a complex method, but in fact is very simple: I spawn a thread that blocks on the process handle. When the process completes, the thread resumes execution, posts a message to my main GUI window, and terminates.

I'm reproducing the code for the WaitInfo class here because it is so small. This is also part of a demo project you can download from this site.

WaitInfo.h

class WaitInfo {
    public:
       WaitInfo() {hProcess = NULL; notifyee = NULL; }
       virtual ~WaitInfo() { }
       void requestNotification(HANDLE pr, CWnd * tell);
       static UINT UWM_PROCESS_TERMINATED;
    protected:
       HANDLE hProcess; // process handle
       CWnd * notifyee; // window to notify
       static UINT waiter(LPVOID p) { ((WaitInfo *)p)->waiter(); return 0; }
       void waiter();
};

/****************************************************************************
*                           UWM_PROCESS_TERMINATED
* Inputs:
*       WPARAM: ignored
*       LPARAM: Process handle of process
* Result: LRESULT
*       Logically void, 0, always
* Effect: 
*       Notifies the parent window that the process has been terminated
* Notes:
*       It is the responsibility of the parent window to perform a
*       ::CloseHandle operation on the handle. Otherwise there will be
*       a handle leak.
****************************************************************************/
#define UWM_PROCESS_TERMINATED_MSG 
_T("UWM_PROCESS_TERMINATED-{F7113F80-6D03-11d3-9FDD-006067718D04}")

WaitInfo.cpp

// 
#include "stdafx.h"
#include "WaitInfo.h"
UINT WaitInfo::UWM_PROCESS_TERMINATED = 
::RegisterWindowMessage(UWM_PROCESS_TERMINATED_MSG); /**************************************************************************** * WaitInfo::requestNotification * Inputs: * HANDLE pr: Process handle * CWnd * wnd: Window to notify on completion * Result: void * * Effect: * Spawns a waiter thread ****************************************************************************/ void WaitInfo::requestNotification(HANDLE pr, CWnd * wnd) { hProcess = pr; notifyee = wnd; AfxBeginThread(waiter, this); } // WaitInfo::requestNotification /**************************************************************************** * WaitInfo::waiter * Result: void * * Effect: * Waits for the thread to complete and notifies the parent ****************************************************************************/ void WaitInfo::waiter() { ::WaitForSingleObject(hProcess, INFINITE); notifyee->PostMessage(UWM_PROCESS_TERMINATED, 0, (LPARAM)hProcess); } // WaitInfo::waiter

The way this is used is that after you have created your process, you call the requestNotification method to request a notification. You pass in the handle of the process and the window which is to receive the notification. When the process terminates, a notification message is sent to the specified window. You must have a WaitInfo object that is created before the requestNotification is called and remains valid until the notification message is received; this means that it cannot be a variable on the stack. In the example code I provide, I put it in the class header of the window class that launches the program.

In the header file for my class, I add the following:

WaitInfo requestor;
afx_msg LRESULT OnCompletion(WPARAM, LPARAM)

In the MESSAGE_MAP of the window, you need to add a line for the handler. Because this uses a qualified name, the ClassWizard is emotionally unprepared to deal with it, so you have to place it as shown, after the //}}AFX_MSG_MAP line.

    //}}AFX_MSG_MAP
    ON_REGISTERED_MESSAGE(WaitInfo::UWM_PROCESS_COMPLETED, OnCompletion)
END_MESSAGE_MAP()

After I launch the process, I do

    HANDLE process = launcher_of_your_choice(program, args);
    if(process != NULL)
       { /* success */
        requestor.requestNotification(process, this);
       } /* success */

The handler is quite simple:

LRESULT CMyClass::OnCompletion(WPARAM, LPARAM lParam)
    {
     // whatever you want to do here
     ::CloseHandle((HANDLE)lParam);
     return 0;
    }

You can study more about what I do in the sample file.

If some of the above looked confusing, you might want to read my essays on message management and worker threads.

Comments