2013-12-01 3 views
2

Я пытаюсь использовать 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 он останавливается на этом утверждении !!!!

здесь статическая функция CALLBACK

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); 
+3

Как установить таймер вверх? Что такое стек вызовов в позиции исключения? –

+2

@Roman R UINT unTimerID = timeSetEvent (m_TimeDelay, 1, (LPTIMECALLBACK) TimerProc, (DWORD) это, TIME_PERIODIC); – user2390922

+3

Вы на 32-битной платформе? DWORD нельзя использовать указатель s на 64-битном. не –

ответ

3

Проблема здесь состоит в том, что мультимедийный API таймера в отличие от многих других есть ограничения на то, что вам разрешено делать внутри обратного вызова. Вы в основном not allowed much, и вам разрешено обновлять внутренние структуры, выполнять отладочный вывод и устанавливать событие синхронизации.

Примечание

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

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

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

1

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

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

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

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

Смежные вопросы