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