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
WaitInfo.cpp // #include "stdafx.h" #include "WaitInfo.h" UINT WaitInfo::UWM_PROCESS_TERMINATED = |
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