I have the following code sample:
#include
#include
#include
using namespace std;
void main()
{
SHELLEXEC
I tried your code on Windows 10, and SysInternals Process Monitor shows the following:
As you can see, calc.exe
spawns a new process and then ends, thus satisfying the wait. That is why you see your output immediately. This second process is the actual calculator process. In my case, it is located at this path:
C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_10.1612.3341.0_x64__8wekyb3d8bbwe\Calculator.exe
As marcinj explained, calc.exe
is launching a second process and then exiting immediately. You are waiting on a HANDLE
to the calc.exe
process, so that wait is satisfied when calc.exe
exits.
If you want to wait until all (grand)children processes have ended, you need to wrap the calc.exe
process inside of a Job object.
Call CreateJobObject(), pass the HANDLE
of the calc.exe
process to AssignProcessToJobObject(), and then wait for notifications from the job object (simply waiting on the job itself will not work, see How do I wait until all processes in a job have exited? for an explanation why).
Any new processes that calc.exe
launches, and that they launch, and so on, will automatically be added to the same job (unless they explicitly request not to be added to the job, via the CREATE_BREAKAWAY_FROM_JOB
flag of CreateProcess()
, but let's not worry about that in this example). When the job notifies you that all processes have ended, you can stop waiting for further notifications.
For example:
#include <windows.h>
#include <iostream>
#include <string>
using namespace std;
int main()
{
HANDLE Job = CreateJobObject(NULL, NULL);
if (!Job)
{
std::cout << "CreateJobObject, error " << GetLastError() << "\n";
return 0;
}
HANDLE IOPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (!IOPort)
{
std::cout << "CreateIoCompletionPort, error " << GetLastError() << "\n";
CloseHandle(Job);
return 0;
}
JOBOBJECT_ASSOCIATE_COMPLETION_PORT Port;
Port.CompletionKey = Job;
Port.CompletionPort = IOPort;
if (!SetInformationJobObject(Job, JobObjectAssociateCompletionPortInformation, &Port, sizeof(Port)))
{
std::cout << "SetInformation, error " << GetLastError() << "\n";
CloseHandle(IOPort);
CloseHandle(Job);
return 0;
}
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {};
WCHAR cmdline[] = L"calc.exe";
if (!CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
{
std::cout << "CreateProcess, error " << GetLastError() << "\n";
CloseHandle(IOPort);
CloseHandle(Job);
return 0;
}
if (!AssignProcessToJobObject(Job, pi.hProcess))
{
std::cout << "Assign, error " << GetLastError() << "\n";
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(IOPort);
CloseHandle(Job);
return 0;
}
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
DWORD CompletionCode;
ULONG_PTR CompletionKey;
LPOVERLAPPED Overlapped;
while (GetQueuedCompletionStatus(IOPort, &CompletionCode, &CompletionKey, &Overlapped, INFINITE))
{
if ((reinterpret_cast<HANDLE>(CompletionKey) == Job) &&
(CompletionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO))
{
break;
}
}
CloseHandle(IOPort);
CloseHandle(Job);
std::cout << "hi! Im done!";
return 0;
}