WinAPI:在单独的线程中重复检查,当检查失败时通知 UI 线程

WinAPI: repeatable check in a separate thread that notifies UI thread when check fails

提问人:bairog 提问时间:12/29/2022 最后编辑:Remy Lebeaubairog 更新时间:12/30/2022 访问量:66

问:

我的应用程序有一个单独的线程,可以重复执行一些检查。如果检查失败,则会通知 UI 线程(显示一个 MessageBox,要求用户操作下一步操作)。

不幸的是,我必须使用 C++ 编译器 (Visual Studio 2010 SP1) 并且禁止使用提升库。因此,我不能使用 、 、 等。这就是为什么我必须使用 ,和其他 WinAPI 函数。<thread><atomic><chrono>CreateThreadPostMessage

这是我的UI线程代码(简化)。我的主窗口是 CMDIFrameWnd(来自 MFC):

//a struct with all parameters that is needed for a repeatable check
struct RepeatFunctionParameters
{
    unsigned int repeatDelayInMilliseconds;
    HWND checkIsFailedPostMessageWindowHandler;
    UINT checkIsFailedPostMessageMessageId;
    HANDLE checkIsPausedMutexHandle;

    RepeatFunctionParameters(unsigned int _repeatDelayInMilliseconds, HWND _checkIsFailedPostMessageWindowHandler,
        UINT _checkIsFailedPostMessageMessageId, HANDLE _haspSerialCheckIsPausedMutexHandle)
        : repeatDelayInMilliseconds(_repeatDelayInMilliseconds), checkIsFailedPostMessageWindowHandler(_checkIsFailedPostMessageWindowHandler),
        checkIsFailedPostMessageMessageId(_checkIsFailedPostMessageMessageId), haspSerialCheckIsPausedMutexHandle(_haspSerialCheckIsPausedMutexHandle)
    {}
};

----------------------------
//creating a mutex to pause repeatable checks (whe Messagebox is displayed in UI thread)
HANDLE haspSerialCheckIsPausedMutexHandle = CreateMutex(NULL, FALSE, NULL);

//starting a separate thread with a check that repeats every 5000 milliseconds
auto params = new RepeatFunctionParameters(5000, myApp_hWnd, WM_USER_HASP_CHECK_FAILED, haspSerialCheckIsPausedMutexHandle);
CreateThread(NULL, 0, RepeatFunction, params, 0, NULL);

----------------------------
//special message that is sended when check is failed
#define WM_USER_HASP_CHECK_FAILED              (WM_USER+0x150)

//mapping message handling function to that message
ON_MESSAGE( WM_USER_HASP_CHECK_FAILED, OnUserHaspCheckFailed)

//message handling function definition
afx_msg LRESULT OnUserHaspCheckFailed(WPARAM wParam, LPARAM lParam);

//message handling function body 
LRESULT CMainWnd::OnUserHaspCheckFailed(WPARAM wParam, LPARAM lParam)
{
    //capturing a mutex that signals to pause repeatable checks
    WaitForSingleObject(haspSerialCheckIsPausedMutexHandle, INFINITE);
    
    //show a messagebox that requires user action what to do next
    if (::MessageBox(myApp_hWnd, ("Check is failed! Retry or cancel?").c_str(),
            myApp_name, MB_RETRYCANCEL | MB_ICONERROR | MB_SYSTEMMODAL) == IDCANCEL)
            //closing main windows if user clicks Cancel
            pWnd->SendMessage(WM_CLOSE, 0x00010000, 0);

    //releasing a mutex that signals to pause repeatable checks
    ReleaseMutex(haspSerialCheckIsPausedMutexHandle);

    return 0;
}

//WM_CLOSE handling function body
LRESULT CMainWnd::OnClose( WPARAM wParam, LPARAM lParam)
{
    ----------------------------

    if( haspSerialCheckIsPausedMutexHandle != NULL)
        CloseHandle( haspSerialCheckIsPausedMutexHandle);

    ----------------------------
    
    CMDIFrameWnd::OnClose();
    return NULL;
}

这是我带有可重复检查代码(简化)的单独线程:

DWORD WINAPI RepeatFunction(LPVOID parameters)
{
    //getting parameters struct from a pointer
    auto temp = static_cast<RepeatFunctionParameters*>(parameters);
    //make a struct local copy (Further, all work goes only with it, regardless of the 
    state of the object, the pointer to which came as a function parameter)
    auto params = *temp;
    //deleting the structure, the pointer to which came as a function parameter
    delete temp;

    //repeatable check
    while (true)
    {
        //checking a mutex that signals to pause repeatable checks. if it is free
        //then there is no messagebox in UI thread and we can perform a check.
        //if it is captured - wait until user clicks some button in that messagebox 
        WaitForSingleObject(params.haspSerialCheckIsPausedMutexHandle, INFINITE);
        //and releasing it immediately
        ReleaseMutex(params.haspSerialCheckIsPausedMutexHandle);

        auto startMilliseconds = GetTickCount();    
        //performing a check
        BOOL success = PerformACheck();

        unsigned long defaultSleepDelay = 1000;
        //if PerformACheck() will last longer than params.repeatDelayInMilliseconds,
        //then check will be repeated after 1000 milliseconds, otherwise - 
        //after params.repeatDelayInMilliseconds minus PerformACheck() call time
        auto endMilliseconds = GetTickCount();
        if ((endMilliseconds - startMilliseconds) < params.repeatDelayInMilliseconds)
            sleepDelay = params.repeatDelayInMilliseconds - (endMilliseconds - startMilliseconds);

        //if check is failed
        if (!success)
        {
            //sending a message with an identifier params.checkIsFailedPostMessageMessageId
            //to a HWND params.checkIsFailedPostMessageWindowHandler so in it's 
            //handling function a messagebox with will be displayed and a mutex 
            //params.haspSerialCheckCanRunMutexHandle will be captured until
            //user click some button in that messagebox 
            PostMessage(params.checkIsFailedPostMessageWindowHandler, params.checkIsFailedPostMessageMessageId, 0, 0);

            //if check is failed then next check always repeats after 1000 milliseconds
            sleepDelay = 1000;
        }

        Sleep(sleepDelay);
    }
}

结果是主窗口在一段时间后变得无响应。看起来我的代码有一些逻辑错误,或者内存泄漏。

我是 C++ 的新手(尤其是过时的标准)。

C++ 多线程 WinAPI MFC C++03

评论

2赞 Simon Mourier 12/29/2022
为什么不改用 SendMessage 并去掉互斥锁(PS:互斥锁是 Windows 不等同于 std:mutex,它通常更适合跨进程同步)。或者 SendMessageTimeout 检查窗口是否死或处理时间异常长。
0赞 bairog 12/29/2022
@SimonMourier发送消息并等待功能完成并返回?我只需要将单行 ( 更改为 ) 并摆脱互斥锁?或者我需要更改其他内容(将消息处理函数映射到消息或函数定义/正文的代码)?SendMessageOnUserHaspCheckFailedPostMessageSendMessage
1赞 Simon Mourier 12/29/2022
在窗口过程处理完消息之前,SendMessage 不会返回,这似乎是您希望使用互斥锁实现的。而且你不会得到任何可能的竞争条件。我不能尝试,因为这不是一个完全可重复的样品 stackoverflow.com/help/minimal-reproducible-example
0赞 bairog 12/29/2022
@SimonMourier 是的,这正是我想用互斥锁实现的目标。好的,我将尝试进行 2 次更改(更改为并摆脱互斥锁)并编译我的代码。我会通知调试结果。PostMessageSendMessage

答: 暂无答案