Вы можете использовать RegisterWaitForSingleObject()
, чтобы получать уведомления с помощью обратного вызова, когда процесс завершится.Функция RegisterWaitForSingleObject
направляет поток ожидания в пуле потоков для ожидания процесса, поэтому это должно быть оптимальным использованием ресурсов.Как прокомментировал Раймонд Чен:
Пул потоков может объединять несколько запросов Wait в один вызов WaitForMultipleObjects, поэтому амортизируемая стоимость составляет 1/63 потока.
AНиже приведен минимальный пример приложения с графическим интерфейсом Win32.Код создает окно, затем создает другой экземпляр себя как дочерний процесс, который указывается параметром "/ child".Он регистрирует функцию обратного вызова ожидания и запускает обычный цикл обработки сообщений.Вы можете изменить размер и переместить окно, чтобы увидеть, что графический интерфейс не заблокирован.Когда дочерний процесс завершился, система асинхронно вызывает обратный вызов ожидания, который отправляет определенное приложением сообщение (WM_APP
) в окно.Когда окно получает сообщение, оно немедленно вызывает UnregisterWait()
, чтобы отменить ожидание.Как говорится в ссылке, даже операции ожидания, которые используют WT_EXECUTEONLYONCE
, должны быть отменены, когда ожидание завершено (но не из-за обратного вызова!).Затем в окне отображается окно сообщения, демонстрирующее, что оно получило сообщение.
Для краткости обработка ошибок опущена.Вам следует проверить возвращаемое значение каждой функции API и вызвать GetLastError()
в случае, если возвращается FALSE
.
#pragma comment(linker, "/SubSystem:Windows")
#include <windows.h>
#include <string>
int APIENTRY wWinMain( HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPWSTR lpCmdLine, int /*nCmdShow*/ )
{
if ( wcsstr( lpCmdLine, L"/child" ) )
{
MessageBoxW( nullptr, L"Hello from child process!", L"Child", MB_OK );
return 0;
}
// Create window
struct WindowData
{
HANDLE hWait = nullptr;
}
wndData;
WNDCLASSW wc{};
wc.hInstance = hInstance;
wc.hCursor = LoadCursor( nullptr, IDC_ARROW );
wc.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject( WHITE_BRUSH ));
wc.lpszClassName = L"MyClass";
wc.cbWndExtra = sizeof(LONG_PTR);
wc.lpfnWndProc = []( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch ( message )
{
case WM_APP:
{
// When the wait is completed, you must call the UnregisterWait or UnregisterWaitEx function to cancel
// the wait operation. (Even wait operations that use WT_EXECUTEONLYONCE must be canceled.)
WindowData* pWndData = reinterpret_cast<WindowData*>(GetWindowLongPtr( hWnd, 0 ));
UnregisterWait( pWndData->hWait );
pWndData->hWait = nullptr;
MessageBoxW( hWnd, L"Child process has ended!", L"Main", MB_OK );
}
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
}
return DefWindowProc( hWnd, message, wParam, lParam );
};
RegisterClassW( &wc );
HWND hWnd = CreateWindowExW( 0, wc.lpszClassName, L"Main", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr );
SetWindowLongPtr( hWnd, 0, reinterpret_cast<LONG_PTR>( &wndData) );
// Create child process
std::wstring cmd( MAX_PATH, L'\0' );
cmd.resize( GetModuleFileNameW( nullptr, &cmd[0], cmd.size() ) );
cmd = L"\"" + cmd + L"\" /child";
STARTUPINFOW si{ sizeof( si ) };
PROCESS_INFORMATION pi{};
CreateProcessW( nullptr, &cmd[0], nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi );
// Get notified when child process ends
RegisterWaitForSingleObject( &wndData.hWait, pi.hProcess,
[]( PVOID lpParameter, BOOLEAN /*TimerOrWaitFired*/ )
{
PostMessage( reinterpret_cast<HWND>(lpParameter), WM_APP, 0, 0 );
},
reinterpret_cast<PVOID>(hWnd), INFINITE, WT_EXECUTEONLYONCE );
// Run message loop
MSG msg;
while ( GetMessage( &msg, nullptr, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// Cleanup
if( wndData.hWait )
UnregisterWait( wndData.hWait );
if( pi.hProcess )
CloseHandle( pi.hProcess );
if( pi.hThread )
CloseHandle( pi.hThread );
return 0;
}
Bonus OldNewThing read : Зачем беспокоиться о RegisterWaitForSingleObject, когдау вас есть MsgWaitForMultipleObjects?