我在 Visual Studio 中使用 C++ MFC 的应用程序有一个堆栈跟踪,该应用程序已崩溃。
我打开了调试器,并带有堆栈跟踪,但我无法判断原因是什么。除了调用显示模式对话框的入口点之外,所有堆栈帧都是外部的。
我怎样才能找到这个问题的原因,或者调试这个问题? (为什么堆栈不是我代码的,而是全部外部的?)
这是堆栈frame中唯一与我的代码相关的调用。
INT_PTR nResponse = dlg->DoModal();
错误断点在这里:
ASSERT(ph[1] == h);
#ifdef _DEBUG
void CHandleMap::RemoveHandle(HANDLE h)
{
// make sure the handle entry is consistent before deleting
CObject* pTemp = LookupTemporary(h);
if (pTemp != NULL)
{
// temporary objects must have correct handle values
HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset); // after CObject
ASSERT(ph[0] == h || ph[0] == NULL);
if (m_nHandles == 2)
ASSERT(ph[1] == h); <------
}
pTemp = LookupPermanent(h);
if (pTemp != NULL)
{
HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset); // after CObject
ASSERT(ph[0] == h);
// permanent object may have secondary handles that are different
}
// remove only from permanent map -- temporary objects are removed
// at idle in CHandleMap::DeleteTemp, always!
m_permanentMap.RemoveKey((LPVOID)h);
}
#endif
从堆栈跟踪中我可以看到 CDC 正在开始分离,在此之前会调用
CStatusBar
OnNCPaint
。我还使用调试器检查了 WindowProc 消息值,它是 133。我查了一下 133 是什么,它与 0085 133 WM_NCPAINT
相关。除了 CDC 可能被释放之外,一切似乎都无关紧要。
我怀疑当应用程序最小化时会发生这种情况 - 一个疯狂的猜测,因为我认为这就是我在它崩溃时所做的 - 也很难重现。
这是我的状态栏代码,可能适用,请注意它是从 OnTimer 调用的。
void MainDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == nTimerStatusbarUpdate)
{
if (statusBarHelper == nullptr) return;
// snip...
statusBarHelper->UpdateRightPanelWidthAndText(text);
}
}
// the helper class method
void StatusBarHelper::UpdateRightPanelWidthAndText(const CString& text)
{
if (statusbar_mutex.try_lock_for(std::chrono::seconds(1))) {
CDC* pDC = statusBar.GetDC();
If (pDC == nullptr)
{
// Failed to get device context
statusbar_mutex.unlock();
return;
}
// snip....
// Release the device context and restore the original font
pDC->SelectObject(pOldFont);
statusBar.ReleaseDC(pDC);
statusbar_mutex.unlock();
}
}
正如 @IInspectable 指出的,它不是堆栈跟踪而是断言,它是 GDI 垃圾收集的一部分。
这是由调用分离 CDC 引起的,而一开始就不应该调用它。
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
// snip
pDC->Detach(); <-- the error
CDC::FromHandle
返回指向 CDC 的指针,该指针不受您的控制。
在我的例子中,被引用的 CDC 是在 DrawItem 方法中。
Clazz::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { ... }
来自 Microsoft 文档:
CDC::DeleteTempMap 由 CWinApp 空闲时间处理程序调用,以删除由 FromHandle 创建的任何临时 CDC 对象。还分离设备上下文。