Link to home
Start Free TrialLog in
Avatar of Evan Li
Evan LiFlag for United States of America

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(nFlags, point);
}

In fact when Class Wizard generate the message handler, it added base function call CDialogBox::OnLButtonUp(nFlags, 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.
Avatar of Zoppo
Zoppo
Flag of Germany image

Hi Evan Li,

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
Avatar of Evan Li

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_lastSentMsg.message,
            pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.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
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.
SOLUTION
Avatar of sarabande
sarabande
Flag of Luxembourg image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Evan Li

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.
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.
Avatar of Evan Li

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.
Avatar of Evan Li

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\ship\atlmfc\src\mfc\wincore.cpp @ 297]      C/C++/ASM

It pointed to MFC code:

      return DefWindowProc(pThreadState->m_lastSentMsg.message,
             pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam);

------------------------------------------------------------------------------Crash Call stack-------------------------------------------------------------------
>      ntdll!NtWaitForMultipleObjects+0xa      C/C++/ASM
       KERNELBASE!WaitForMultipleObjectsEx+0xe8      C/C++/ASM
       kernel32!WaitForMultipleObjectsExImplementation+0xb3      C/C++/ASM
       kernel32!WerpReportFaultInternal+0x215      C/C++/ASM
       kernel32!WerpReportFault+0x77      C/C++/ASM
       kernel32!BasepReportFault+0x1f      C/C++/ASM
       kernel32!UnhandledExceptionFilter+0x1fc      C/C++/ASM
       ntdll! ?? ::FNODOBFM::`string'+0x2025      C/C++/ASM
       ntdll!_C_specific_handler+0x8c      C/C++/ASM
       ntdll!RtlpExecuteHandlerForException+0xd      C/C++/ASM
       ntdll!RtlDispatchException+0x45a      C/C++/ASM
       ntdll!KiUserExceptionDispatch+0x2e      C/C++/ASM
       nxrmc!CWnd::Default+0x68 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 297]      C/C++/ASM
       nxrmc!CWnd::OnLButtonUp+0x8      C/C++/ASM
       nxrmc!CProjMember::OnLButtonUp+0x16 [c:\jenkins\jobs\release_moirai_main\workspace\1642\source\rmc\nxrmc\projmember.cpp @ 174]      C/C++/ASM
       nxrmc!CWnd::OnWndMsg+0x4c0 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 2315]      C/C++/ASM
       nxrmc!CWnd::WindowProc+0x4e [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 2093]      C/C++/ASM
       nxrmc!AfxCallWndProc+0x12d [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 265]      C/C++/ASM
       nxrmc!AfxWndProc+0x54 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 417]      C/C++/ASM
       user32!UserCallWinProcCheckWow+0x1ad      C/C++/ASM
       user32!DispatchMessageWorker+0x3b5      C/C++/ASM
       user32!IsDialogMessageW+0x153      C/C++/ASM
       nxrmc!CWnd::IsDialogMessageW+0x55 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winocc.cpp @ 194]      C/C++/ASM
       nxrmc!CDialog::PreTranslateMessage+0xc3 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dlgcore.cpp @ 80]      C/C++/ASM
       nxrmc!CDialogEx::PreTranslateMessage+0x32 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxdialogex.cpp @ 275]      C/C++/ASM
       nxrmc!CWnd::WalkPreTranslateTree+0x51 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp @ 3373]      C/C++/ASM
       nxrmc!AfxInternalPreTranslateMessage+0x76 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp @ 233]      C/C++/ASM
       nxrmc!AfxPreTranslateMessage+0x3a [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp @ 252]      C/C++/ASM
       nxrmc!AfxInternalPumpMessage+0x3c [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp @ 178]      C/C++/ASM
       nxrmc!CWinThread::Run+0x83 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp @ 629]      C/C++/ASM
       nxrmc!AfxWinMain+0xd6 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winmain.cpp @ 47]      C/C++/ASM
       nxrmc!invoke_main+0x21 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 113]      C/C++/ASM
       nxrmc!__scrt_common_main_seh+0x117 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 253]      C/C++/ASM
       kernel32!BaseThreadInitThunk+0xd      C/C++/ASM
       ntdll!RtlUserThreadStart+0x1d      C/C++/ASM
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Evan Li

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.
Avatar of Evan Li

ASKER

Thank you Zoppo and Sarabande. I appreciate your help.
Avatar of Evan Li

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
Avatar of Evan Li

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.
Avatar of Evan Li

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."
I have changed the SendMessage to PostMessage
generally 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
Avatar of Evan Li

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.
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::OnButtonxxxClicked() 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
Avatar of Evan Li

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