An application can enable the user to choose a help topic with the keyboard by intercepting the F1 key. Intercepting this key lets the user select a menu, menu item, dialog box, message box, or control window and view help for it with a single keystroke.
To intercept the F1 key, the application must install a message-filter procedure by using the SetWindowsHookEx function. This allows the application to examine all keystrokes for the application, regardless of which window has the input focus. If the filter procedure detects the F1 key, it posts a WM_F1DOWN message (application-defined) to the application's main window procedure. The procedure then determines which help topic to display.
The filter procedure should have the following form:
int CALLBACK FilterFunc(nCode, wParam, lParam)
int nCode;
WORD wParam;
DWORD lParam;
{
LPMSG lpmsg = (LPMSG)lParam;
if ((nCode == MSGF_DIALOGBOX || nCode == MSGF_MENU) &&
lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_F1) {
PostMessage(hWnd, WM_F1DOWN, nCode, 0L);
}
DefHookProc(nCode, wParam, lParam, &lpFilterFunc);
return 0;
}
The application should install the filter procedure after creating the main window, as shown in the following example:
lpProcInstance = MakeProcInstance(FilterFunc, hInstance);
if (lpProcInstance == NULL)
return FALSE;
lpFilterFunc = SetWindowsHook(WH_MSGFILTER, lpProcInstance);
The filter procedure sends a WM_F1DOWN message only when the F1 key is pressed in a dialog box, message box, or menu. Many applications also display the Contents topic if no menu, dialog box, or message box is selected when the user presses the F1 key. In this case, the application should define the F1 key as an accelerator key that starts Windows Help.
To create an accelerator key, the application's resource-definition file must define an accelerator table, as follows:
1 ACCELERATORS
BEGIN
VK_F1, IDM_HELP_CONTENTS, VIRTKEY
END
To support the accelerator key, the application must load the accelerator table by using the LoadAccelerators function and translate the accelerator keys in the main message loop by using the TranslateAccelerator function.
In addition to installing the filter procedure, the application must keep track of which menu, menu item, dialog box, or message box is currently selected. In other words, when the user selects an item, the application must set a global variable indicating the current context. For dialog and message boxes, the application should set the global variable immediately before calling the DialogBox or MessageBox function. For menus and menu items, the application should set the variable whenever it receives a WM_MENUSELECT message. As long as identifiers for all menu items and controls in an application are unique, an application can use code similar to the following example to monitor menu selections:
case WM_MENUSELECT:
/*
* Set dwCurrentHelpId to the help ID of the menu item that is
* currently selected.
*/
if (HIWORD(lParam) == 0) /* no menu selected */
dwCurrentHelpId = ID_NONE;
else if (lParam & MF_POPUP) { /* pop-up selected */
if ((HMENU)wParam == hMenuFile)
dwCurrentHelpId = ID_FILE;
else if ((HMENU)wParam == hMenuEdit)
dwCurrentHelpId = ID_EDIT;
else if ((HMENU)wParam == hMenuHelp)
dwCurrentHelpId = ID_HELP;
else
dwCurrentHelpId = ID_SYSTEM;
}
else /* menu item selected */
dwCurrentHelpId = wParam;
break;
In this example, the hMenuFile, hMenuEdit, and hMenuHelp parameters must previously have been set to specify the corresponding menu handles. An application can use the GetMenu and GetSubMenu functions to retrieve these handles.
When the main window procedure finally receives a WM_F1DOWN message, it should use the current value of the global variable to display a help topic. The application can also provide help for individual controls in a dialog box by determining which control has the focus at this point, as shown in the following example:
case WM_F1DOWN:
/*
* If there is a current help context, display it.
*/
if (dwCurrentHelpId != ID_NONE) {
DWORD dwHelp = dwCurrentHelpId;
/*
* Check for context-sensitive help for individual dialog
* box controls.
*/
if (wParam == MSGF_DIALOGBOX) {
WORD wID = GetWindowWord(GetFocus(), GWW_ID);
if (wID != IDOK && wID != IDCANCEL)
dwHelp = (DWORD) wID;
}
WinHelp(hWnd, szHelpFileName, HELP_CONTEXT, dwHelp);
/*
* This call is used to remove the highlighting from the
* System menu, if necessary.
*/
DrawMenuBar(hWnd);
}
break;
When the application ends, it must remove the filter procedure by using the UnhookWindowsHookEx function.