/* * Module Name: help.c * Copyright (c) 1985 - 1999, Microsoft Corporation * History: * 23-May-95 BradG Created to consolicate client-side help routines. */ #include "precomp.h" #pragma hdrstop #define MAX_ATTEMPTS 5 // maximum -1 id controls to search through char szDefaultHelpFileA[] = "windows.hlp"; PWCHAR szEXECHELP = TEXT("\\winhlp32 - "); PWCHAR szMS_WINHELP = L"MS_WINHELP"; // Application class PWCHAR szMS_POPUPHELP = L"MS_POPUPHELP"; // Popup class PWCHAR szMS_TCARDHELP = L"MS_TCARDHELP"; // Training card class // These are in winhelp.h in Chicago #define HLP_POPUP 'p' // Execute WinHelp as a popup #define HLP_TRAININGCARD 'c' // Execute WinHelp as a training card #define HLP_APPLICATION 'x' // Execute WinHelp as application help /* * SendWinHelpMessage * Attempts to give the winhelp process the right to take the * foreground (it will fail if the calling processs doesn't have * the right itself). Then it sends the WM_WINHELP message * History: * 02-10-98 GerardoB Created */ LRESULT SendWinHelpMessage (HWND hwnd, WPARAM wParam, LPARAM lParam) { DWORD dwProcessId = 0; GetWindowThreadProcessId(hwnd, &dwProcessId); AllowSetForegroundWindow(dwProcessId); return SendMessage(hwnd, WM_WINHELP, wParam, lParam); } /* * HFill * Builds a data block for communicating with help * LATER 13 Feb 92 GregoryW * This needs to stay ANSI until we have a Unicode help engine * History: * 04-15-91 JimA Ported. * 03-24-95 BradG - YAP of Win95 code. Added code to prevent memory * overwrite on bad ulData == 0 parameter. */ LPHLP HFill( LPCSTR lpszHelp, DWORD ulCommand, // HELP_ constant ULONG_PTR ulData) { DWORD cb; // Size of the data block DWORD cbStr; // Length of the help file name DWORD cbData; // Size of the dwData parameter in bytes (0 if not used) LPHLP phlp; // Pointer to data block BYTE bType; // dwData parameter type /* * Get the length of the help file name */ cbStr = (lpszHelp) ? strlen(lpszHelp) + 1 : 0; /* * Get the length of any dwData parameters */ bType = HIBYTE(LOWORD(ulCommand)); if (ulData) { switch (bType) { case HIBYTE(HELP_HB_STRING): /* * ulData is an ANSI string, so compute its length */ cbData = strlen((LPSTR)ulData) + 1; break; case HIBYTE(HELP_HB_STRUCT): /* * ulData points to a structure who's first member is * an int that contains the size of the structure in bytes. */ cbData = *((int *)ulData); break; default: /* * dwData has no parameter */ cbData = 0; } } else { /* * No parameter is present */ cbData = 0; } /* * Calculate size (NOTE: HLP is called WINHLP in Win95) */ cb = sizeof(HLP) + cbStr + cbData; /* * Get data block */ if ((phlp = (LPHLP)LocalAlloc(LPTR, cb)) == NULL) return NULL; /* * Fill in info */ phlp->cbData = (WORD)cb; phlp->usCommand = (WORD)ulCommand; phlp->ulReserved = 0; // phlp->ulTopic = 0; /* * Fill in file name */ if (lpszHelp) { phlp->offszHelpFile = sizeof(HLP); // NOTE: HLP is called WINHLP in Win95 strcpy((LPSTR)(phlp + 1), lpszHelp); } else { phlp->offszHelpFile = 0; } /* * Fill in data */ switch (bType) { case HIBYTE(HELP_HB_STRING): if (cbData) { phlp->offabData = (WORD)(sizeof(HLP) + cbStr); // NOTE: HLP is called WINHLP in Win95 strcpy((LPSTR)phlp + phlp->offabData, (LPSTR)ulData); } else { phlp->offabData = 0; } break; case HIBYTE(HELP_HB_STRUCT): if (cbData) { phlp->offabData = (WORD)(sizeof(HLP) + cbStr); // NOTE: HLP is called WINHLP in Win95 RtlCopyMemory((LPBYTE)phlp + phlp->offabData, (PVOID)ulData, *((int far *)ulData)); } else { phlp->offabData = 0; } break; default: phlp->offabData = 0; // BradG - This item is named differently in the Win95 WINHLP structure // phlp->ctx = ulData; phlp->ulTopic = ulData; break; } return(phlp); } /* * This function launches the WinHlp32 executable with the correct command line arguments. * History: * 3/23/95 BradG YAP (yet another port) of changes from Win95. */ BOOL LaunchHelper(LPWSTR lpfile, DWORD dwType) { int cchLen; int cchShift; DWORD idProcess; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; PWCHAR pwcExecHelp; pwcExecHelp = szEXECHELP; /* * Are we at the root?? If so, skip over leading backslash in text string */ if (*lpfile) { cchLen = wcslen(lpfile); cchShift = (lpfile[cchLen - 1] == TEXT('\\')) ? 1 : 0; wcscat(lpfile, pwcExecHelp + cchShift); } else { wcscat(lpfile, pwcExecHelp + 1); } /* * Defaultly send "winhlp32 -x" or adjust the last flag character */ switch (dwType) { case TYPE_POPUP: lpfile[wcslen(lpfile)-1] = TEXT(HLP_POPUP); break; case TYPE_TCARD: lpfile[wcslen(lpfile)-1] = TEXT(HLP_TRAININGCARD); break; default: lpfile[wcslen(lpfile)-1] = TEXT(HLP_APPLICATION); break; } /* * Launch winhelp */ memset(&StartupInfo,0,sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); StartupInfo.wShowWindow = SW_SHOW; StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK; idProcess = (DWORD)CreateProcessW(NULL, lpfile, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &StartupInfo, &ProcessInformation); if (idProcess) { WaitForInputIdle(ProcessInformation.hProcess, 10000); NtClose(ProcessInformation.hProcess); NtClose(ProcessInformation.hThread); return TRUE; } return FALSE; } /* * LaunchHelp * Traverse the Windows, System and %PATH% directories attempting to launch * the WinHelp32 application. * History: * 3/23/95 BradG YAP of new changes from Win95 */ BOOL LaunchHelp(DWORD dwType) { WCHAR wszPath[MAX_PATH]; GetSystemWindowsDirectoryW(wszPath, MAX_PATH); if (LaunchHelper(wszPath, dwType)) return TRUE; /* * Search the system directory (Not in Win95) */ GetSystemDirectoryW(wszPath, MAX_PATH); if (LaunchHelper(wszPath, dwType)) return TRUE; /* * Try the search path */ wszPath[0] = L'\0'; return LaunchHelper(wszPath, dwType); } /* * GetNextDlgHelpItem * This is a reduced version of the GetNextDlgTabItem function that does not * skip disabled controls. * History: * 3/25/95 BradG Ported from Win95 */ PWND GetNextDlgHelpItem(PWND pwndDlg, PWND pwnd) { PWND pwndSave; if (pwnd == pwndDlg) pwnd = NULL; else { pwnd = _GetChildControl(pwndDlg, pwnd); if (pwnd) { if (!_IsDescendant(pwndDlg, pwnd)) return(NULL); } } /* * BACKWARD COMPATIBILITY * Note that the result when there are no tabstops of * IGetNextDlgTabItem(hwndDlg, NULL, FALSE) was the last item, now * will be the first item. We could put a check for fRecurse here * and do the old thing if not set. */ /* * We are going to bug out if we hit the first child a second time. */ pwndSave = pwnd; pwnd = _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE); while ((pwnd != pwndSave) && (pwnd != pwndDlg)) { UserAssert(pwnd); if (!pwndSave) pwndSave = pwnd; if ((pwnd->style & (WS_TABSTOP | WS_VISIBLE)) == (WS_TABSTOP | WS_VISIBLE)) /* * Found it. */ break; pwnd = _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE); } return(pwnd); } /* * HelpMenu * History: * 01-Feb-1994 mikeke Ported. */ UINT HelpMenu( HWND hwnd, PPOINT ppt) { INT cmd; HMENU hmenu = LoadMenu( hmodUser, MAKEINTRESOURCE(ID_HELPMENU)); if (hmenu != NULL) { cmd = TrackPopupMenu( GetSubMenu(hmenu, 0), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, ppt->x, ppt->y, 0, hwnd, NULL); NtUserDestroyMenu(hmenu); return cmd; } return (UINT)-1; } /* * FindWinHelpWindow * This function attempts to locate the help window. If it fails, it attempts * to launch WinHlp32.exe and then look for its window. * History: * 03/24/95 BradG Created by extracting code from xxxWinHelpA */ HWND FindWinHelpWindow( LPCWSTR lpwstrHelpWindowClass, DWORD dwType, BOOL bLaunchIt ) { HWND hwndHelp; /* * Find the current help window. If not found, try and launch * the WinHlp32 application. We are interested only in 32 bit help. * Note that 16 bit apps don't walk this path, ntvdm taking care of * starting the 16 bit help for them. */ hwndHelp = InternalFindWindowExW(NULL, NULL, lpwstrHelpWindowClass, NULL, FW_32BIT); if (hwndHelp == NULL) { if (bLaunchIt) { /* * Can't find it --> see if we want to launch it */ if ((LaunchHelp(dwType) == FALSE) || (hwndHelp = FindWindowEx(NULL, NULL, (LPWSTR)lpwstrHelpWindowClass, NULL)) == NULL) { /* * Can't find help, or not enough memory to load help. * pwndHelp will be NULL at this point. */ #if DBG RIPMSG0( RIP_WARNING, "xxxWinHelpA: xxxLaunchHelp and xxxFindWinow failed." ); #endif } } } return hwndHelp; } /* * HWND version of Enumeration function to fins controls while * ignoring group boxes but not disabled controls. */ BOOL CALLBACK EnumHwndDlgChildProc(HWND hwnd, LPARAM lParam ) { PWND pwnd; BOOL bResult; if (pwnd = ValidateHwnd(hwnd)) bResult = EnumPwndDlgChildProc( pwnd, lParam ); else bResult = TRUE; return bResult; } /* * WinHelp * Displays help * History: * 04-15-91 JimA Ported. * 01-29-92 GregoryW Neutral version. * 05-22-92 SanfordS Added support for help structures * 03-24-95 BradG Moved from Client side WinHelpA to server side * xxxWinHelpA because of changes in Win95. The * function xxxServerWinHelp was merged. */ BOOL WinHelpA( HWND hwnd, // hwndMain may be NULL. LPCSTR lpszHelp, UINT uCommand, ULONG_PTR dwData) { LPWSTR lpwstrHelpWindowClass; LPHLP lpHlp = NULL; DWORD dwType; PWND pwnd; HWND hwndHelp = NULL; /* Handle of help's main window */ PWND pwndTop = NULL; /* Top level window that WinHelp uses. */ PWND pwndMain; /* pointer to main help control */ LRESULT lResult; POINT ptCur; BOOL bResult = TRUE; pwnd = ValidateHwnd(hwnd); if (uCommand & HELP_TCARD) { /* * For Training Cards, the HELP_TCARD bit is set. We need to * set our help window class to szMS_TCARDHELP and then remove * the HELP_TCARD bit. */ lpwstrHelpWindowClass = szMS_TCARDHELP; uCommand &= ~HELP_TCARD; // mask out the tcard flag dwType = TYPE_TCARD; } else { if( (uCommand == HELP_CONTEXTMENU) || (uCommand == HELP_CONTEXTPOPUP) || (uCommand == HELP_SETPOPUP_POS) || (uCommand == HELP_WM_HELP)) { /* * Popups should be connected to a valid window. pwndMain has already * been validated as a real window handle or NULL, so we just need to * check the NULL case here. */ if (pwnd == NULL) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "WinHelpA: NULL hWnd invalid for this type of help command (0x%X)", uCommand); bResult = FALSE; goto Exit_WinHelp; } dwType = TYPE_POPUP; lpwstrHelpWindowClass = szMS_POPUPHELP; } else { dwType = TYPE_NORMAL; lpwstrHelpWindowClass = szMS_WINHELP; } } /* * Get the cursor's current location This is where we assume the user * clicked. We will use this position to search for a child window and * to set the context sensitive help popup window's location. * If the last input was a keyboard one, use the point in the center * of the focus window rectangle. MCostea #249270 */ if (gpsi->bLastRITWasKeyboard) { HWND hWndFocus = GetFocus(); RECT rcWindow; if (GetWindowRect(hWndFocus, &rcWindow)) { ptCur.x = (rcWindow.left + rcWindow.right)/2; ptCur.y = (rcWindow.top + rcWindow.bottom)/2; } else { goto getCursorPos; } } else { getCursorPos: GetCursorPos(&ptCur); } /* * If we are handling the HELP_CONTEXTMENU command, see if we * can determine the correct child window. */ if (uCommand == HELP_CONTEXTMENU && FIsParentDude(pwnd)) { LONG lPt; int nHit; DLGENUMDATA DlgEnumData; /* * If the user really clicked on the caption or the system menu, * then we want the context menu for the window, not help for a * control. This makes it consistent across all 3.x and 4.0 * windows. */ lPt = MAKELONG(ptCur.x,ptCur.y); nHit = FindNCHit(pwnd, lPt); if ((nHit == HTCAPTION) || (nHit == HTSYSMENU)) DefWindowProc(hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, lPt); /* * If this is a dialog class, then one of three things has * happened: * o This is a disabled control * o This is a static text control * o This is the background of the dialog box. * What we do is enumerate the child windows and see if * any of them contain the current cursor point. If they do, * change our window handle and continue on. Otherwise, * return doing nothing -- we don't want context-sensitive * help for a dialog background. * If this is a group box, then we might have clicked on a * disabled control, so we enumerate child windows to see * if we get another control. */ DlgEnumData.pwndDialog = pwnd; DlgEnumData.pwndControl = NULL; DlgEnumData.ptCurHelp = ptCur; EnumChildWindows(hwnd, (WNDENUMPROC)EnumHwndDlgChildProc, (LPARAM)&DlgEnumData); if (DlgEnumData.pwndControl == NULL) { /* * Can't find a control, so nothing to do. */ goto Exit_WinHelp; } else { /* * Remember this control because it will be used as the * control for context sensitive help. */ pwndMain = DlgEnumData.pwndControl; } } else { /* * We will use pwnd as our main control. No need to lock it * because it is already locked. */ pwndMain = pwnd; } /* * For HELP_CONTEXTPOPUP and HELP_WM_HELP, see if we can derive the * context id by looking at the array of double word ID pairs that * have been passed in in dwData. */ if (uCommand == HELP_CONTEXTMENU || uCommand == HELP_WM_HELP) { int id; int i; LPDWORD pid; // MapIdToHelp: /* * Be careful about the cast below. We need the ID, which is stored * in the LOWORD of spmenu to be sign extended to an int. * Don't sign extend so IDs like 8008 work */ id = (DWORD)(PTR_TO_ID(pwndMain->spmenu)); // get control id pid = (LPDWORD) dwData; /* * Is the control's ID -1? */ if ((SHORT)id == -1) { /* * This is a static (i.e., ID'less) control */ PWND pwndCtrl; int cAttempts = 0; /* * If the control is a group box, with an ID of -1, bail out * as the UI specs decided to have no context help * for these cases. MCostea */ if ((TestWF(pwndMain, BFTYPEMASK) == BS_GROUPBOX) && (GETFNID(pwndMain) == FNID_BUTTON)) { goto Exit_WinHelp; } /* * For non-id controls (typically static controls), step * through to the next tab item. Keep finding the next tab * item until we find a valid id, or we have tried * MAX_ATTEMPTS times. */ do { pwndCtrl = GetNextDlgHelpItem( REBASEPWND(pwndMain,spwndParent), pwndMain ); /* * pwndCtrl will be NULL if hwndMain doesn't have a parent, * or if there are no tab stops. */ if (!pwndCtrl) { /* * Remember to unlock the control */ bResult = FALSE; goto Exit_WinHelp; } /* * Be careful about the cast below. We need the ID, which is stored * in the LOWORD of spmenu to be sign extended to an int. * Don't sign extend so IDs like 8008 work */ id = (DWORD)(PTR_TO_ID(pwndCtrl->spmenu)); } while (((SHORT)id == -1) && (++cAttempts < MAX_ATTEMPTS)); } if ((SHORT)id == -1) { id = -1; } /* * Find the id value in array of id/help context values */ for (i = 0; pid[i]; i += 2) { if ((int) pid[i] == id) break; } /* * Since no help was specified for the found control, see if * the control is one of the known ID (i.e., OK, Cancel...) */ if (!pid[i]) { /* * Help for the standard controls is in the default * help file windows.hlp. Switch to this file. */ lpszHelp = szDefaultHelpFileA; switch (id) { case IDOK: dwData = IDH_OK; break; case IDCANCEL: dwData = IDH_CANCEL; break; case IDHELP: dwData = IDH_HELP; break; default: /* * Unknown control, give a generic missing context info * popup message in windows.hlp. */ dwData = IDH_MISSING_CONTEXT; } } else { dwData = pid[i + 1]; if (dwData == (DWORD)-1) { /* * Remember, to unlock the control */ goto Exit_WinHelp; // caller doesn't want help after all } } /* * Now that we know the caller wants help for this control, display the * help menu. */ if (uCommand == HELP_CONTEXTMENU) { int cmd; /* * Must lock pwndMain because it may have been reassigned above. */ cmd = HelpMenu(HW(pwndMain), &ptCur); if (cmd <= 0) // probably means user cancelled the menu goto Exit_WinHelp; } /* * Create WM_WINHELP's HLP data structure for HELP_SETPOPUP_POS */ if (!(lpHlp = HFill(lpszHelp, HELP_SETPOPUP_POS, MAKELONG(pwndMain->rcWindow.left, pwndMain->rcWindow.top)))) { /* * Remember to unlock pwndMain if needed */ bResult = FALSE; goto Exit_WinHelp; } /* * Tell WinHelp where to put the popup. This is different than Win95 * because we try and avoid a recursive call here. So, we find the * WinHlp32 window and send the HELP_SETPOPUP_POS. No recursion. */ hwndHelp = FindWinHelpWindow( lpwstrHelpWindowClass, dwType, TRUE); if (hwndHelp == NULL ) { /* * Uable to communicate with WinHlp32.exe. * Remember to unlock the control */ bResult = FALSE; goto Exit_WinHelp; } /* * Send the WM_WINHELP message to WinHlp32's window. */ lResult = SendWinHelpMessage(hwndHelp, (WPARAM)HW(pwndMain), (LPARAM)lpHlp); LocalFree(lpHlp); lpHlp = NULL; /* BradG - revalidate pwndMain? */ if (!lResult) { /* * WinHlp32 couldn't process the command. Bail out! */ bResult = FALSE; goto Exit_WinHelp; } /* * Make HELP_WM_HELP and HELP_CONTEXTMENU act like HELP_CONTEXTPOPUP */ uCommand = HELP_CONTEXTPOPUP; } if (uCommand == HELP_CONTEXTPOPUP ) { // MapNullHlpToWindowsHlp: /* * If no help file was specified, use windows.hlp */ if (lpszHelp == NULL || *lpszHelp == '\0') lpszHelp = szDefaultHelpFileA; // default: use windows.hlp /* * WINHELP.EXE will call SetForegroundWindow on the hwnd that we pass * to it below. We really want to pass the parent dialog hwnd of the * control so that focus will properly be restored to the dialog and * not the control that wants help. */ pwndTop = GetTopLevelWindow(pwndMain); } else { pwndTop = pwndMain; } /* * Move Help file name to a handle */ if (!(lpHlp = HFill(lpszHelp, uCommand, dwData))) { /* * Can't allocate memory */ bResult = FALSE; goto Exit_WinHelp; } /* * Get a pointer to the help window. */ hwndHelp = FindWinHelpWindow( lpwstrHelpWindowClass, dwType, (uCommand != HELP_QUIT)); if (hwndHelp == NULL) { if (uCommand != HELP_QUIT) /* * Can't find Winhlp */ bResult = FALSE; goto Exit_WinHelp; } /* * Send the WM_WINHELP message to WinHlp32's window * Must ThreadLock pwndHelp AND pwndMain (because pwndMain may have been * reassigned above). */ SendWinHelpMessage(hwndHelp, (WPARAM)HW(pwndTop), (LPARAM)lpHlp); /* * Free the help info data structure (if not already free). */ Exit_WinHelp: if (lpHlp != NULL) { LocalFree(lpHlp); } return bResult; } #if 0 /* * Communicating with WinHelp involves using Windows SendMessage function * to pass blocks of information to WinHelp. The call looks like. * SendMessage(hwndHelp, WM_WINHELP, pidSource, pwinhlp); * Where: * hwndHelp - the window handle of the help application. This * is obtained by enumerating all the windows in the * system and sending them HELP_FIND commands. The * application may have to load WinHelp. * pidSource - the process id of the sending process * pwinhlp - a pointer to a WINHLP structure * The data in the handle will look like: * +-------------------+ * | cbData | * | ulCommand | * | hwndHost | * | ulTopic | * | ulReserved | * | offszHelpFile |\ - offsets measured from beginning * | offaData | \ of header. * +-------------------+ / * | Help file name |/ * | and path | * +-------------------+ * | Other data | * | (keyword) | * +-------------------+ * hwndMain - the handle to the main window of the application * calling help * The defined commands are: * HELP_CONTEXT 0x0001 Display topic in ulTopic * HELP_KEY 0x0101 Display topic for keyword in offabData * HELP_QUIT 0x0002 Terminate help */ BOOL WinHelpA( HWND hwndMain, LPCSTR lpszHelp, UINT uCommand, ULONG_PTR dwData) { LPHLP lpHlp; BOOL fSuccess; DWORD dwType; HWND hwndHelp; /* Handle of help's main window */ LPWSTR lpwstr; PWND pwndMain; pwndMain = ValidateHwnd(hwndMain); if ((uCommand == HELP_CONTEXTMENU) || (uCommand == HELP_CONTEXTPOPUP) || (uCommand == HELP_SETPOPUP_POS) || (uCommand == HELP_WM_HELP)) { dwType = TYPE_POPUP; } else { dwType = TYPE_NORMAL; } switch (uCommand) { case HELP_WM_HELP: { #if 0 // Tell WinHelp where to put the popup if (!WinHelpA(hwndMain, lpszHelp, HELP_SETPOPUP_POS, MAKELONG(pwndMain->rcWindow.left, pwndMain->rcWindow.top))) return FALSE; /* * Unlike HELP_CONTEXTMENU, with this command we pop up the help * topic immediately instead of making the user go through a menu. */ #endif goto MapIdToHelp; } break; case HELP_CONTEXTMENU: { int cmd; int id; int i; POINT ptCur; LPDWORD pid; GetCursorPos(&ptCur); if (pwndMain != NULL && pwndMain->fnid == FNID_DIALOG) { /* * If this is a dialog class, then one of three things has * happened: * o This is a disabled control * o This is a static text control * o This is the background of the dialog box. * What we do is enumerate the child windows and see if * any of them contain the current cursor point. If they do, * change our window handle and continue on. Otherwise, * return doing nothing -- we don't want context-sensitive * help for a dialog background. */ pwndMain = REBASEPWND(pwndMain, spwndChild); while (pwndMain != NULL && PtInRect(&(pwndMain->rcWindow), ptCur)) { pwndMain = REBASEPWND(pwndMain, spwndNext); } if (pwndMain == NULL) return FALSE; } cmd = HelpMenu(hwndMain, &ptCur); if (cmd <= 0) { // probably means user cancelled the menu return(FALSE); // !!! mem leak? } else if (cmd == HELP_INDEX) { // Search uCommand = HELP_FINDER; } else { MapIdToHelp: UserAssert(pwndMain); // Tell WinHelp where to put the popup if (!WinHelpA(hwndMain, lpszHelp, HELP_SETPOPUP_POS, MAKELONG(pwndMain->rcWindow.left, pwndMain->rcWindow.top))) return FALSE; id = LOWORD(pwndMain->spmenu); // get control id pid = (LPDWORD) dwData; if (id == -1) { // static control? PWND pwndCtrl; int cAttempts = 0; // For non-id controls (typically static controls), step // through to the next tab item. Keep finding the next tab // item until we find a valid id, or we have tried // MAX_ATTEMPTS times. do { pwndCtrl = _GetNextDlgTabItem(REBASEPWND(pwndMain, spwndParent), pwndMain, FALSE); // hwndCtrl will be NULL if hwndMain doesn't have a parent, // or if there are no tab stops. if (!pwndCtrl) { return(FALSE); } id = LOWORD(pwndCtrl->spmenu); } while ((id == -1) && (++cAttempts < MAX_ATTEMPTS)); } // Find the id value in array of id/help context values for (i = 0; pid[i]; i += 2) { if ((int) pid[i] == id) break; } if (!pid[i]) { lpszHelp = szDefaultHelpFileA; // switch to windows.hlp switch (id) { case IDOK: dwData = IDH_OK; break; case IDCANCEL: dwData = IDH_CANCEL; break; case IDHELP: dwData = IDH_HELP; break; default: dwData = IDH_MISSING_CONTEXT; } } else { dwData = pid[i + 1]; if (dwData == (DWORD) -1) { return TRUE; // caller doesn't want help after all } } uCommand = HELP_CONTEXTPOPUP; } // If no help file was specified, use windows.hlp if (lpszHelp == NULL || *lpszHelp == '\0') lpszHelp = szDefaultHelpFileA; // default: use windows.hlp } } /* * Move Help file name to a handle */ if (!(lpHlp = HFill(lpszHelp, uCommand, dwData))) return FALSE; /* * Pass it on to WinHelp32 */ fSuccess = TRUE; if (dwType == TYPE_POPUP) lpwstr = szMS_POPUPHELP; else lpwstr = szMS_WINHELP; if ((hwndHelp = FindWindowEx(NULL, NULL, lpwstr, NULL)) == NULL) { if (uCommand == HELP_QUIT) fSuccess = TRUE; /* * Can't find it --> launch it */ if (!LaunchHelp(dwType) || (hwndHelp = FindWindowEx(NULL, NULL, lpwstr, NULL)) == NULL) fSuccess = FALSE; } if (hwndHelp != NULL) SendMessage(hwndHelp, WM_WINHELP, (DWORD)hwndMain, (LONG)lpHlp); LocalFree(lpHlp); return fSuccess; } #endif /* * WinHelpW * Calls WinHelpA after doing any necessary translation. * Our help engine is ASCII only. */ BOOL WinHelpW( HWND hwndMain, LPCWSTR lpwszHelp, UINT uCommand, ULONG_PTR dwData) { BOOL fSuccess = FALSE; LPSTR lpAnsiHelp = NULL; LPSTR lpAnsiKey = NULL; PMULTIKEYHELPA pmkh = NULL; PHELPWININFOA phwi = NULL; NTSTATUS Status; /* * First convert the string. */ if (lpwszHelp != NULL && !WCSToMB(lpwszHelp, -1, &lpAnsiHelp, -1, TRUE)) { return FALSE; } /* * Then convert dwData if needed */ switch (uCommand) { case HELP_MULTIKEY: if (!WCSToMB(((PMULTIKEYHELPW)dwData)->szKeyphrase, -1, &lpAnsiKey, -1, TRUE)) { goto FreeAnsiHelp; } pmkh = (PMULTIKEYHELPA)LocalAlloc(LPTR, sizeof(MULTIKEYHELPA) + strlen(lpAnsiKey)); if (pmkh == NULL) { goto FreeAnsiKeyAndHelp; } pmkh->mkSize = sizeof(MULTIKEYHELPA) + strlen(lpAnsiKey); Status = RtlUnicodeToMultiByteN((LPSTR)&pmkh->mkKeylist, sizeof(CHAR), NULL, (LPWSTR)&((PMULTIKEYHELPW)dwData)->mkKeylist, sizeof(WCHAR)); strcpy(pmkh->szKeyphrase, lpAnsiKey); if (!NT_SUCCESS(Status)) { goto FreeAnsiKeyAndHelp; } dwData = (ULONG_PTR)pmkh; break; case HELP_SETWINPOS: if (!WCSToMB(((PHELPWININFOW)dwData)->rgchMember, -1, &lpAnsiKey, -1, TRUE)) { goto FreeAnsiKeyAndHelp; } phwi = (PHELPWININFOA)LocalAlloc(LPTR, ((PHELPWININFOW)dwData)->wStructSize); if (phwi == NULL) { goto FreeAnsiKeyAndHelp; } *phwi = *((PHELPWININFOA)dwData); // copies identical parts strcpy(phwi->rgchMember, lpAnsiKey); dwData = (ULONG_PTR)phwi; break; case HELP_KEY: case HELP_PARTIALKEY: case HELP_COMMAND: if (!WCSToMB((LPCTSTR)dwData, -1, &lpAnsiKey, -1, TRUE)) { goto FreeAnsiKeyAndHelp; } dwData = (ULONG_PTR)lpAnsiKey; break; } /* * Call the Ansi version */ fSuccess = WinHelpA(hwndMain, lpAnsiHelp, uCommand, dwData); if (pmkh) { LocalFree(pmkh); } if (phwi) { LocalFree(phwi); } FreeAnsiKeyAndHelp: if (lpAnsiKey) { LocalFree(lpAnsiKey); } FreeAnsiHelp: if (lpAnsiHelp) LocalFree(lpAnsiHelp); return fSuccess; }