PRB: Modal Dialog Box Prevents Calls to PreTranslateMessage (126874)
The information in this article applies to:
- The Microsoft Foundation Classes (MFC)
This article was previously published under Q126874 NOTE: This article contains references to CWinApp member functions. In the
32-bit version of MFC, the bulk of the code for these functions is actually
implemented in the CWinThread class from which CWinApp is derived.
SYMPTOMS
PreTranslateMessage is not called when a modal dialog box has been invoked.
A modal dialog box has its own message loop, which has no interaction with
the application's main message loop. This prevents CWinApp::PumpMessage
from being called. PumpMessage is the function that calls
PreTranslateMessage.
CAUSE
An application is begun by a call to WinMain. MFC's implementation of
WinMain calls CWinApp::Run, which has the application's main message loop.
The message loop in Run calls PumpMessage to process messages. PumpMessage
retrieves the messages from the application's message queue using the
GetMessage API. PumpMessage then calls CWinApp::PreTranslateMessage, which
will call PreTranslateMessage for the appropriate CWnd. For details on
MFC's message routing mechanism, see MFC TechNote #21 and examine the
CWinApp::PreTranslateMessage function in the MFC source code that can be
found in APPCORE.CPP. In the 32-bit version of MFC, this code is in
CWinThread::PreTranslateMessage and can be found in THRDCORE.CPP. Here is a
picture of this process:
-------------------------------------
| CWinApp::PumpMessage |
-------------------------------------
|
|
-------------------------------------
| CWinApp::PreTranslateMessage |
-------------------------------------
|
|
-------------------------------------
| CWnd:: PreTranslateMessage |
-------------------------------------
When a modal dialog box has been invoked, the above sequence is no longer
used. A modal dialog box uses the Dialog Manager (the code built into
Windows for implementing dialog boxes) to retrieve messages from the
application's message queue and process them. In other words, the Dialog
Manager takes control of all message processing during the existence of a
modal dialog box. Here's a picture of this process:
-------------------------------------
| CWinApp::PumpMessage |
-------------------------------------
|
|
-------------------------------------
| CDialog::DoModal |
-------------------------------------
|
|
-------------------------------------
| Dialog Manager's Message Loop |
-------------------------------------
The PumpMessage has dispatched the message that invoked the dialog box in
the first place. PumpMessage will not be called again until the Dialog
Manager exits its message loop -- when the modal dialog box has been
dismissed.
However, a modeless dialog box uses the normal message processing sequence
because it uses the application's message loop, not the Dialog Manager's
message loop.
RESOLUTION
PreTranslateMessage is generally overidden to check for certain messages
and do some specialized or additional processing before these messages are
translated and dispatched. Because PreTranslateMessage will not be called
while a modal dialog box exists, another technique must be used to achieve
this processing.
The additional message processing can be achieved by using one of the
following two methods: - Use a modeless dialog box to simulate a modal dialog box. To do this,
create a modeless dialog box, and then disable the application's main
frame window. This method causes the normal message processing sequence
to be followed, so the PreTranslateMessage function is called. The
application's main window can be disabled by using this:
AfxGetApp()->m_pMainWnd->EnableWindow(FALSE)
This window should be re-enabled once the dialog box has been destroyed.
Use the following call to do it:
AfxGetApp()->m_pMainWnd->EnableWindow(TRUE)
One possible place to do this might be in the CDialog-derived object's
PostNcDestroy function that gets called when the dialog box window
itself (the HWND) is destroyed.
If you have multiple top-level windows in your application you may also
want to disable the other windows. This can be done for the other
windows in the same way -- by using EnableWindow. - If a modal dialog box seems necessary, perform the additional message
processing by overiding the CWinApp::ProcessMessageFilter function. The
ProcessMessageFilter function gets called for all messages when a modal
dialog box exists. This is set up for you by MFC using a WH_MSGFILTER
hook. See the ProcessMessageFilter documentation for details. The
following sample code illustrates this concept:
BOOL CMyApp::ProcessMessageFilter(int code, LPMSG lpMsg)
{
// Check to see if the modal dialog box is up
if (m_hwndDialog != NULL)
if (lpMsg->hwnd == m_hwndDialog ||
::IsChild(m_hwndDialog, lpMsg->hwnd))
{
// Use the global IsChild() function to get
// messages destined for the dialog's controls
// Perform customized message processing here
}
return CWinApp::ProcessMessageFilter(code, lpMsg);
}
In the above sample code m_hwndDialog is a member variable of the CWinApp
derived class that holds the handle of the modal dialog box. This variable
should be initialized to m_hWnd of the modal dialog in the OnInitDialog
function, and should be set to NULL when the modal dialog is destroyed.
Also the variable should be initialized in the CWinApp-derived object's
constructor. For example:
// The OnInitDialog to initialize m_hwndDialog
//
CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
((CMyApp *)AfxGetApp())->m_hwndDialog=m_hWnd;
}
// When the dialog is destroyed restore m_hwnDialog to NULL
//
void CMyDialog::PostNcDestroy()
{
CDialog::PostNcDestroy();
((CMyApp *)AfxGetApp())->m_hwndDialog=NULL;
}
// The CWinApp object's constructor to initialize m_hwndDialog
CMyApp::CMyApp()
{
m_hwndDialog=NULL;
}
STATUS
This behavior is by design.
REFERENCES
Books Online: MFC TechNote #21.
"Meandering Through the Maze of MFC Message and Command Routing" by
Paul DiLascia in the July 1995 issue of "Microsoft Systems Journal"
(Volume 10, Number 7).
Modification Type: | Major | Last Reviewed: | 7/25/2001 |
---|
Keywords: | kbcode kbprb KbUIDesign KB126874 |
---|
|