我正在尝试将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);
答案 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()。人眼只是没有这个要求。只有我们的耳朵。