c++ - 等待 Windows 线程中的句柄

标签 c++ winapi mfc

我有一个 MFC 应用程序,它使用 CreateProcess(...) 启动另一个进程。我想在创建的进程终止时执行 UI 更新。通常,我会在返回的进程HANDLE上使用WaitForSingleObjectWaitForMutlipleObject,但这会阻塞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/

相关文章:

c++ - 如何迭代 CString 并将其字符与 int 进行比较?

c++ - 用cout建表,复制的代码格式不一样

c++ - 从 MFC C++ 6.0 应用程序迁移到 VS 2005

c++ - 在 Howard Hinnant 的 Date 库中获取毫秒舍入时间戳字符串的最佳方法是什么?

c - 从 C 或 Delphi 从 Win32 获取 BIOS UUID

c++ - directx 9 SetTime 不是成员 c++

C# PostMessage 语法,试图将 WM_CHAR 发布到另一个应用程序窗口

c++ - 当另一个窗口关闭时关闭无模式对话框

c++ - 像Matlab一样在C++中分配数组?

c++ - 越界访问数组不会出错,为什么?