Msg routing in modal dialog in DLL
Jesper de Jong -- jesperdj@ati.cmg.nl
Wednesday, August 21, 1996
Environment: Win 3.1, VC++ 1.52
Dear reader,
We are developing a printer driver using MFC (and take my advice: don't
try that at home, MFC really isn't suited for writing drivers, but we're
stuck with it). The driver is actually a DLL.
The driver has a setup dialog which can be called from Control Panel or
via Print Setup in an application. The setup dialog is derived from
CPropertySheet and is put on the screen using CPropertySheet::DoModal().
The first problem was that pressing F1 in the setup dialog didn't work. I
solved this using an ugly hack: in my overridden PreTranslateMessage(), I
do OnHelp() when I get a WM_KEYUP message with wParam == VK_F1.
The setup dialog also contains some buttons which activate a few other
modal dialogs (derived from CDialog, using CDialog::DoModal()). Also in
these dialogs, pressing F1 does not work, but the fix seems to be not as
simple as for the CPropertySheet-derived setup dialog.
The reason why overriding PreTranslateMessage() works for a
CPropertySheet-derived dialog is that CPropertySheet really isn't a dialog
at all. CPropertySheet::DoModal() implements its own message pump loop, in
which PreTranslateMessage() is called etc.; unlike CDialog::DoModal(),
which calls the Windows API function ::MessageBox(). ::MessageBox() does
ofcourse not call PreTranslateMessage().
In a normal MFC application, CDialog should handle pressing F1, I think by
calling OnHelp() in the CWinApp-derived object. The driver does have a
CWinApp-derived object, but because it is a DLL this object isn't used for
message routing (Is this true? At least, none of the handler functions I
define in the CWinApp-derived object ever gets called).
Another strange thing is that when I use the setup dialog from an
application written using MFC, F1-Help does work; it looks like it uses
the CWinApp-object of the application instead of the one from the driver!
Please write me if you have some idea about how to make F1-Help work in
the modal dialog boxes and if my ideas presented above are right or wrong.
Regards, Jesper de Jong
jesper.de.jong@cmg.nl
John Simmons -- jms@connectnet.com
Sunday, August 25, 1996
[Mini-digest: 3 responses]
At 04:57 PM 8/21/96 +0200, you wrote:
>Environment: Win 3.1, VC++ 1.52
>
>Dear reader,
>
>We are developing a printer driver using MFC (and take my advice: don't
>try that at home, MFC really isn't suited for writing drivers, but we're
>stuck with it). The driver is actually a DLL.
>
>The driver has a setup dialog which can be called from Control Panel or
>via Print Setup in an application. The setup dialog is derived from
>CPropertySheet and is put on the screen using CPropertySheet::DoModal().
>
> <>
PretranslateMessage doesn't always work(as you've already found out. I have
attached a text file that illustrates the techniques we use in our dialogs.
Hope it helps in a property sheet.
struct vpCtlInfo
{
UINT Id;
BOOL AcceptsCalc;
BOOL IsCurrency;
double MinValue;
double MaxValue;
};
class CMyDlg : public CDialog
{
// for trapping F1 key to show proper help //
FARPROC lpfnFilterProc;
static HHOOK HookID;
HINSTANCE hInst;
#ifdef WIN32
static DWORD CALLBACK KeystrokeHook(int nCode, WORD wParam, LONG lParam);
#else
static DWORD __export CALLBACK KeystrokeHook(int nCode, WORD wParam, LONG lParam);
#endif
public:
CmyDlg(CWnd* pParent = NULL);
//{{AFX_DATA(CGrowthAdmin)
enum { IDD = DLG_MyDlg };
//}}AFX_DATA
void GetFocusedControl();
protected:
vpCtlInfo m_CurrentCtl; // holds control info for Calculator call
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//{{AFX_MSG(CGrowthAdmin)
afx_msg void OnOKAY();
afx_msg void OnCANCEL();
afx_msg LONG OnKeystroke(UINT, LONG);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
// CMyDlg dialog
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
//{{AFX_MSG_MAP(CMyDlg)
ON_COMMAND(IDOK,OnOKAY)
ON_COMMAND(IDCANCEL,OnCANCEL)
ON_COMMAND(VPHELP,OnVPHELP)
ON_COMMAND(VPNEXT,OnVPNEXT)
ON_COMMAND(VPPREV,OnVPPREV)
ON_MESSAGE(WM_KEYSTROKE,OnKeystroke)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//----------------------------------------------------------------------------/
CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMyDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMyDlg)
m_UseDpGrowth = FALSE;
m_UseGrowthRates = TRUE;
m_UseAdminExpense = TRUE;
//}}AFX_DATA_INIT
}
//----------------------------------------------------------------------------/
void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
VpDataDlg::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyDlg)
//}}AFX_DATA_MAP
}
HHOOK CMyDlg::HookID=0;
//----------------------------------------------------------------------------/
BOOL CMyDlg::OnInitDialog()
{
VpDataDlg::OnInitDialog();
LPDWORD processid = 0;
#ifdef WIN32
HookID = SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC) CMyDlg::KeystrokeHook,
theApp.m_hInstance,
GetWindowThreadProcessId(m_hWnd, processid));
#else
HookID = SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC) CMyDlg::KeystrokeHook,
theApp.m_hInstance, GetWindowTask(m_hWnd));
#endif
return TRUE; // return TRUE unless you set the focus to a control
}
//----------------------------------------------------------------------------/
void CMyDlg::OnOKAY()
{
GetClientData();
if (!ValidInfo())
{
return;
}
#ifdef WIN32
UnhookWindowsHookEx(CMyDlg::HookID);
#else
UnhookWindowsHook(WH_MSGFILTER,(HOOKPROC) CLifeInsDetail::KeystrokeHook);
#endif
EndDialog(IDOK);
}
//----------------------------------------------------------------------------/
void CMyDlg::OnCANCEL()
{
#ifdef WIN32
UnhookWindowsHookEx(CMyDlg::HookID);
#else
UnhookWindowsHook(WH_MSGFILTER,(HOOKPROC) CLifeInsDetail::KeystrokeHook);
#endif
EndDialog(IDCANCEL);
}
//----------------------------------------------------------------------------/
void CMyDlg::OnVPHELP()
{
WinHelp(IDD, HELP_CONTEXT);
GotoDlgCtrl(GetDlgItem(m_CurrentCtl.Id));
}
//----------------------------------------------------------------------------/
void CMyDlg::OnVPNEXT()
{
GetClientData();
if (!ValidInfo())
{
return;
}
#ifdef WIN32
UnhookWindowsHookEx(CMyDlg::HookID);
#else
UnhookWindowsHook(WH_MSGFILTER,(HOOKPROC) CLifeInsDetail::KeystrokeHook);
#endif
EndDialog(VPNEXT);
}
//----------------------------------------------------------------------------/
void CMyDlg::OnVPPREV()
{
GetClientData();
if (!ValidInfo())
{
return;
}
#ifdef WIN32
UnhookWindowsHookEx(CMyDlg::HookID);
#else
UnhookWindowsHook(WH_MSGFILTER,(HOOKPROC) CLifeInsDetail::KeystrokeHook);
#endif
EndDialog(VPPREV);
}
//----------------------------------------------------------------------------//
LONG CMyDlg::OnKeystroke(UINT wParam, LONG lParam)
{
switch (toupper(wParam))
{
case KEY_HELP : OnVPHELP(); return 1L;
case KEY_PGDN : OnVPNEXT(); return 1L;
case KEY_PGUP : OnVPPREV(); return 1L;
case KEY_TAB : GetFocusedControl(); return 0L;
default : break;
}
return 1L;
}
//----------------------------------------------------------------------------//
// This function catches arrowkey movement and tab/shift-tab key strokes.
// The reason is that we had to know if the currently focused control was a
// button. As long as the user clicks on the button wqith the mouse,
// everything is cool, but the minute they use a keystroke to move between
// controls, the dialog doesn't know which control has the focus (if it's a
// button). This was more efficient than enumerating thru the list of
// controls to see which one had the focus.
//----------------------------------------------------------------------------//
void CMyDlg::GetFocusedControl()
{
m_CurrentCtl.Id = GetFocus()->GetDlgCtrlID();
char classname[80];
CString ClassName;
ClassName.Empty();
if (::GetClassName(GetDlgItem(m_CurrentCtl.Id)->m_hWnd, classname, sizeof(classname)))
ClassName = classname;
if (ClassName.Find("Button") >= 0)
m_CurrentCtl.AcceptsCalc = FALSE;
}
//----------------------------------------------------------------------------//
#ifdef WIN32
DWORD CALLBACK CMyDlg::KeystrokeHook(int nCode, WORD wParam, LONG lParam)
#else
DWORD __export CALLBACK CLifeInsDetail::KeystrokeHook(int nCode, WORD wParam, LONG lParam)
#endif
{
if (nCode < 0)
return CallNextHookEx((HHOOK) HookID, nCode, wParam, lParam);
if (nCode == MSGF_DIALOGBOX)
{
MSG FAR *ptrMsg = (MSG FAR *)lParam;
if (WM_KEYDOWN == ptrMsg->message)
{
switch (ptrMsg->wParam)
{
case VK_F1 : ::PostMessage(::GetParent(ptrMsg->hwnd),WM_KEYSTROKE,KEY_HELP,lParam);
return 1L;
case VK_NEXT : ::PostMessage(::GetParent(ptrMsg->hwnd),WM_KEYSTROKE,KEY_PGDN,lParam);
return 1L;
case VK_PRIOR : ::PostMessage(::GetParent(ptrMsg->hwnd),WM_KEYSTROKE,KEY_PGUP,lParam);
return 1L;
case VK_UP :
case VK_DOWN :
case VK_RIGHT :
case VK_LEFT :
case VK_TAB : ::PostMessage(::GetParent(ptrMsg->hwnd),WM_KEYSTROKE,KEY_TAB,lParam);
return 0L;
}
}
}
return 0L;
}
--=====================_840968860==_
Content-Type: text/plain; charset="us-ascii"
/=========================================================\
| John Simmons (Redneck Techno-Biker) |
| jms@connectnet.com |
| Home Page |
| www2.connectnet.com/users/jms/ |
|---------------------------------------------------------|
| ViewPlan, Inc. home page (my employer) |
| www.viewplan.com/index.html |
|---------------------------------------------------------|
| IGN #12 American Eagle Motorsports Zerex Ford |
| Teammates - Steve Stevens (#13 Hooters Ford and |
| 1995 IGN Points Champ) |
| Pat Campbell (#14 Dr. Pepper Ford) |
| American Eagle Motorsports Team Page |
| www2.connectnet.com/users/jms/ignteam |
| Hawaii Multi-Player Nickname: AEMwest |
|---------------------------------------------------------|
| Competitor - Longest Signature File On The Net |
\=========================================================/
-----From: "Rommel Songco"
Hello there!
Like you, the PreTranslateMessage is where I trap F1. To be able to
process F1 in the same way for modal dialogs, I suggest the following:
create your modal dialog box by making a modeless dialog box and then
disable the dialog's parent manually to make the dialog "modal". This way,
you can now override CDialog::PreTranslateMessage and perform your own
processing there.
Regards,
Rommel
rsongco@spectrasoft.com
-----From: Jesper de Jong
On Sun, 25 Aug 1996, Geri Wolters wrote:
> Hoi Jesper,
>
> Ik zou het oplossen met behulp van de WM_COMMANDHELP message (een interne
> AFX message), die gewrapped wordt in
> de CWnd::OnCommandHelp funktie. Kijk op
> http://www.microsoft.com/kb/developr/visual_c/q110506.htm voor een
> voorbeeld.
>
> Ik hoop dat het helpt!
>
> Groet,
>
> Geri Wolters
For the non-Dutch readers: Geri suggests that I should write a handler for
the WM_COMMANDHELP message, which is an internal AFX message, and that
more info can be found in KB article Q110506.
Unfortunately, this does not work. In fact, CDialog itself has a message
map entry for the WM_COMMANDHELP message and an OnCommandHelp handler that
calls WinHelp. When the dialog is in a DLL, the CWinApp of the DLL is not
used in the message routing process. When the application that uses the
DLL is not an MFC application, the dialog will not get a WM_COMMANDHELP
message.
I now solved the problem by the method explained in KB article Q72219,
which is really a very ugly hack: insert a message filter hook function,
and if it encounters an F1 keypress message for the dialog box, send your
own special message to tell the dialog it should run WinHelp...
Regards, Jesper de Jong
jesper.de.jong@cmg.nl
| Вернуться в корень Архива
|