我有一个 MFC 应用程序,它使用 CreateProcess(...)
启动另一个进程。我想在创建的进程终止时执行 UI 更新。通常,我会在返回的进程HANDLE
上使用WaitForSingleObject
或WaitForMutlipleObject
,但这会阻塞GUI线程(不好)。
我能想到的唯一解决方案是生成一个新线程,该线程可以在句柄上等待并在进程终止时发布消息。这并不理想。
那么是否可以向 Windows 管理器注册句柄并在进程终止时接收 Windows 消息?
最佳答案
您可以使用RegisterWaitForSingleObject()
当进程结束时通过回调收到通知。 RegisterWaitForSingleObject
函数在 thread pool 中指示一个等待线程。等待进程,所以这应该是资源的最佳利用。正如 Raymond Chen 评论的那样:
The thread pool can batch multiple Wait requests into a single call to WaitForMultipleObjects so the amortized cost is 1/63 of a thread.
下面是一个 Win32 GUI 应用程序的最小示例。该代码创建一个窗口,然后创建其自身的另一个实例作为子进程,由“/child”参数指示。它注册等待回调函数并运行常规消息循环。您可以调整窗口大小并移动窗口以查看 GUI 是否未被阻挡。当子进程结束时,系统异步调用等待回调,该回调将应用程序定义的消息 (WM_APP
) 发送到窗口。当窗口收到消息时,立即调用UnregisterWait()
取消等待。正如引用所述,即使使用 WT_EXECUTEONLYONCE
的等待操作也必须在等待完成时取消(但不能在回调中取消!)。然后窗口会显示一个消息框,表明已收到消息。
为简洁起见,省略了错误处理。您应该检查每个 API 函数的返回值,并在返回 FALSE
时调用 GetLastError()
。
#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;
}
旧新事物阅读奖励:Why bother with RegisterWaitForSingleObject when you have MsgWaitForMultipleObjects?
关于c++ - 等待 Windows 线程中的句柄,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50759946/