为什么快捷方式调用的命令永远不会在Windows 7下的MFC中崩溃

时间:2012-03-26 08:57:32

标签: windows-7 mfc

当在Windows 7下通过MFC应用程序中的键盘快捷方式调用命令时,即使在执行命令处理程序期间发生了一些非法操作(如零除或访问冲突),应用程序也不会崩溃。但是,通过菜单调用的同一命令将按预期崩溃。

此行为发生在Windows 7下,但不在Windows XP下(我没有要检查的Vista)。如果应用程序是使用Visual Studio 6或Visual Studio 2010编译的,并且MFC是否静态链接,以及它是发布版本还是调试版本,则无关紧要。

这显然是一个主要问题,因为在访问冲突或其他一些问题的情况下,命令会过早停止,可能会使数据处于未定义状态,应用程序就会继续,好像什么都没发生一样。世界上每个MFC应用程序都可能会关注这个问题。

通过为每个键盘快捷方式实现特殊的存根命令处理程序,可以避免此问题。然后,这些存根命令只是通过PostMessage函数将WM_COMMAND放回到消息中。

可以在此处找到演示问题和解决方案的非常简单的存根应用程序的完整Visual Studio 6和2010项目:

http://www.epsitec.ch/download/mfccrash/mfccrash.zip

因此,实际问题是:有人知道发生了什么吗?任何人都可以建议我找到一个更优雅的方法吗?

2 个答案:

答案 0 :(得分:1)

我认为你已经点击kb976038,其中有一个修补程序可用。 当然,您总是可以尝试将您的应用程序设置为64位,但我想在大多数情况下这不是一个真正的选择。

答案 1 :(得分:1)

这实际上是一个KB976038问题。仅关注键盘快捷键调用命令的原因是因为MFC通过:: TranslateAccelerator(m_hWnd,hAccel,pMsg)函数调用它们。此函数有时会进入内核模式(请参阅下面的堆栈转储),然后返回到用户模式,这就存在问题。

为每个键盘快捷方式实现一个特殊的存根命令处理程序然后将WM_COMMAND放回到消息队列中的想法,正如我在问题中提到的那样,绝对不是好事。

为了正确解决问题,我在CMainFrame类中重新实现了OnCommand函数:

BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
   __try
  {    
    if (LOWORD(wParam) != ID_PAGEUP && LOWORD(wParam) != ID_PAGEDOWN)
      GetApp()->DestroyIntellisenseDlg() ;

    return CMDIFrameWnd::OnCommand(wParam, lParam) ;

  }
  __except(RecordExceptionInfo(GetExceptionInformation(), ""))
}

现在,每当其中一个命令发生崩溃时,它就会被RecordExceptionInfo函数捕获。要实现RecordExceptionInfo函数,请查看Hans Dietrich的优秀article on Codeproject

堆叠转储

通过菜单调用的命令的堆栈转储:我们在进入OnAppAbout函数之前不进入内核模式:

testcrash1.exe!Ctestcrash1App::OnAppAbout()  Line 151   C++
testcrash1.exe!_AfxDispatchCmdMsg(CCmdTarget * pTarget, unsigned int nID, int nCode, void (void)* pfn, void * pExtra, unsigned int nSig, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 82    C++
testcrash1.exe!CCmdTarget::OnCmdMsg(unsigned int nID, int nCode, void * pExtra, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 381 + 0x27 bytes   C++
testcrash1.exe!CFrameWnd::OnCmdMsg(unsigned int nID, int nCode, void * pExtra, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 978 + 0x23 bytes    C++
testcrash1.exe!CMainFrame::OnCmdMsg(unsigned int nID, int nCode, void * pExtra, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 198    C++
testcrash1.exe!CWnd::OnCommand(unsigned int wParam, long lParam)  Line 2729 C++
testcrash1.exe!CFrameWnd::OnCommand(unsigned int wParam, long lParam)  Line 371 C++
testcrash1.exe!CFrameWndEx::OnCommand(unsigned int wParam, long lParam)  Line 367 + 0x10 bytes  C++
testcrash1.exe!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult)  Line 2101 + 0x1e bytes   C++
testcrash1.exe!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam)  Line 2087 + 0x20 bytes C++
testcrash1.exe!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam)  Line 257 + 0x1c bytes   C++
testcrash1.exe!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam)  Line 420 C++
user32.dll!_InternalCallWinProc@20()  + 0x23 bytes  
user32.dll!_UserCallWinProcCheckWow@32()  + 0xb7 bytes  
user32.dll!_DispatchMessageWorker@8()  + 0xed bytes 
user32.dll!_DispatchMessageW@4()  + 0xf bytes   
testcrash1.exe!AfxInternalPumpMessage()  Line 183   C++
testcrash1.exe!CWinThread::PumpMessage()  Line 900  C++
testcrash1.exe!CWinThread::Run()  Line 629 + 0xd bytes  C++
testcrash1.exe!CWinApp::Run()  Line 832 C++
testcrash1.exe!AfxWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow)  Line 47 + 0xd bytes C++

通过键盘快捷键调用的命令的堆栈转储。我们在进入OnAppAbout函数之前进入内核模式,注意以NTDLL开头的行:

testcrash1.exe!Ctestcrash1App::OnAppAbout()  Line 151   C++
testcrash1.exe!_AfxDispatchCmdMsg(CCmdTarget * pTarget, unsigned int nID, int nCode, void (void)* pfn, void * pExtra, unsigned int nSig, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 82    C++
testcrash1.exe!CCmdTarget::OnCmdMsg(unsigned int nID, int nCode, void * pExtra, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 381 + 0x27 bytes   C++
testcrash1.exe!CFrameWnd::OnCmdMsg(unsigned int nID, int nCode, void * pExtra, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 978 + 0x23 bytes    C++
testcrash1.exe!CMainFrame::OnCmdMsg(unsigned int nID, int nCode, void * pExtra, AFX_CMDHANDLERINFO * pHandlerInfo)  Line 198    C++
testcrash1.exe!CWnd::OnCommand(unsigned int wParam, long lParam)  Line 2729 C++
testcrash1.exe!CFrameWnd::OnCommand(unsigned int wParam, long lParam)  Line 371 C++
testcrash1.exe!CFrameWndEx::OnCommand(unsigned int wParam, long lParam)  Line 367 + 0x10 bytes  C++
testcrash1.exe!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult)  Line 2101 + 0x1e bytes   C++
testcrash1.exe!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam)  Line 2087 + 0x20 bytes C++
testcrash1.exe!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam)  Line 257 + 0x1c bytes   C++
testcrash1.exe!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam)  Line 420 C++
user32.dll!_InternalCallWinProc@20()  + 0x23 bytes  
user32.dll!_UserCallWinProcCheckWow@32()  + 0xb7 bytes  
user32.dll!_DispatchClientMessage@24()  + 0x51 bytes    
user32.dll!___fnDWORD@4()  + 0x2b bytes 
ntdll.dll!_KiUserCallbackDispatcher@12()  + 0x2e bytes  
user32.dll!_NtUserTranslateAccelerator@12()  + 0x15 bytes   
user32.dll!_TranslateAcceleratorW@12()  + 0x1c464 bytes 
testcrash1.exe!CFrameWnd::PreTranslateMessage(tagMSG * pMsg)  Line 254 + 0x1b bytes C++
testcrash1.exe!CFrameWndEx::PreTranslateMessage(tagMSG * pMsg)  Line 290    C++
testcrash1.exe!CWnd::WalkPreTranslateTree(HWND__ * hWndStop, tagMSG * pMsg)  Line 3311 + 0x14 bytes C++
testcrash1.exe!AfxInternalPreTranslateMessage(tagMSG * pMsg)  Line 233 + 0x12 bytes C++
testcrash1.exe!CWinThread::PreTranslateMessage(tagMSG * pMsg)  Line 777 + 0x9 bytes C++
testcrash1.exe!AfxPreTranslateMessage(tagMSG * pMsg)  Line 252 + 0x11 bytes C++
testcrash1.exe!AfxInternalPumpMessage()  Line 178 + 0x18 bytes  C++
testcrash1.exe!CWinThread::PumpMessage()  Line 900  C++
testcrash1.exe!CWinThread::Run()  Line 629 + 0xd bytes  C++