多媒体计时器在释放模式下工作正常,但在调试模式下不工作

时间:2013-12-01 08:22:47

标签: c debugging timer static release

我正在尝试将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);

2 个答案:

答案 0 :(得分:3)

这里的问题是多媒体计时器API与许多其他API不同,它限制了你在回调中允许做什么。您基本上是not allowed much,您可以更新内部结构,执行一些调试输出以及设置同步事件。

  

说明

     

应用程序不应从内部调用任何系统定义的函数   一个回调函数,除了PostMessage,timeGetSystemTime,   timeGetTime,timeSetEvent,timeKillEvent,midiOutShortMsg,   midiOutLongMsg和OutputDebugString。

断言失败开始显示不允许的消息框,最终可能导致进程崩溃。此外,也不允许使用诸如IsWindow和朋友之类的窗口API,这是导致断言失败的第一个原因。

这里最好的是避免使用多媒体计时器。在大多数情况下,您的限制性替代选项较少。

答案 1 :(得分:1)

只有看起来就像你的代码在Release版本中工作一样,它不会断言()你做得对。你做得不对。

来自多媒体计时器的回调在任意线程池线程上运行。你必须非常小心你在回调中做了什么。首先,您不能直接触摸UI,该代码基本上是线程不安全的。所以你肯定不能调用UpdateData()。充其量,您更新变量并让UI线程知道它需要刷新窗口。使用PostMessage()。通常,您需要一个关键部分,以确保在UI线程使用它来更新窗口时,您的回调不会更新该变量。

你在Debug版本中获得的断言表明更麻烦。看起来你没有确保当用户关闭窗口时计时器不能再回调。这很难干净利落地解决,这是一场基本的线程竞赛。 PostMessage()已经让你摆脱了最大的麻烦。要完全清理,您必须阻止窗口关闭,直到您知道计时器不再回调为止。这需要在获得WM_CLOSE并且不调用DestroyWindow时设置事件。计时器的回调需要检查该事件,调用timeKillEvent()并发布另一条消息。 UI线程现在可以用来真正关闭窗口。

线程很难,确保SetTimer()还不够好,无法完成工作。当然,如果UI更新是唯一的副作用。当您需要一个需要执行与 UI相关的事情的准确计时器时,您只需要timeSetEvent()。人眼只是没有这个要求。只有我们的耳朵。