CustomDraw 中的 SetWindowLong 导致未处理的异常
我正在使用自定义绘图对 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); 重新绘制子项,虽然不太优雅,但它有效。
I am making some changes to a CSliderCtrl using the Custom Draw, the control is to be used in a dialog. Here is the structure:
In my MessageMap I have : ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW, OnNMCustomdraw)
The OnNMCustomdraw method looks like the following:
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;
}
Reading around I learnt that you had to use SetWindowLong to set the return value for the custom draw, otherwise your method will not always receive the CDDS_ITEMPREPAINT message. However, when using SetWindowLong my application will never receive the CDDS_ITEMPREPAINT and so my slider just looks like a standard slider. The application crashes when any sort of interaction takes place upon the slider, such as hovering over it or minimizing and maximizing the dialog.
I snipped the TBCD_CHANNEL code as it is never reached.
When running in debug mode, it crashes at the end of the AfxUnlockGlobals method, in afxcrit.cpp. Here is a stack trace:
Update: Since adding debug symbols, the crash seems to be picked up at 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
So, does anyone have any insight into this matter? If you need more info just let me know.
Update: I have found a work around for now, instead of using SetWindowLong I just assign the result to pResult, then return. I force a re-draw that does repaint the sub items by calling SetRangeMin(GetRangeMin(), TRUE);, not exactly elegant but it works.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您在错误的窗口上调用
SetWindowLong
。SetWindowLong
的第一个参数是处理消息的对话框,而不是发送消息的窗口。SetWindowLong(m_hWnd, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
。您的代码正在更改发送者窗口上的窗口,这最终会破坏该窗口的私有数据。
You are calling
SetWindowLong
on the wrong window. The first parameter toSetWindowLong
is the dialog that is handling the message, not the window that sent the message.SetWindowLong(m_hWnd, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
.Your code is changing the window long on the sender window, which ends up corrupting that window's private data.