首页 文章

等待Windows线程中的句柄

提问于
浏览
2

我有一个MFC应用程序,使用 CreateProcess(...) 启动另一个进程 . 我想在创建的进程终止时执行UI更新 . 通常,我会在返回的进程 HANDLE 上使用 WaitForSingleObjectWaitForMutlipleObject 但这会阻止GUI线程(坏) .

我能想到的唯一解决方案是生成一个新线程,该线程可以等待句柄并在进程终止时发布消息 . 这不太理想 .

那么是否可以使用Windows管理器注册句柄并在进程终止时接收Windows消息?

5 回答

  • 0

    我会做这两个中的一个:

    • 在某个地方的消息循环中调用 WaitForSingleObject() 或超时为零的任何内容(可能必须将循环更改为 PeekMessage() 或添加 WM_TIMER 消息以确保每隔一段时间检查一次)

    • 或者更好的是,产生一个具有非常小的堆栈的线程(您可以在 CreateThread() 调用中自定义)仅等待此子进程,然后将消息发布到您的消息循环 .

    我更喜欢选项2,因为一个小堆栈的线程除了等待东西之外什么都不做,这几乎不是资源消耗 .

  • 0

    当进程结束时,您可以使用RegisterWaitForSingleObject()通过回调获得通知 . RegisterWaitForSingleObject 函数指示thread pool中的等待线程等待进程,因此这应该是资源的最佳使用 . 正如Raymond Chen评论的那样:

    线程池可以将多个Wait请求批处理为对WaitForMultipleObjects的单个调用,因此摊销成本是线程的1/63 .

    下面是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;
    }
    

    Bonus OldNewThing readWhy bother with RegisterWaitForSingleObject when you have MsgWaitForMultipleObjects?

  • 1

    如果您可以修改子进程的代码,您可以添加一个反向通道,该通道将在它即将离开时通过 SendMessage 通知父进程 . 如果你不能这样做,你可以创建一个中继过程,它只传递原始的子过程数据(如果有的话),但会在孩子离开时完成信息工作 . 当然,这至少要比仅仅使用专用线程和例如线程要少得多 . WaitForSingleObject .

  • 2

    解决方案是在创建应用程序时创建一个线程 . 然后等待一个应该在需要时发出脉冲的事件 . 例:

    BOOL bStatus = TRUE;
    CEvent mEvevnt;
    
    // thread function
    UINT LaunchThread( LPVOID p )
    {
        while(bStatus && ::WaitForSingleObject(HANDLE(mEvevnt), INFINITE) == WAIT_OBJECT_0) {
                // create procees here
        }
    }
    
    // thread creation
    AfxBeginThread(LaunchThread, NULL);
    

    触发线程进入操作:

    mEvevnt.PulseEvent();
    

    你的应用程序结束时销毁线程:

    bStatus = FALSE;
    mEvevnt.PulseEvent();
    
  • 0

    好消息! Windows拥有您正在寻找的API:MsgWaitForMultipleObjects () .

    Tricker,是为了让它进入MFC的消息泵,但是我发现this link建议执行以下操作(代码未经测试,修复(!),并且适合仅在一个句柄上等待):

    // virtual
    BOOL CMyApp::PumpMessage()
    {
        DWORD const res = ::MsgWaitForMultipleObjects
            (1, &handle_I_am_interested in, TRUE, INFINITE, QS_ALLINPUT);
    
        switch (res)
        {
            case WAIT_OBJECT_0 + 0:
                // the handle was signalled, strut your stuff here
                return TRUE;
    
            case WAIT_OBJECT_0 + 1:
                // there is a message in the queue, let MFC handle it
                return __super::PumpMessage();
        }
    
        // Shouldn't happen
        return TRUE;
    }
    

    我不得不说这段代码对我来说仍然不太理想,但它可能足够接近 . 我不太了解MFC进一步评论 .

    Please note: 在MFC通过消息泵之前,此代码不会看到句柄已发出信号 . 例如,当 MessageBox() 具有控制权时,可能会发生这种情况 . 如果这让您感到困扰,请考虑使用RegisterWaitForSingleObject,正如传奇Raymond Chen所推荐的那样 .

相关问题