Мультимедийный таймер прекрасно работает в режиме выпуска, но не в режиме отладки


Я пытаюсь использовать mmTimer с функцией обратного вызова, которая является статической функцией CALLBACK. Я знаю, что статическая функция не может вызвать нестатическую функцию, благодаря вам всем, ребята, за исключением случая, когда статическая функция получает указатель на объект в качестве аргумента. самое странное, что мой таймер отлично работает в режиме выпуска, и когда я пытаюсь запустить его в режиме отладки, появляется это необработанное исключение,которое ломает программу.

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

Мои вопросы таковы: есть ли способ решить эту проблему? - Если эта ошибка возникает в режиме отладки, кто гарантирует мне, что она не произойдет, как только я выпущу программу?

Есть место, где программа останавливается:

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

Когда он добирается до pMap, он останавливается на этом утверждении!!!!

Вот статическая функция обратного вызова

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

Вот как я устанавливаю таймер

UINT unTimerID = timeSetEvent(m_TimeDelay,1,(LPTIMECALLBACK)TimerProc,(DWORD)this,TIME_PERIODIC);
2 2

2 ответа:

Проблема здесь в том, что multimedia timer API, в отличие от многих других, имеет ограничения на то, что вам разрешено делать внутри обратного вызова. Вы в основном не позволяете много , и то, что вам разрешено, - это обновить внутренние структуры, сделать некоторые выходные данные отладки и установить событие синхронизации.

Примечания

Приложения не должны вызывать какие-либо системные функции изнутри функция обратного вызова, за исключением PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg и OutputDebugString.

Ошибки утверждения запускают отображение окон сообщений, которые не разрешены и могут в конечном итоге привести к сбою процесса. Кроме того, оконные API, такие как IsWindow и друзья, также не допускаются и являются первой причиной, приводящей в дальнейшем к ошибкам утверждения.

Здесь лучше всего вообще не использовать мультимедийные таймеры. В большинстве случаев у вас есть менее ограничительные альтернативные варианты.

Это только выглядит как ваш код работает в сборке выпуска, он не будет утверждать (), что вы делаете это правильно. И вы делаете это неправильно.

Обратный вызов от мультимедийного таймера выполняется в произвольном потоке пула потоков. Вы должны быть Очень осторожны в том, что вы делаете в обратном вызове. Во-первых, вы не можете непосредственно коснуться пользовательского интерфейса, этот код принципиально небезопасен для потоков. Поэтому вы наверняка не можете вызвать UpdateData (). В лучшем случае вы обновляете переменную и позволяете поток пользовательского интерфейса знает, что ему нужно обновить окно. Используйте PostMessage (). В общем случае вам нужен критический раздел, чтобы гарантировать, что ваш обратный вызов не обновит эту переменную, пока поток пользовательского интерфейса использует ее для обновления окна.

Утверждение, которое вы получаете в отладочной сборке, предполагает больше проблем. Похоже, вы не уверены, что таймер больше не может вызвать обратный вызов, когда пользователь закрывает окно. Это довольно трудно решить чисто, это фундаментальная гонка потоков. Метод postMessage() это уже убережет тебя от худших неприятностей. Чтобы сделать это идеально чистым, вы должныпредотвратить закрытие окна, пока выне узнаете , что таймер больше не может вызывать обратный вызов. Что требует установки события, когда вы получаете WM_CLOSE и не вызываете DestroyWindow. Обратный вызов таймера должен проверить это событие, вызвать timeKillEvent () и отправить другое сообщение. Который поток пользовательского интерфейса теперь может использовать, чтобы действительно закрыть окно.

Резьба трудна, убедитесь, что SetTimer() не является уже достаточно хорошо, чтобы сделать свою работу. Это, безусловно, будет, если обновление пользовательского интерфейса является единственным побочным эффектом. TimeSetEvent () нужен только тогда, когда требуется точный таймер, который должен сделать что-то, что не связано с пользовательским интерфейсом . Человеческие глаза просто не имеют такого требования. Это делают только наши уши.