/**************************************************************************** PROGRAM: HelpEx.c PURPOSE: Illustrates calls to WinHelp and context-sensitive help. HelpEx loads library MenuHook, which detects F1 keystrokes in the HelpEx application menus. FUNCTIONS: WinMain() - Calls initialization function, processes message loop. InitApplication() - Initializes window data and registers window class. InitInstance() - Saves instance handle and creates main window. MainWndProc() - Processes window messages. About() - Processes messages for "About" dialog box. MakeHelpPathName() - Derives path name of help file. ****************************************************************************/ #include #include "helpex.h" #include "helpids.h" HWND hWnd; /* Handle to main window */ HANDLE hInst; /* Handle to instance data*/ FARPROC lpFilterFunc; BOOL bHelp = FALSE; /* Help mode flag; TRUE = "ON"*/ DWORD dwCurrentHelpId = HELPID_NONE; /* Current help context for menus and dialogs */ HCURSOR hHelpCursor; /* Cursor displayed when in help mode*/ char szHelpFileName[EXE_NAME_MAX_SIZE+1]; /* Help file name*/ HANDLE hAccTable; /* handle to accelerator table */ HMENU hMenuFile, hMenuEdit, hMenuHelp; void MakeHelpPathName(char*); /* Function deriving help file path */ int FAR PASCAL FilterFunc(int, WORD, DWORD); /* Filter function to detect F1*/ BOOL MyWinHelp(HWND, LPSTR, WORD, DWORD); void StartDialog(FARPROC, WORD); void DisplayError(WORD); /**************************************************************************** FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) PURPOSE: Calls initialization function, processes message loop. ****************************************************************************/ int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow) HANDLE hInstance; HANDLE hPrevInstance; LPSTR lpCmdLine; int nCmdShow; { MSG msg; if (!hPrevInstance) if (!InitApplication(hInstance)) return (FALSE); if (!InitInstance(hInstance, nCmdShow)) return (FALSE); while (GetMessage(&msg, NULL, NULL, NULL)) { /* Only translate message if it is not an accelerator message */ if (!TranslateAccelerator(hWnd, hAccTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (msg.wParam); } /**************************************************************************** FUNCTION: InitApplication(HANDLE) PURPOSE: Initializes window data and registers window class. RETURNS: Status of RegisterClass() call. ****************************************************************************/ BOOL InitApplication(hInstance) HANDLE hInstance; { WNDCLASS wc; wc.style = NULL; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = COLOR_WINDOW+1; wc.lpszMenuName ="HelpexMenu"; wc.lpszClassName = "HelpexWClass"; return (RegisterClass(&wc)); } /**************************************************************************** FUNCTION: InitInstance(HANDLE, int) PURPOSE: Saves instance handle in global variable and creates main window. RETURNS: Status of CreateWindow() call. ****************************************************************************/ BOOL InitInstance(hInstance, nCmdShow) HANDLE hInstance; int nCmdShow; { FARPROC lpProcInstance; HMENU hMenu; hInst = hInstance; hAccTable = LoadAccelerators(hInst, "HelpexAcc"); hWnd = CreateWindow( "HelpexWClass", "Help Example ", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); if (!hWnd) return (FALSE); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); EnableMenuItem(GetSubMenu(GetMenu(hWnd), 1), IDM_CLEAR, MF_ENABLED); MakeHelpPathName(szHelpFileName); hHelpCursor = LoadCursor(hInst,"HelpCursor"); lpProcInstance = MakeProcInstance(FilterFunc,hInstance); if (lpProcInstance == NULL) return (FALSE); lpFilterFunc = SetWindowsHook(WH_MSGFILTER,lpProcInstance); /* * Store menu handles in global variables so that we can compare * them for context sensitive help later. */ hMenu = GetMenu(hWnd); hMenuFile = GetSubMenu(hMenu, 0); hMenuEdit = GetSubMenu(hMenu, 1); hMenuHelp = GetSubMenu(hMenu, 2); return (TRUE); } /**************************************************************************** FUNCTION: FilterFunc(int, WORD, DWORD) PURPOSE: Checks for the F1 key being pressed while a menu or dialog box is displayed. MESSAGES: WM_F1DOWN- User defined message posted to main window to indicate F1 has been pressed. ****************************************************************************/ int FAR PASCAL 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 (FALSE); } /**************************************************************************** FUNCTION: MyWinHelp(HWND, LPSTR, WORD, DWORD) PURPOSE: This function may be used instead of WinHelp to make HELP_SETCONTENTS work better. By using MyWinHelp() instead of WinHelp() everywhere, you may send HELP_SETCONTENTS to set the contents and not bring up Help, and use any WinHelp API (including HELP_CONTENTS) without losing the contents that got set. ****************************************************************************/ BOOL MyWinHelp(hwnd, lpHelpfile, wCommand, dwData) HWND hwnd; LPSTR lpHelpfile; WORD wCommand; DWORD dwData; { static DWORD ctxContents = (DWORD)-1L; if (wCommand == HELP_SETCONTENTS) { ctxContents = dwData; return (TRUE); } if (wCommand == HELP_CONTENTS && ctxContents != (DWORD)-1L) { WinHelp(hwnd, lpHelpfile, HELP_CONTEXT, ctxContents); } else { WinHelp(hwnd, lpHelpfile, wCommand, dwData); } if (wCommand != HELP_QUIT && ctxContents != (DWORD)-1L) { WinHelp (hwnd, lpHelpfile, HELP_SETCONTENTS, dwData); } } /**************************************************************************** FUNCTION: StartDialog(WORD) PURPOSE: This function is used to start a dialog with context sensitive help. It assumes that the dialog resource id is the same as the number that has been mapped to the help context. ****************************************************************************/ void StartDialog(lpDialogProc, wDlgId) FARPROC lpDialogProc; WORD wDlgId; { FARPROC lpInstanceProc; dwCurrentHelpId = (DWORD) wDlgId; lpInstanceProc = MakeProcInstance(lpDialogProc, hInst); DialogBox(hInst, MAKEINTRESOURCE(wDlgId), hWnd, lpInstanceProc); FreeProcInstance(lpInstanceProc); dwCurrentHelpId = HELPID_NONE; } /**************************************************************************** FUNCTION: DisplayError(WORD) PURPOSE: This function is used to display an error message with context sensitive help. It assumes that the error string resource id is the same as the number that has been mapped to the help context. ****************************************************************************/ void DisplayError(wErrId) WORD wErrId; { char rgchBuf[ERRORSTRING_MAX_SIZE]; dwCurrentHelpId = (DWORD) wErrId; LoadString(hInst, wErrId, rgchBuf, ERRORSTRING_MAX_SIZE); MessageBox(hWnd, rgchBuf, "Help Example", MB_OK); dwCurrentHelpId = HELPID_NONE; } /**************************************************************************** FUNCTION: MainWndProc(HWND, UINT, WPARAM, LPARAM) PURPOSE: Processes window messages. MESSAGES: WM_COMMAND- Application menu item WM_DESTROY- Destroy window ****************************************************************************/ long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam) HWND hWnd; UINT message; WPARAM wParam; LPARAM lParam; { switch (message) { case WM_SYSCOMMAND: /* Are we in help mode (Shift-F1)? */ if (bHelp) { dwCurrentHelpId = (wParam == SC_RESTORE) ? (DWORD) HELPID_SYSRESTORE : (wParam == SC_MOVE) ? (DWORD) HELPID_SYSMOVE : (wParam == SC_SIZE) ? (DWORD) HELPID_SYSSIZE : (wParam == SC_MINIMIZE) ? (DWORD) HELPID_SYSMINIMIZE : (wParam == SC_MAXIMIZE) ? (DWORD) HELPID_SYSMAXIMIZE : (wParam == SC_CLOSE) ? (DWORD) HELPID_SYSCLOSE : (wParam == SC_TASKLIST) ? (DWORD) HELPID_SYSSWITCH : (DWORD) HELPID_NONE; if (dwCurrentHelpId == HELPID_NONE) return (DefWindowProc(hWnd, message, wParam, lParam)); bHelp = FALSE; WinHelp(hWnd,szHelpFileName,HELP_CONTEXT,dwCurrentHelpId); dwCurrentHelpId = HELPID_NONE; break; } return (DefWindowProc(hWnd, message, wParam, lParam)); case WM_COMMAND: /* Are we in help mode (Shift-F1)? */ if (bHelp) { bHelp = FALSE; WinHelp(hWnd,szHelpFileName,HELP_CONTEXT, (DWORD)wParam); return NULL; } switch (wParam) { case IDM_NEW: case IDM_OPEN: case IDM_SAVE: case IDM_SAVEAS: case IDM_PRINT: case IDM_UNDO: case IDM_CUT: case IDM_CLEAR: case IDM_COPY: case IDM_PASTE: DisplayError(ERR_NOT_YET_IMPLEMENTED); break; case IDM_EXIT: DestroyWindow(hWnd); break; case IDM_HELP_CONTENTS: WinHelp(hWnd,szHelpFileName,HELP_CONTENTS,0L); break; case IDM_HELP_KEYBOARD: WinHelp(hWnd, szHelpFileName, HELP_COMMAND, (DWORD)(LPSTR)"JumpID(`myhelp.hlp',`IDM_HELP_KEYBOARD')"); break; case IDM_HELP_SEARCH: WinHelp(hWnd,szHelpFileName,HELP_PARTIALKEY,(DWORD)(LPSTR)""); break; case IDM_HELP_HELP: WinHelp(hWnd,szHelpFileName,HELP_HELPONHELP,0L); break; case IDM_ABOUT: StartDialog(About, ABOUTBOX); break; case IDM_HELP_MULTIKEY: StartDialog(Multikey, EDITMULTIKEY); break; default: return (DefWindowProc(hWnd, message, wParam, lParam)); } break; case WM_LBUTTONDOWN: if (bHelp) { bHelp = FALSE; WinHelp( hWnd, szHelpFileName, HELP_CONTEXT, (DWORD) HELPID_EDIT_WINDOW ); break; } return (DefWindowProc(hWnd, message, wParam, lParam)); case WM_NCLBUTTONDOWN: /* If we are in help mode (Shift-F1) then display context- */ /* sensitive help for non-client area. */ if (bHelp) { dwCurrentHelpId = (wParam == HTCAPTION) ? (DWORD) HELPID_TITLE_BAR : (wParam == HTREDUCE) ? (DWORD) HELPID_MINIMIZE_ICON : (wParam == HTZOOM) ? (DWORD) HELPID_MAXIMIZE_ICON : (wParam == HTBOTTOM) ? (DWORD) HELPID_SIZING_BORDER : (wParam == HTBOTTOMLEFT) ? (DWORD) HELPID_SIZING_BORDER : (wParam == HTBOTTOMRIGHT) ? (DWORD) HELPID_SIZING_BORDER : (wParam == HTTOP) ? (DWORD) HELPID_SIZING_BORDER : (wParam == HTLEFT) ? (DWORD) HELPID_SIZING_BORDER : (wParam == HTRIGHT) ? (DWORD) HELPID_SIZING_BORDER : (wParam == HTTOPLEFT) ? (DWORD) HELPID_SIZING_BORDER : (wParam == HTTOPRIGHT) ? (DWORD) HELPID_SIZING_BORDER : (DWORD) HELPID_NONE; if (dwCurrentHelpId == HELPID_NONE) return (DefWindowProc(hWnd, message, wParam, lParam)); bHelp = FALSE; WinHelp(hWnd,szHelpFileName,HELP_CONTEXT,dwCurrentHelpId); dwCurrentHelpId = HELPID_NONE; break; } return (DefWindowProc(hWnd, message, wParam, lParam)); case WM_KEYDOWN: if (wParam == VK_F1) { /* If Shift-F1, turn help mode on and set help cursor */ if (GetKeyState(VK_SHIFT)<0) { bHelp = TRUE; SetCursor(hHelpCursor); return (DefWindowProc(hWnd, message, wParam, lParam)); } /* If F1 without shift, then call up help main index topic */ else { WinHelp(hWnd,szHelpFileName,HELP_CONTENTS,0L); } } else if (wParam == VK_ESCAPE && bHelp) { /* Escape during help mode: turn help mode off */ bHelp = FALSE; SetCursor((HCURSOR)GetClassWord(hWnd,GCW_HCURSOR)); } break; 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 = HELPID_NONE; else if (lParam & MF_POPUP) { /* pop-up selected */ if ((HMENU)wParam == hMenuFile) dwCurrentHelpId = HELPID_FILE; else if ((HMENU)wParam == hMenuEdit) dwCurrentHelpId = HELPID_EDIT; else if ((HMENU)wParam == hMenuHelp) dwCurrentHelpId = HELPID_HELP; else dwCurrentHelpId = HELPID_SYSTEM; } else /* menu item selected */ dwCurrentHelpId = wParam; break; case WM_F1DOWN: /* If there is a current help context, then */ /* display it */ if (dwCurrentHelpId != HELPID_NONE) { DWORD dwHelp = dwCurrentHelpId; /******************************************* To provide context sensitive help for individual controls in your dialog boxes, include the following code: 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; case WM_SETCURSOR: /* * In help mode it is necessary to reset the cursor in response * to every WM_SETCURSOR message.Otherwise, by default, Windows * will reset the cursor to that of the window class. */ if (bHelp) { SetCursor(hHelpCursor); break; } return (DefWindowProc(hWnd, message, wParam, lParam)); break; case WM_INITMENU: if (bHelp) { SetCursor(hHelpCursor); } return (TRUE); case WM_DESTROY: WinHelp(hWnd,szHelpFileName,HELP_QUIT,0L); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, message, wParam, lParam)); } return (NULL); } /**************************************************************************** FUNCTION: About(HWND, UINT, WPARAM, LPARAM) PURPOSE: Processes messages for "About" dialog box MESSAGES: WM_INITDIALOG - Initialize dialog box WM_COMMAND- Input received ****************************************************************************/ BOOL FAR PASCAL About(hDlg, message, wParam, lParam) HWND hDlg; UINT message; WPARAM wParam; LPARAM lParam; { switch (message) { case WM_INITDIALOG: return (TRUE); case WM_COMMAND: if (wParam == IDOK) { EndDialog(hDlg, TRUE); return (TRUE); } break; } return (FALSE); } /**************************************************************************** FUNCTION: Multikey(HWND, UINT, WPARAM, LPARAM) PURPOSE: Processes messages for "Keyword" dialog box. Sends HELP_MULTIKEY commands with the keyword entered in the edit box. MESSAGES: WM_INITDIALOG - Initialize dialog box WM_COMMAND- Input received ****************************************************************************/ BOOL FAR PASCAL Multikey(hDlg, message, wParam, lParam) HWND hDlg; UINT message; WPARAM wParam; LPARAM lParam; { char rgchBuf[sizeof(MULTIKEYHELP) + MULTIKEY_MAX_SIZE]; MULTIKEYHELP FAR * lpMultikey; switch (message) { case WM_INITDIALOG: SendDlgItemMessage(hDlg, IDEDITMULTIKEY, WM_SETTEXT, 0, (DWORD)(LPSTR)"Multikey"); return (TRUE); case WM_COMMAND: switch (wParam) { case IDOK: lpMultikey = (MULTIKEYHELP FAR *) rgchBuf; lpMultikey->mkKeylist = 'M'; SendDlgItemMessage(hDlg, IDEDITMULTIKEY, WM_GETTEXT, MULTIKEY_MAX_SIZE, (DWORD)(LPSTR)lpMultikey->szKeyphrase); lpMultikey->mkSize = sizeof(MULTIKEYHELP) + lstrlen(lpMultikey->szKeyphrase); WinHelp(hWnd, szHelpFileName, HELP_MULTIKEY, (DWORD)lpMultikey); /* Fall through: */ case IDCANCEL: EndDialog(hDlg, TRUE); return (TRUE); } } return (FALSE); } /**************************************************************************** FUNCTION: MakeHelpPathName PURPOSE: HelpEx assumes that the .HLP help file is in the same directory as the HelpEx executable.This function derives the full path name of the help file from the path of the executable. ****************************************************************************/ void MakeHelpPathName(szFileName) char * szFileName; { char * pcFileName; int nFileNameLen; nFileNameLen = GetModuleFileName(hInst,szFileName,EXE_NAME_MAX_SIZE); pcFileName = szFileName + nFileNameLen; while (pcFileName > szFileName) { if (*pcFileName == '\\' || *pcFileName == ':') { *(++pcFileName) = '\0'; break; } nFileNameLen--; pcFileName--; } if ((nFileNameLen+13) < EXE_NAME_MAX_SIZE) { lstrcat(szFileName, "helpex.hlp"); } else { lstrcat(szFileName, "?"); } return; }