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.