Question

I'm trying to use mmTimer with a callback function, which is a static CALLBACK function. I know that a static function cannot call a non-static function, thanks to you all guys, except from the case where the static function gets a pointer to an object as an argument. the weird thing is that my timer works fine in release mode, and when I try to run it in debug mode there is this unhandeled exception that pops up and breaks the program down.

void  CMMTimerDlg::TimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
    CMMTimerDlg* p = (CMMTimerDlg*)dwUser;
    if(p)
    {
        p->m_MMTimer += p->m_TimeDelay;
        p->UpdateData(FALSE);
    }
}

my questions are : - is there any way to resolve this problem? - If this error occurs on debug mode, who ensures me that it wouldn't happen once i release the program?

there is where the program stops:

#ifdef _DEBUG
void CWnd::AssertValid() const
{
    if (m_hWnd == NULL)
        return;     // null (unattached) windows are valid

    // check for special wnd??? values
    ASSERT(HWND_TOP == NULL);       // same as desktop
    if (m_hWnd == HWND_BOTTOM)
        ASSERT(this == &CWnd::wndBottom);
    else if (m_hWnd == HWND_TOPMOST)
        ASSERT(this == &CWnd::wndTopMost);
    else if (m_hWnd == HWND_NOTOPMOST)
        ASSERT(this == &CWnd::wndNoTopMost);
    else
    {
        // should be a normal window
        ASSERT(::IsWindow(m_hWnd));

        // should also be in the permanent or temporary handle map
        CHandleMap* pMap = afxMapHWND();
        ASSERT(pMap != NULL);

when it gets to pMap it stops at that assertion!!!!

here is the static CALLBACK function

static void  CALLBACK TimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);

here is how I set the timer

UINT unTimerID = timeSetEvent(m_TimeDelay,1,(LPTIMECALLBACK)TimerProc,(DWORD)this,TIME_PERIODIC);
Was it helpful?

Solution

The problem here is that multimedia timer API unlike many other has restrictions on what you are allowed to do inside the callback. You are basically not allowed much and what you are allowed is to update internal structures, do some debug output, and set an synchronization event.

Remarks

Applications should not call any system-defined functions from inside a callback function, except for PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and OutputDebugString.

Assertion failures start display message boxes which are not allowed and can eventually crash the process. Additionally, windowing API such as IsWindow and friends are not allowed either and are the first place cause leading further to assertion failures.

The best here is to avoid using multimedia timers at all. In most cases you have less restrictive alternate options.

OTHER TIPS

It only looks like your code works in the Release build, it will not assert() that you are doing it right. And you are not doing it right.

The callback from a multi-media timer runs on an arbitrary thread-pool thread. You have to be very careful about what you do in the callback. For one, you cannot directly touch the UI, that code is fundamentally thread-unsafe. So you most certainly cannot call UpdateData(). At best, you update a variable and let the UI thread know that it needs to refresh the window. Use PostMessage(). In general you need a critical section to ensure that your callback doesn't update that variable while the UI thread is using it to update the window.

The assert you get in the Debug build suggests more trouble. Looks like you are not making sure that the timer can no longer callback when the user closes the window. That's pretty hard to solve cleanly, it is a fundamental threading race. PostMessage() will already keep you out of the worst trouble. To do it perfectly clean, you must prevent the window from closing until you know that the timer cannot callback anymore. Which requires setting an event when you get WM_CLOSE and not call DestroyWindow. The timer's callback needs to check that event, call timeKillEvent() and post another message. Which the UI thread can now use to really close the window.

Threading is hard, do make sure that SetTimer() isn't already good enough to get the job done. It certainly will be if the UI update is the only side-effect. You only need timeSetEvent() when you require an accurate timer that needs to do something that is not UI related. Human eyes just don't have that requirement. Only our ears do.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top