SetWindowLong в CustomDraw вызывает необработанное исключение
Я делаю некоторые изменения в CSliderCtrl, используя Custom Draw, элемент управления должен использоваться в диалоге. Вот структура: в моей 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, так как он никогда не был достигнут.
При работе в режиме отладки происходит сбой в конце метода AfxUnlockGlobals, в afxcrit.cpp. Вот трассировка стека: Обновление: После добавления символов отладки сбой, похоже, выявляется в 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); не совсем элегантно, но это работает.
1 ответ
Ты звонишь SetWindowLong
на неправильном окне. Первый параметр для SetWindowLong
это диалог, который обрабатывает сообщение, а не окно, которое отправило сообщение. SetWindowLong(m_hWnd, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
,
Ваш код меняет окно в окне отправителя, что приводит к повреждению личных данных этого окна.