Evan Li
asked on
MFC:CDialog: Why Do I need to call the base class handler
Hi, dear experts:
I have a stupid question here, I used MFC for a long time, I still don't know why should I can the base class handler even when I provide the function to handle the message, here is an example:
class CMyDialog : CDialog
{....
}
void CMyDialog:OnLButtonUp(UINT nFlags, CPoint point)
{
MyHandler();
CDialogBox::OnLButtonUp(nF lags, point);
}
In fact when Class Wizard generate the message handler, it added base function call CDialogBox::OnLButtonUp(nF lags, point);
Here now here is my question, why do we need to call this function? what if I do not call this function?
Thanks for your help.
I have a stupid question here, I used MFC for a long time, I still don't know why should I can the base class handler even when I provide the function to handle the message, here is an example:
class CMyDialog : CDialog
{....
}
void CMyDialog:OnLButtonUp(UINT
{
MyHandler();
CDialogBox::OnLButtonUp(nF
}
In fact when Class Wizard generate the message handler, it added base function call CDialogBox::OnLButtonUp(nF
Here now here is my question, why do we need to call this function? what if I do not call this function?
Thanks for your help.
ASKER
Thank you Zoppo,
In fact it called cwnd::default
Here is the function definition:
LRESULT CWnd::Default()
{
// call DefWindowProc with the last message
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
return DefWindowProc(pThreadState ->m_lastSe ntMsg.mess age,
pThreadState->m_lastSentMs g.wParam, pThreadState->m_lastSentMs g.lParam);
}
What I observed that when I call this function.
statement:
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
Crashed.
I just wonder if I do not call this function is there any side effect.
Do you know why this statement crash?
Thank you for your help.
Evan
In fact it called cwnd::default
Here is the function definition:
LRESULT CWnd::Default()
{
// call DefWindowProc with the last message
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
return DefWindowProc(pThreadState
pThreadState->m_lastSentMs
}
What I observed that when I call this function.
statement:
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
Crashed.
I just wonder if I do not call this function is there any side effect.
Do you know why this statement crash?
Thank you for your help.
Evan
Yes, that's right, many base-class message handler implementation just call CWnd::Default(), this function calls the window's default message handler to process the current message.
I didn't yet have a case where CWnd::Default crashs. I would suspect there's something messed up with MFC's internal states or it's a multi-threading issue.
Do you probably use the dialog in a thread which is not the main thread? Or do you use multiple modules (DLLs)?
If it crashs in one message handler I think it's a severe problem since you can't know in which situations other message handlers might crash.
I didn't yet have a case where CWnd::Default crashs. I would suspect there's something messed up with MFC's internal states or it's a multi-threading issue.
Do you probably use the dialog in a thread which is not the main thread? Or do you use multiple modules (DLLs)?
If it crashs in one message handler I think it's a severe problem since you can't know in which situations other message handlers might crash.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thank you Zoppo, Sara,
Looks like the crash happens when calling DefWindowProc. If I do win32 API code, I do not call defwindowproc when I handling the message. I do not know why we need to call this function, seems that it is against what Win32 API spec.
Looks like the crash happens when calling DefWindowProc. If I do win32 API code, I do not call defwindowproc when I handling the message. I do not know why we need to call this function, seems that it is against what Win32 API spec.
I guess the crash happens because the pointer pThreadState is invalid so dereferencing its members lead to access violation or something.
First one thing: I promise I implemented many more than 100 WM_LBUTTONUP message handlers (and thousands of other message handlers overall) in MFC applications just using Visual Studio's Tools (resource editor, class wizard) and had not one case where calling the base class member caused any problems (except of course as mentioned in cases where it is intended as i.e. in CListCtrl::DrawItem or CWnd::OnPaint).
Of course sometimes I removed the base class member call, but only in cases where it was intended to override and skip the default behavior completeley.
So IMO there must be something unusual in you program, as mentioned, two common cases which I know may cause troubles with MFC if not handled correctly: using MFC within DLLs or in multiple threads. Or there's something else missing or wrong.
Did you initially create the project as MFC project? If so, is it a dialog application or a SDI/MDI? If it's a dialog application, is the CMyDialog you main dialog? Did you change any code which was generated by App-/ClassWizard?
If you have any other idea about anything what could be unusual/uncommon in your project/code please tell us.
First one thing: I promise I implemented many more than 100 WM_LBUTTONUP message handlers (and thousands of other message handlers overall) in MFC applications just using Visual Studio's Tools (resource editor, class wizard) and had not one case where calling the base class member caused any problems (except of course as mentioned in cases where it is intended as i.e. in CListCtrl::DrawItem or CWnd::OnPaint).
Of course sometimes I removed the base class member call, but only in cases where it was intended to override and skip the default behavior completeley.
So IMO there must be something unusual in you program, as mentioned, two common cases which I know may cause troubles with MFC if not handled correctly: using MFC within DLLs or in multiple threads. Or there's something else missing or wrong.
Did you initially create the project as MFC project? If so, is it a dialog application or a SDI/MDI? If it's a dialog application, is the CMyDialog you main dialog? Did you change any code which was generated by App-/ClassWizard?
If you have any other idea about anything what could be unusual/uncommon in your project/code please tell us.
ASKER
Thank you Zoppo, I know that if I used multiple thread I cam messed up. It is not multi-threading here. This code is inside EXE module. I need to investigate according to what you have told here. Must be something serious problem here. I'll update this question thread when I found the root cause. Thank you for your help.
ASKER
In fact, I could not repro the crash on my machine, I have dump file, and I got following call stack, I could not read this call stack very well. Just wonder if this give you any info.
When I double clicked line:
nxrmc!CWnd::Default+0x68 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win core.cpp @ 297] C/C++/ASM
It pointed to MFC code:
return DefWindowProc(pThreadState ->m_lastSe ntMsg.mess age,
pThreadState->m_lastSentMs g.wParam, pThreadState->m_lastSentMs g.lParam);
-------------------------- ---------- ---------- ---------- ---------- ---------- --Crash Call stack--------------------- ---------- ---------- ---------- ---------- ------
> ntdll!NtWaitForMultipleObj ects+0xa C/C++/ASM
KERNELBASE!WaitForMultiple ObjectsEx+ 0xe8 C/C++/ASM
kernel32!WaitForMultipleOb jectsExImp lementatio n+0xb3 C/C++/ASM
kernel32!WerpReportFaultIn ternal+0x2 15 C/C++/ASM
kernel32!WerpReportFault+0 x77 C/C++/ASM
kernel32!BasepReportFault+ 0x1f C/C++/ASM
kernel32!UnhandledExceptio nFilter+0x 1fc C/C++/ASM
ntdll! ?? ::FNODOBFM::`string'+0x202 5 C/C++/ASM
ntdll!_C_specific_handler+ 0x8c C/C++/ASM
ntdll!RtlpExecuteHandlerFo rException +0xd C/C++/ASM
ntdll!RtlDispatchException +0x45a C/C++/ASM
ntdll!KiUserExceptionDispa tch+0x2e C/C++/ASM
nxrmc!CWnd::Default+0x68 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win core.cpp @ 297] C/C++/ASM
nxrmc!CWnd::OnLButtonUp+0x 8 C/C++/ASM
nxrmc!CProjMember::OnLButt onUp+0x16 [c:\jenkins\jobs\release_m oirai_main \workspace \1642\sour ce\rmc\nxr mc\projmem ber.cpp @ 174] C/C++/ASM
nxrmc!CWnd::OnWndMsg+0x4c0 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win core.cpp @ 2315] C/C++/ASM
nxrmc!CWnd::WindowProc+0x4 e [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win core.cpp @ 2093] C/C++/ASM
nxrmc!AfxCallWndProc+0x12d [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win core.cpp @ 265] C/C++/ASM
nxrmc!AfxWndProc+0x54 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win core.cpp @ 417] C/C++/ASM
user32!UserCallWinProcChec kWow+0x1ad C/C++/ASM
user32!DispatchMessageWork er+0x3b5 C/C++/ASM
user32!IsDialogMessageW+0x 153 C/C++/ASM
nxrmc!CWnd::IsDialogMessag eW+0x55 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win occ.cpp @ 194] C/C++/ASM
nxrmc!CDialog::PreTranslat eMessage+0 xc3 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\dlg core.cpp @ 80] C/C++/ASM
nxrmc!CDialogEx::PreTransl ateMessage +0x32 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\afx dialogex.c pp @ 275] C/C++/ASM
nxrmc!CWnd::WalkPreTransla teTree+0x5 1 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win core.cpp @ 3373] C/C++/ASM
nxrmc!AfxInternalPreTransl ateMessage +0x76 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\thr dcore.cpp @ 233] C/C++/ASM
nxrmc!AfxPreTranslateMessa ge+0x3a [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\thr dcore.cpp @ 252] C/C++/ASM
nxrmc!AfxInternalPumpMessa ge+0x3c [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\thr dcore.cpp @ 178] C/C++/ASM
nxrmc!CWinThread::Run+0x83 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\thr dcore.cpp @ 629] C/C++/ASM
nxrmc!AfxWinMain+0xd6 [f:\dd\vctools\vc7libs\shi p\atlmfc\s rc\mfc\win main.cpp @ 47] C/C++/ASM
nxrmc!invoke_main+0x21 [f:\dd\vctools\crt\vcstart up\src\sta rtup\exe_c ommon.inl @ 113] C/C++/ASM
nxrmc!__scrt_common_main_s eh+0x117 [f:\dd\vctools\crt\vcstart up\src\sta rtup\exe_c ommon.inl @ 253] C/C++/ASM
kernel32!BaseThreadInitThu nk+0xd C/C++/ASM
ntdll!RtlUserThreadStart+0 x1d C/C++/ASM
When I double clicked line:
nxrmc!CWnd::Default+0x68 [f:\dd\vctools\vc7libs\shi
It pointed to MFC code:
return DefWindowProc(pThreadState
pThreadState->m_lastSentMs
--------------------------
> ntdll!NtWaitForMultipleObj
KERNELBASE!WaitForMultiple
kernel32!WaitForMultipleOb
kernel32!WerpReportFaultIn
kernel32!WerpReportFault+0
kernel32!BasepReportFault+
kernel32!UnhandledExceptio
ntdll! ?? ::FNODOBFM::`string'+0x202
ntdll!_C_specific_handler+
ntdll!RtlpExecuteHandlerFo
ntdll!RtlDispatchException
ntdll!KiUserExceptionDispa
nxrmc!CWnd::Default+0x68 [f:\dd\vctools\vc7libs\shi
nxrmc!CWnd::OnLButtonUp+0x
nxrmc!CProjMember::OnLButt
nxrmc!CWnd::OnWndMsg+0x4c0
nxrmc!CWnd::WindowProc+0x4
nxrmc!AfxCallWndProc+0x12d
nxrmc!AfxWndProc+0x54 [f:\dd\vctools\vc7libs\shi
user32!UserCallWinProcChec
user32!DispatchMessageWork
user32!IsDialogMessageW+0x
nxrmc!CWnd::IsDialogMessag
nxrmc!CDialog::PreTranslat
nxrmc!CDialogEx::PreTransl
nxrmc!CWnd::WalkPreTransla
nxrmc!AfxInternalPreTransl
nxrmc!AfxPreTranslateMessa
nxrmc!AfxInternalPumpMessa
nxrmc!CWinThread::Run+0x83
nxrmc!AfxWinMain+0xd6 [f:\dd\vctools\vc7libs\shi
nxrmc!invoke_main+0x21 [f:\dd\vctools\crt\vcstart
nxrmc!__scrt_common_main_s
kernel32!BaseThreadInitThu
ntdll!RtlUserThreadStart+0
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thank you Zoppo. I appreciate your help. You have helped me a lot. Since you have answered my original question, I am closing this now. If I need further help. I'll post another question.
ASKER
Thank you Zoppo and Sarabande. I appreciate your help.
ASKER
Hi Zoppo and Sarabande,
I eventually figured out why the OnLButtonUp crashed.
In my handling function, I have used a SendMessage, in this message handling I destroyed the window that OnLButtonUp resides. I have changed the SendMessage to PostMessage, it will delay the destroy, that makes the it now crash at all. And CWnd::OnLButtonUp works with not crash at all.
Thank you for your help. I should dig this up earlier before, my Bad..
Evan
I eventually figured out why the OnLButtonUp crashed.
In my handling function, I have used a SendMessage, in this message handling I destroyed the window that OnLButtonUp resides. I have changed the SendMessage to PostMessage, it will delay the destroy, that makes the it now crash at all. And CWnd::OnLButtonUp works with not crash at all.
Thank you for your help. I should dig this up earlier before, my Bad..
Evan
ASKER
One thing I can prove is that, from crash dump debugging, I get local variable window and find this pointer, find CWnd::m_hWnd, the value 0. Sorry, I was not getting this out immediately.
ASKER
For the comment I gave at D: 42307117
"that makes the it now crash at all. And CWnd::OnLButtonUp works with not crash at all." has a typo, "now" should be changed to "not" The sentence should be:
"that makes the it not crash at all. And CWnd::OnLButtonUp works with not crash at all."
"that makes the it now crash at all. And CWnd::OnLButtonUp works with not crash at all." has a typo, "now" should be changed to "not" The sentence should be:
"that makes the it not crash at all. And CWnd::OnLButtonUp works with not crash at all."
I have changed the SendMessage to PostMessagegenerally you shouldn't destroy controls of your dialog or formview but instead hide and disable them. you also could move them to outside of the visible area or resize them to zero size. all that is much better than to destroy a control as there might still exist active usage of the control which would fatally fail when accessing it while there is no window associated.
Sara
ASKER
Thank you Sarabande,
I destroyed the dialog, Yes, I am in the process to change it to not to destroy the dialog yet, reuse the dialog with new data display. Thank you for our suggestion.
I destroyed the dialog, Yes, I am in the process to change it to not to destroy the dialog yet, reuse the dialog with new data display. Thank you for our suggestion.
If you want to close the dialog from within OnLButtonDown and if it's a modal dialog (shown with DoModal()) you should try to use EndDialog() instead of DestroyWindow().
OnLButtonUp and OnLButtonDown are low level message handlers which require subclassing of the button control in order to catch the messages or - worse - handling the messages in the PreTranslateMessage function.
if you were using a CDialog derived dialog class (or a view class derived from CFormView) you much easier could handle the BN_CLICKED notification in the YourDialog::OnButtonxxxCli cked() handler. as told by Zoppo you would simply call EndDialog(IDCANCEL); to close your dialog what would orderly Close your modal dialog and return IDCANCEL to the caller of the DoModal() function. to close your formview you would PostMessage(WM_CLOSE, 0, 0);
Sara
if you were using a CDialog derived dialog class (or a view class derived from CFormView) you much easier could handle the BN_CLICKED notification in the YourDialog::OnButtonxxxCli
Sara
ASKER
Thank you Zoppo and Sarabande,
I do have modaless dialog, so destroywindow is correct. I do not have to do that before I exit the button response function. Thank you or your all advices. I displayed many of them in one window as a project window because the data in one project can be clickable.
Evan
I do have modaless dialog, so destroywindow is correct. I do not have to do that before I exit the button response function. Thank you or your all advices. I displayed many of them in one window as a project window because the data in one project can be clickable.
Evan
I think there's no general rule. For some message in some situations it doesn't matter much if you call the base class implementation or not, in other cases it may be needed (i.e. in a CDialog::OnOK the base class implementation closes the dialog, if you don't call it you have to do this yourself using EndDialog) or should be avoided (i.e. message handlers for WM_DRAWITEM in ownerdrawn controls/windows) to call base class implementation.
I guess class wizard just always adds call to base class functions just in order the default functionality of the window's message handler is called, so adding the handler with ClassWizard at first doesn't change the behavior of the window. This often is even the best because ClassWizard doesn't know how the windows-derived class you're working on is used, probably something essential is implemented in CDialogBox::OnLButtonUp, probably not, so adding the base class call is usually better.
Hope that helps,
ZOPPO