если мы запускаем дочерний процесс и хотим, чтобы когда он и все дочерние процессы завершались, мы можем использовать объект задания. общие шаги
, конечно, нам нужно Также создайте порт завершения ввода / вывода с помощью CreateIoCompletionPort
и одного или нескольких (если только для этой задачи - одного потока более чем достаточно) потоков, которые будут вызывать GetQueuedCompletionStatus
на порту до конца сигнала.
мы можем, например, использовать lpCompletionKey в качестве указателя на объект с виртуальными функциями, и каждый объект знает, как обрабатывать событие действия. демонстрационный код:
struct __declspec(novtable) PortTask
{
virtual bool OnIoCompletion(OVERLAPPED* lpOverlapped, ULONG NumberOfBytesTransferred) = 0;
};
struct EndTask : public PortTask
{
virtual bool OnIoCompletion(OVERLAPPED* /*lpOverlapped*/, ULONG /*NumberOfBytesTransferred*/)
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
delete this;
return false;
}
};
struct IOPort
{
HANDLE CompletionPort;
LONG dwRefCount;
IOPort() : dwRefCount(1), CompletionPort(0) {
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
~IOPort(){
if (CompletionPort) CloseHandle(CompletionPort);
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
void AddRef(){
InterlockedIncrementNoFence(&dwRefCount);
}
void Release(){
if (!InterlockedDecrement(&dwRefCount)) {
delete this;
}
}
static ULONG WINAPI PortThread(PVOID This)
{
union {
ULONG_PTR CompletionKey;
PortTask* pTask;
};
ULONG NumberOfBytesTransferred;
OVERLAPPED* lpOverlapped;
HANDLE CompletionPort = reinterpret_cast<IOPort*>(This)->CompletionPort;
while (GetQueuedCompletionStatus(CompletionPort, &NumberOfBytesTransferred, &CompletionKey, &lpOverlapped, INFINITE) &&
pTask->OnIoCompletion(lpOverlapped, NumberOfBytesTransferred)) continue;
reinterpret_cast<IOPort*>(This)->Release();
return 0;
}
ULONG Create()
{
if (CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1))
{
AddRef();
if (HANDLE hThread = CreateThread(0, 0, PortThread, this, 0, 0))
{
CloseHandle(hThread);
return NOERROR;
}
Release();
}
return GetLastError();
}
ULONG Stop()
{
if (EndTask* pTask = new EndTask)
{
if (!PostQueuedCompletionStatus(CompletionPort, 0, (ULONG_PTR)pTask, 0))
{
ULONG dwError = GetLastError();
delete pTask;
return dwError;
}
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
};
struct ActiveProcessZeroTask : public PortTask
{
//HWND hwnd; // in real code you send some message to hwnd instead thread
HANDLE _hJob;
ULONG _dwThreadId;
ActiveProcessZeroTask() : _hJob(0), _dwThreadId(GetCurrentThreadId()) { }
~ActiveProcessZeroTask() {
CloseHandle(_hJob);
PostThreadMessageW(_dwThreadId, WM_QUIT, 0, 0);
}
virtual bool OnIoCompletion(OVERLAPPED* dwProcessId, ULONG MessageId)
{
DbgPrint("%s<%p>(%x %p)\n", __FUNCTION__, this, MessageId, dwProcessId);
switch (MessageId)
{
case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
DbgPrint("%p - ACTIVE_PROCESS_ZERO\n", dwProcessId);
delete this;
break;
case JOB_OBJECT_MSG_NEW_PROCESS:
DbgPrint("%p - NEW_PROCESS\n", dwProcessId);
break;
case JOB_OBJECT_MSG_EXIT_PROCESS:
DbgPrint("%p - EXIT_PROCESS\n", dwProcessId);
break;
case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
DbgPrint("%p - ABNORMAL_EXIT_PROCESS\n", dwProcessId);
break;
}
return true;
}
ULONG Create(HANDLE CompletionPort, PCWSTR ApplicationName)
{
if (HANDLE hJob = CreateJobObjectW(0, 0))
{
_hJob = hJob;
JOBOBJECT_ASSOCIATE_COMPLETION_PORT jacp = { this, CompletionPort };
if (SetInformationJobObject(hJob, JobObjectAssociateCompletionPortInformation, &jacp, sizeof(jacp)))
{
STARTUPINFO si = { sizeof(si)};
PROCESS_INFORMATION pi;
if (CreateProcessW(ApplicationName, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi))
{
ULONG dwError = NOERROR;
if (!AssignProcessToJobObject(hJob, pi.hProcess) ||
!ResumeThread(pi.hThread))
{
dwError = GetLastError();
TerminateProcess(pi.hProcess, 0);
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return dwError;
}
}
}
return GetLastError();
}
};
void demo()
{
if (IOPort* port = new IOPort)
{
if (port->Create() == NOERROR)
{
MessageBoxW(0, 0, L"just for demo #1", MB_ICONINFORMATION);
// exec cmd for demo
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
if (ActiveProcessZeroTask* pTask = new ActiveProcessZeroTask)
{
if (pTask->Create(port->CompletionPort, ApplicationName) != NOERROR)
{
delete pTask;
}
}
}
// wait all childs exit
MessageBoxW(0, 0, L"Wait for MSG_ACTIVE_PROCESS_ZERO", MB_ICONINFORMATION);
// stop track thread
if (port->Stop() != NOERROR) __debugbreak();
}
port->Release();
}
{
MSG msg;
// remove Wm_QUIT
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) continue;
MessageBoxW(0, 0, L"just for demo #2", MB_ICONINFORMATION);
}
}