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

71 views Asked by At

My application has a separate thread that repeatably performs some check. If the check fails, the UI thread is notified (a MessageBox is displayed that requires user action what to do next).

Unfortunately, I have to use C++03 compiler (Visual Studio 2010 SP1) and boost library usage is prohibited. Therefore, I cannot use <thread>, <atomic>, <chrono>, etc. Thats why I have to use CreateThread, PostMessage and other WinAPI functions.

Here is my UI thread code (simplified). My main window is CMDIFrameWnd (from 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;
}

Here is my separate thread with repeatable check code (simplified):

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);
    }
}

The result is that the main window becomes unresponsive after some time. It looks like my code has some logical mistake, or a memory leak.

I'm a newbie to C++ (and especially to outdated standards).

0

There are 0 answers