CustomDraw 中的 SetWindowLong 导致未处理的异常

Posted

技术标签:

【中文标题】CustomDraw 中的 SetWindowLong 导致未处理的异常【英文标题】:SetWindowLong in CustomDraw causes unhandled exception 【发布时间】:2010-04-13 07:31:58 【问题描述】:

我正在使用自定义绘图对 CSliderCtrl 进行一些更改,该控件将在对话框中使用。这是结构: 在我的 MessageMap 中,我有:ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW, OnNMCustomdraw)

OnNMCustomdraw 方法如下所示:

BOOL CCustomSliderCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)

    *pResult = CDRF_DODEFAULT;
    LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);

    switch(pNMCD->dwDrawStage)
    
        case CDDS_PREPAINT:
        
            //Dialogs don't receive CDRF_NOTIFYITEMDRAW notifcations by returning it as part of pResult, we must
            //use the following so we ensure we receive the msg
            SetWindowLong(pNMHDR->hwndFrom, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
            return TRUE;
        
        case CDDS_ITEMPREPAINT:
            if(pNMCD->dwItemSpec == TBCD_CHANNEL)
            
                ...SNIP...
                SetWindowLong(pNMHDR->hwndFrom, DWL_MSGRESULT, CDRF_SKIPDEFAULT);
                return TRUE;
            
    
    return FALSE;

阅读后我了解到您必须使用 SetWindowLong 来设置自定义绘制的返回值,否则您的方法将不会总是收到 CDDS_ITEMPREPAINT 消息。但是,当使用 SetWindowLong 时,我的应用程序将永远不会收到 CDDS_ITEMPREPAINT,因此我的滑块看起来就像一个标准滑块。当滑块上发生任何类型的交互时,应用程序会崩溃,例如将鼠标悬停在滑块上或最小化和最大化对话框。

我剪断了 TBCD_CHANNEL 代码,因为它从未到达过。

在调试模式下运行时,它会在 afxcrit.cpp 中的 AfxUnlockGlobals 方法结束时崩溃。这是一个堆栈跟踪: 更新:由于添加了调试符号,崩溃似乎是在 CWnd::DefWindowProc mwthod 处发生的。

comctl32.dll!_TrackBarWndProc@16()  + 0x551 bytes   
user32.dll!_InternalCallWinProc@20()  + 0x28 bytes  
user32.dll!_UserCallWinProcCheckWow@32()  + 0xb7 bytes  
user32.dll!_CallWindowProcAorW@24()  + 0x51 bytes   
user32.dll!_CallWindowProcW@20()  + 0x1b bytes  
mfc90ud.dll!CWnd::DefWindowProcW(unsigned int nMsg=15, unsigned int wParam=0, long lParam=0)  Line 1043 + 0x20 bytes    C++
mfc90ud.dll!CWnd::WindowProc(unsigned int message=15, unsigned int wParam=0, long lParam=0)  Line 1756 + 0x1c bytes C++
mfc90ud.dll!AfxCallWndProc(CWnd * pWnd=0x0012fdbc, HWND__ * hWnd=0x000308fe, unsigned int nMsg=15, unsigned int wParam=0, long lParam=0)  Line 240 + 0x1c bytes C++
mfc90ud.dll!AfxWndProc(HWND__ * hWnd=0x000308fe, unsigned int nMsg=15, unsigned int wParam=0, long lParam=0)  Line 403  C++
mfc90ud.dll!AfxWndProcBase(HWND__ * hWnd=0x000308fe, unsigned int nMsg=15, unsigned int wParam=0, long lParam=0)  Line 441 + 0x15 bytes C++
user32.dll!_InternalCallWinProc@20()  + 0x28 bytes  
user32.dll!_UserCallWinProcCheckWow@32()  + 0xb7 bytes  
user32.dll!_DispatchClientMessage@20()  + 0x4d bytes    
user32.dll!___fnDWORD@4()  + 0x24 bytes 
ntdll.dll!_KiUserCallbackDispatcher@12()  + 0x13 bytes  
user32.dll!_NtUserDispatchMessage@4()  + 0xc bytes  
user32.dll!_DispatchMessageW@4()  + 0xf bytes   
mfc90ud.dll!AfxInternalPumpMessage()  Line 183  C++
mfc90ud.dll!CWinThread::PumpMessage()  Line 900 C++
mfc90ud.dll!AfxPumpMessage()  Line 190 + 0xd bytes  C++
mfc90ud.dll!CWnd::RunModalLoop(unsigned long dwFlags=4)  Line 4386 + 0x5 bytes  C++
mfc90ud.dll!CDialog::DoModal()  Line 584 + 0xc bytes    C++
SetSelection.exe!CSetSelectionApp::InitInstance()  Line 64 + 0xb bytes  C++
mfc90ud.dll!AfxWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x00020a84, int nCmdShow=1)  Line 37 + 0xd bytes C++
SetSelection.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x00020a84, int nCmdShow=1)  Line 34  C++
SetSelection.exe!__tmainCRTStartup()  Line 578 + 0x35 bytes C
SetSelection.exe!wWinMainCRTStartup()  Line 403 C
kernel32.dll!_BaseProcessStart@4()  + 0x23 bytes

那么,有人对这件事有任何见解吗?如果您需要更多信息,请告诉我。


更新:我现在找到了一个解决方法,而不是使用 SetWindowLong,我只是将结果分配给 pResult,然后返回。我通过调用 SetRangeMin(GetRangeMin(), TRUE); 强制重新绘制确实重新绘制子项目;虽然不完全优雅,但它可以工作。

【问题讨论】:

提示:要获得更好的堆栈跟踪,请配置symbol server 谢谢,我现在就玩这个。 添加了更好的堆栈跟踪,感谢您的提示。 【参考方案1】:

您在错误的窗口上调用SetWindowLongSetWindowLong 的第一个参数是处理消息的对话框,而不是发送消息的窗口。 SetWindowLong(m_hWnd, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);.

您的代码在发送方窗口上长时间更改窗口,最终破坏了该窗口的私有数据。

【讨论】:

以上是关于CustomDraw 中的 SetWindowLong 导致未处理的异常的主要内容,如果未能解决你的问题,请参考以下文章

带有 NM_CUSTOMDRAW 的 Listview 项目闪烁

Clistctrl 项目文本颜色

ClistCtrl 设置项目的颜色

CTreeCtrl 自定义绘制附加状态

VC控件自绘制三步曲

使用 CListCtrl,如何使选择颜色整行?