test: help.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# help.c

Go to the documentation of this file.

    00001 /**************************************************************************\
    00002 * Module Name: help.c
    00003 *
    00004 * Copyright (c) 1985 - 1999, Microsoft Corporation
    00005 *
    00006 * History:
    00007 * 23-May-95 BradG   Created to consolicate client-side help routines.
    00008 *
    00009 \**************************************************************************/
    00010
    00011 #include "precomp.h"
    00012 #pragma hdrstop
    00013
    00014
    00015 #define MAX_ATTEMPTS    5       // maximum -1 id controls to search through
    00016 char szDefaultHelpFileA[] = "windows.hlp";
    00017
    00018 PWCHAR szEXECHELP = TEXT("\\winhlp32 - ");
    00019 PWCHAR szMS_WINHELP =     L"MS_WINHELP";    // Application class
    00020 PWCHAR szMS_POPUPHELP =   L"MS_POPUPHELP";  // Popup class
    00021 PWCHAR szMS_TCARDHELP =   L"MS_TCARDHELP";  // Training card class
    00022
    00023 // These are in winhelp.h in Chicago
    00024 #define HLP_POPUP               'p'     // Execute WinHelp as a popup
    00025 #define HLP_TRAININGCARD        'c'     // Execute WinHelp as a training card
    00026 #define HLP_APPLICATION         'x'     // Execute WinHelp as application help
    00027
    00028
    00029 /***************************************************************************\
    00030 * SendWinHelpMessage
    00031 *
    00032 * Attempts to give the winhelp process the right to take the
    00033 *  foreground (it will fail if the calling processs doesn't have
    00034 *  the right itself). Then it sends the WM_WINHELP message
    00035 *
    00036 * History:
    00037 * 02-10-98 GerardoB     Created
    00038 \***************************************************************************/
    00039 LRESULT SendWinHelpMessage (HWND hwnd, WPARAM wParam, LPARAM lParam)
    00040 {
    00041     DWORD dwProcessId = 0;
    00042     GetWindowThreadProcessId(hwnd, &dwProcessId);
    00043     AllowSetForegroundWindow(dwProcessId);
    00044     return SendMessage(hwnd, WM_WINHELP, wParam, lParam);
    00045 }
    00046 /***************************************************************************\
    00047 * HFill
    00048 *
    00049 * Builds a data block for communicating with help
    00050 *
    00051 * LATER 13 Feb 92 GregoryW
    00052 * This needs to stay ANSI until we have a Unicode help engine
    00053 *
    00054 * History:
    00055 * 04-15-91 JimA Ported.
    00056 * 03-24-95 BradG - YAP of Win95 code.  Added code to prevent memory
    00057 *                  overwrite on bad ulData == 0 parameter.
    00058 \***************************************************************************/
    00059
    00060 LPHLP HFill(
    00061     LPCSTR lpszHelp,
    00062     DWORD  ulCommand,        // HELP_ constant
    00063     ULONG_PTR ulData)
    00064 {
    00065     DWORD   cb;     // Size of the data block
    00066     DWORD   cbStr;  // Length of the help file name
    00067     DWORD   cbData; // Size of the dwData parameter in bytes (0 if not used)
    00068     LPHLP   phlp;   // Pointer to data block
    00069     BYTE    bType;  // dwData parameter type
    00070
    00071     /*
    00072      * Get the length of the help file name
    00073      */
    00074     cbStr = (lpszHelp) ? strlen(lpszHelp) + 1 : 0;
    00075
    00076     /*
    00077      * Get the length of any dwData parameters
    00078      */
    00079     bType = HIBYTE(LOWORD(ulCommand));
    00080     if (ulData) {
    00081         switch (bType) {
    00082         case HIBYTE(HELP_HB_STRING):
    00083             /*
    00084              * ulData is an ANSI string, so compute its length
    00085              */
    00086             cbData = strlen((LPSTR)ulData) + 1;
    00087             break;
    00088
    00089         case HIBYTE(HELP_HB_STRUCT):
    00090             /*
    00091              * ulData points to a structure who's first member is
    00092              * an int that contains the size of the structure in bytes.
    00093              */
    00094             cbData = *((int *)ulData);
    00095             break;
    00096
    00097         default:
    00098             /*
    00099              * dwData has no parameter
    00100              */
    00101             cbData = 0;
    00102         }
    00103     } else {
    00104         /*
    00105          * No parameter is present
    00106          */
    00107         cbData = 0;
    00108     }
    00109
    00110     /*
    00111      * Calculate size (NOTE: HLP is called WINHLP in Win95)
    00112      */
    00113     cb = sizeof(HLP) + cbStr + cbData;
    00114
    00115     /*
    00116      * Get data block
    00117      */
    00118     if ((phlp = (LPHLP)LocalAlloc(LPTR, cb)) == NULL)
    00119         return NULL;
    00120
    00121     /*
    00122      * Fill in info
    00123      */
    00124     phlp->cbData = (WORD)cb;
    00125     phlp->usCommand = (WORD)ulCommand;
    00126     phlp->ulReserved = 0;
    00127     // phlp->ulTopic = 0;
    00128
    00129     /*
    00130      * Fill in file name
    00131      */
    00132     if (lpszHelp) {
    00133         phlp->offszHelpFile = sizeof(HLP);  // NOTE: HLP is called WINHLP in Win95
    00134         strcpy((LPSTR)(phlp + 1), lpszHelp);
    00135     } else {
    00136         phlp->offszHelpFile = 0;
    00137     }
    00138
    00139     /*
    00140      * Fill in data
    00141      */
    00142     switch (bType) {
    00143     case HIBYTE(HELP_HB_STRING):
    00144         if (cbData) {
    00145             phlp->offabData = (WORD)(sizeof(HLP) + cbStr);  // NOTE: HLP is called WINHLP in Win95
    00146             strcpy((LPSTR)phlp + phlp->offabData, (LPSTR)ulData);
    00147         } else {
    00148             phlp->offabData = 0;
    00149         }
    00150         break;
    00151
    00152     case HIBYTE(HELP_HB_STRUCT):
    00153         if (cbData) {
    00154             phlp->offabData = (WORD)(sizeof(HLP) + cbStr);  // NOTE: HLP is called WINHLP in Win95
    00155             RtlCopyMemory((LPBYTE)phlp + phlp->offabData, (PVOID)ulData,
    00156                     *((int far *)ulData));
    00157         } else {
    00158             phlp->offabData = 0;
    00159         }
    00160         break;
    00161
    00162     default:
    00163         phlp->offabData = 0;
    00164 // BradG - This item is named differently in the Win95 WINHLP structure
    00165 //      phlp->ctx   = ulData;
    00166         phlp->ulTopic = ulData;
    00167         break;
    00168     }
    00169
    00170     return(phlp);
    00171 }
    00172
    00173
    00174 /***************************************************************************\
    00175 * LaunchHelper
    00176 *
    00177 * This function launches the WinHlp32 executable with the correct command
    00178 * line arguments.
    00179 *
    00180 * History:
    00181 *   3/23/95 BradG   YAP (yet another port) of changes from Win95.
    00182 \***************************************************************************/
    00183 BOOL LaunchHelper(LPWSTR lpfile, DWORD dwType)
    00184 {
    00185     int                 cchLen;
    00186     int                 cchShift;
    00187     DWORD               idProcess;
    00188     STARTUPINFO         StartupInfo;
    00189     PROCESS_INFORMATION ProcessInformation;
    00190     PWCHAR pwcExecHelp;
    00191
    00192     pwcExecHelp = szEXECHELP;
    00193
    00194     /*
    00195      *  Are we at the root?? If so, skip over leading backslash in text string
    00196      */
    00197     if (*lpfile) {
    00198         cchLen = wcslen(lpfile);
    00199         cchShift = (lpfile[cchLen - 1] == TEXT('\\')) ? 1 : 0;
    00200         wcscat(lpfile, pwcExecHelp + cchShift);
    00201     } else {
    00202         wcscat(lpfile, pwcExecHelp + 1);
    00203     }
    00204
    00205
    00206     /*
    00207      * Defaultly send "winhlp32 -x" or adjust the last flag character
    00208      */
    00209     switch (dwType) {
    00210     case TYPE_POPUP:
    00211         lpfile[wcslen(lpfile)-1] = TEXT(HLP_POPUP);
    00212         break;
    00213
    00214     case TYPE_TCARD:
    00215         lpfile[wcslen(lpfile)-1] = TEXT(HLP_TRAININGCARD);
    00216         break;
    00217
    00218     default:
    00219         lpfile[wcslen(lpfile)-1] = TEXT(HLP_APPLICATION);
    00220         break;
    00221     }
    00222
    00223     /*
    00224      *  Launch winhelp
    00225      */
    00226     memset(&StartupInfo,0,sizeof(StartupInfo));
    00227     StartupInfo.cb = sizeof(StartupInfo);
    00228     StartupInfo.wShowWindow = SW_SHOW;
    00229     StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
    00230
    00231     idProcess = (DWORD)CreateProcessW(NULL, lpfile,
    00232             NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &StartupInfo,
    00233             &ProcessInformation);
    00234
    00235     if (idProcess) {
    00236         WaitForInputIdle(ProcessInformation.hProcess, 10000);
    00237         NtClose(ProcessInformation.hProcess);
    00238         NtClose(ProcessInformation.hThread);
    00239         return TRUE;
    00240     }
    00241
    00242     return FALSE;
    00243 }
    00244
    00245
    00246 /***************************************************************************\
    00247 * LaunchHelp
    00248 *
    00249 * Traverse the Windows, System and %PATH% directories attempting to launch
    00250 * the WinHelp32 application.
    00251 *
    00252 * History:
    00253 * 3/23/95 BradG     YAP of new changes from Win95
    00254 \***************************************************************************/
    00255
    00256 BOOL LaunchHelp(DWORD dwType)
    00257 {
    00258     WCHAR wszPath[MAX_PATH];
    00259
    00260     GetSystemWindowsDirectoryW(wszPath, MAX_PATH);
    00261
    00262     if (LaunchHelper(wszPath, dwType))
    00263         return TRUE;
    00264
    00265     /*
    00266      * Search the system directory (Not in Win95)
    00267      */
    00268     GetSystemDirectoryW(wszPath, MAX_PATH);
    00269     if (LaunchHelper(wszPath, dwType))
    00270         return TRUE;
    00271
    00272     /*
    00273      * Try the search path
    00274      */
    00275     wszPath[0] = L'\0';
    00276     return LaunchHelper(wszPath, dwType);
    00277
    00278 }
    00279
    00280
    00281 /***************************************************************************\
    00282 * GetNextDlgHelpItem
    00283 *
    00284 * This is a reduced version of the GetNextDlgTabItem function that does not
    00285 * skip disabled controls.
    00286 *
    00287 * History:
    00288 * 3/25/95 BradG     Ported from Win95
    00289 \***************************************************************************/
    00290 PWND GetNextDlgHelpItem(PWND pwndDlg, PWND pwnd)
    00291 {
    00292     PWND pwndSave;
    00293
    00294     if (pwnd == pwndDlg)
    00295         pwnd = NULL;
    00296     else
    00297     {
    00298         pwnd = _GetChildControl(pwndDlg, pwnd);
    00299         if (pwnd)
    00300         {
    00301             if (!_IsDescendant(pwndDlg, pwnd))
    00302                 return(NULL);
    00303         }
    00304     }
    00305
    00306     /*
    00307      *  BACKWARD COMPATIBILITY
    00308      *
    00309      *  Note that the result when there are no tabstops of
    00310      *  IGetNextDlgTabItem(hwndDlg, NULL, FALSE) was the last item, now
    00311      *  will be the first item.  We could put a check for fRecurse here
    00312      *  and do the old thing if not set.
    00313      */
    00314
    00315     /*
    00316      *  We are going to bug out if we hit the first child a second time.
    00317      */
    00318     pwndSave = pwnd;
    00319     pwnd = _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE);
    00320
    00321     while ((pwnd != pwndSave) && (pwnd != pwndDlg))
    00322     {
    00323         UserAssert(pwnd);
    00324
    00325         if (!pwndSave)
    00326             pwndSave = pwnd;
    00327
    00328         if ((pwnd->style & (WS_TABSTOP | WS_VISIBLE))  == (WS_TABSTOP | WS_VISIBLE))
    00329             /*
    00330              *  Found it.
    00331              */
    00332             break;
    00333
    00334         pwnd = _NextControl(pwndDlg, pwnd, CWP_SKIPINVISIBLE);
    00335     }
    00336
    00337     return(pwnd);
    00338 }
    00339
    00340
    00341 /***************************************************************************\
    00342 * HelpMenu
    00343 *
    00344 * History:
    00345 * 01-Feb-1994 mikeke    Ported.
    00346 \***************************************************************************/
    00347
    00348 UINT HelpMenu(
    00349     HWND hwnd,
    00350     PPOINT ppt)
    00351 {
    00352     INT     cmd;
    00353     HMENU   hmenu = LoadMenu( hmodUser, MAKEINTRESOURCE(ID_HELPMENU));
    00354
    00355     if (hmenu != NULL) {
    00356         cmd = TrackPopupMenu( GetSubMenu(hmenu, 0),
    00357               TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
    00358               ppt->x, ppt->y, 0, hwnd, NULL);
    00359         NtUserDestroyMenu(hmenu);
    00360         return cmd;
    00361     }
    00362
    00363     return (UINT)-1;
    00364 }
    00365
    00366 /***************************************************************************\
    00367 * FindWinHelpWindow
    00368 *
    00369 * This function attempts to locate the help window.  If it fails, it attempts
    00370 * to launch WinHlp32.exe and then look for its window.
    00371 *
    00372 * History:
    00373 * 03/24/95 BradG   Created by extracting code from xxxWinHelpA
    00374 \***************************************************************************/
    00375 HWND FindWinHelpWindow(
    00376     LPCWSTR lpwstrHelpWindowClass,
    00377     DWORD   dwType,
    00378     BOOL    bLaunchIt
    00379     )
    00380 {
    00381     HWND    hwndHelp;
    00382
    00383     /*
    00384      *  Find the current help window.  If not found, try and launch
    00385      *  the WinHlp32 application.  We are interested only in 32 bit help.
    00386      *  Note that 16 bit apps don't walk this path, ntvdm taking care of
    00387      *  starting the 16 bit help for them.
    00388      */
    00389     hwndHelp = InternalFindWindowExW(NULL, NULL, lpwstrHelpWindowClass, NULL, FW_32BIT);
    00390
    00391     if (hwndHelp == NULL) {
    00392         if (bLaunchIt) {
    00393             /*
    00394              *  Can't find it --> see if we want to launch it
    00395              */
    00396             if ((LaunchHelp(dwType) == FALSE) ||
    00397                 (hwndHelp = FindWindowEx(NULL, NULL, (LPWSTR)lpwstrHelpWindowClass, NULL)) == NULL)
    00398               {
    00399
    00400                 /*
    00401                  *  Can't find help, or not enough memory to load help.
    00402                  *  pwndHelp will be NULL at this point.
    00403                  */
    00404 #if DBG
    00405                 RIPMSG0( RIP_WARNING, "xxxWinHelpA: xxxLaunchHelp and xxxFindWinow failed." );
    00406 #endif
    00407             }
    00408         }
    00409     }
    00410
    00411     return hwndHelp;
    00412 }
    00413
    00414
    00415 /*
    00416  *  HWND version of Enumeration function to fins controls while
    00417  *  ignoring group boxes but not disabled controls.
    00418  */
    00419 BOOL CALLBACK EnumHwndDlgChildProc(HWND hwnd, LPARAM lParam )
    00420 {
    00421     PWND pwnd;
    00422     BOOL bResult;
    00423
    00424     if (pwnd = ValidateHwnd(hwnd))
    00425         bResult = EnumPwndDlgChildProc( pwnd, lParam );
    00426     else
    00427         bResult = TRUE;
    00428
    00429     return bResult;
    00430 }
    00431
    00432
    00433 /***************************************************************************\
    00434 * WinHelp
    00435 *
    00436 * Displays help
    00437 *
    00438 * History:
    00439 * 04-15-91 JimA             Ported.
    00440 * 01-29-92 GregoryW         Neutral version.
    00441 * 05-22-92 SanfordS         Added support for help structures
    00442 * 03-24-95 BradG            Moved from Client side WinHelpA to server side
    00443 *                           xxxWinHelpA because of changes in Win95.  The
    00444 *                           function xxxServerWinHelp was merged.
    00445 \***************************************************************************/
    00446
    00447 BOOL WinHelpA(
    00448     HWND    hwnd,       // hwndMain may be NULL.
    00449     LPCSTR  lpszHelp,
    00450     UINT    uCommand,
    00451     ULONG_PTR dwData)
    00452 {
    00453     LPWSTR  lpwstrHelpWindowClass;
    00454     LPHLP   lpHlp = NULL;
    00455     DWORD   dwType;
    00456     PWND    pwnd;
    00457     HWND    hwndHelp = NULL;    /* Handle of help's main window         */
    00458     PWND    pwndTop = NULL;     /* Top level window that WinHelp uses.  */
    00459     PWND    pwndMain;           /* pointer to main help control         */
    00460     LRESULT lResult;
    00461     POINT   ptCur;
    00462     BOOL    bResult = TRUE;
    00463
    00464     pwnd = ValidateHwnd(hwnd);
    00465
    00466     if (uCommand & HELP_TCARD) {
    00467         /*
    00468          * For Training Cards, the HELP_TCARD bit is set.  We need to
    00469          * set our help window class to szMS_TCARDHELP and then remove
    00470          * the HELP_TCARD bit.
    00471          */
    00472         lpwstrHelpWindowClass = szMS_TCARDHELP;
    00473         uCommand &= ~HELP_TCARD;    // mask out the tcard flag
    00474         dwType = TYPE_TCARD;
    00475     } else {
    00476         if( (uCommand == HELP_CONTEXTMENU) || (uCommand == HELP_CONTEXTPOPUP) ||
    00477             (uCommand == HELP_SETPOPUP_POS) || (uCommand == HELP_WM_HELP)) {
    00478             /*
    00479              *  Popups should be connected to a valid window.  pwndMain has already
    00480              *  been validated as a real window handle or NULL, so we just need to
    00481              *  check the NULL case here.
    00482              */
    00483             if (pwnd == NULL) {
    00484                 RIPERR1(ERROR_INVALID_PARAMETER,
    00485                         RIP_WARNING,
    00486                         "WinHelpA: NULL hWnd invalid for this type of help command (0x%X)",
    00487                         uCommand);
    00488
    00489                 bResult = FALSE;
    00490                 goto Exit_WinHelp;
    00491             }
    00492             dwType = TYPE_POPUP;
    00493             lpwstrHelpWindowClass = szMS_POPUPHELP;
    00494         } else {
    00495             dwType = TYPE_NORMAL;
    00496             lpwstrHelpWindowClass = szMS_WINHELP;
    00497         }
    00498     }
    00499
    00500     /*
    00501      * Get the cursor's current location  This is where we assume the user
    00502      * clicked.  We will use this position to search for a child window and
    00503      * to set the context sensitive help popup window's location.
    00504      *
    00505      * If the last input was a keyboard one, use the point in the center
    00506      * of the focus window rectangle. MCostea #249270
    00507      */
    00508     if (gpsi->bLastRITWasKeyboard) {
    00509         HWND hWndFocus = GetFocus();
    00510         RECT rcWindow;
    00511
    00512         if (GetWindowRect(hWndFocus, &rcWindow)) {
    00513             ptCur.x = (rcWindow.left + rcWindow.right)/2;
    00514             ptCur.y = (rcWindow.top + rcWindow.bottom)/2;
    00515         } else {
    00516             goto getCursorPos;
    00517         }
    00518     } else {
    00519 getCursorPos:
    00520         GetCursorPos(&ptCur);
    00521     }
    00522
    00523     /*
    00524      * If we are handling the HELP_CONTEXTMENU command, see if we
    00525      * can determine the correct child window.
    00526      */
    00527     if (uCommand == HELP_CONTEXTMENU && FIsParentDude(pwnd)) {
    00528         LONG        lPt;
    00529         int         nHit;
    00530         DLGENUMDATA DlgEnumData;
    00531
    00532         /*
    00533          *  If the user really clicked on the caption or the system menu,
    00534          *  then we want the context menu for the window, not help for a
    00535          *  control.  This makes it consistent across all 3.x and 4.0
    00536          *  windows.
    00537          */
    00538         lPt = MAKELONG(ptCur.x,ptCur.y);
    00539         nHit = FindNCHit(pwnd, lPt);
    00540         if ((nHit == HTCAPTION) || (nHit == HTSYSMENU))
    00541             DefWindowProc(hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, lPt);
    00542
    00543         /*
    00544          * If this is a dialog class, then one of three things has
    00545          * happened:
    00546          *
    00547          *  o   This is a disabled control
    00548          *  o   This is a static text control
    00549          *  o   This is the background of the dialog box.
    00550          *
    00551          * What we do is enumerate the child windows and see if
    00552          * any of them contain the current cursor point. If they do,
    00553          * change our window handle and continue on. Otherwise,
    00554          * return doing nothing -- we don't want context-sensitive
    00555          * help for a dialog background.
    00556          *
    00557          * If this is a group box, then we might have clicked on a
    00558          * disabled control, so we enumerate child windows to see
    00559          * if we get another control.
    00560          */
    00561         DlgEnumData.pwndDialog = pwnd;
    00562         DlgEnumData.pwndControl = NULL;
    00563         DlgEnumData.ptCurHelp = ptCur;
    00564         EnumChildWindows(hwnd, (WNDENUMPROC)EnumHwndDlgChildProc, (LPARAM)&DlgEnumData);
    00565         if (DlgEnumData.pwndControl == NULL) {
    00566             /*
    00567              * Can't find a control, so nothing to do.
    00568              */
    00569             goto Exit_WinHelp;
    00570         } else {
    00571             /*
    00572              * Remember this control because it will be used as the
    00573              * control for context sensitive help.
    00574              */
    00575             pwndMain = DlgEnumData.pwndControl;
    00576         }
    00577     } else {
    00578         /*
    00579          * We will use pwnd as our main control.  No need to lock it
    00580          * because it is already locked.
    00581          */
    00582         pwndMain = pwnd;
    00583     }
    00584
    00585     /*
    00586      * For HELP_CONTEXTPOPUP and HELP_WM_HELP, see if we can derive the
    00587      * context id by looking at the array of double word ID pairs that
    00588      * have been passed in in dwData.
    00589      */
    00590     if (uCommand == HELP_CONTEXTMENU || uCommand == HELP_WM_HELP) {
    00591         int     id;
    00592         int     i;
    00593         LPDWORD pid;
    00594
    00595 // MapIdToHelp:
    00596         /*
    00597          * Be careful about the cast below.  We need the ID, which is stored
    00598          * in the LOWORD of spmenu to be sign extended to an int.
    00599          * Don't sign extend so IDs like 8008 work
    00600          */
    00601         id = (DWORD)(PTR_TO_ID(pwndMain->spmenu));   // get control id
    00602         pid = (LPDWORD) dwData;
    00603
    00604         /*
    00605          * Is the control's ID -1?
    00606          */
    00607         if ((SHORT)id == -1)
    00608         {
    00609             /*
    00610              * This is a static (i.e., ID'less) control
    00611              */
    00612             PWND pwndCtrl;
    00613             int cAttempts = 0;
    00614
    00615             /*
    00616              * If the control is a group box, with an ID of -1, bail out
    00617              * as the UI specs decided to have no context help
    00618              * for these cases.  MCostea
    00619              */
    00620             if ((TestWF(pwndMain, BFTYPEMASK) == BS_GROUPBOX) &&
    00621                 (GETFNID(pwndMain) == FNID_BUTTON)) {
    00622                 goto Exit_WinHelp;
    00623             }
    00624
    00625             /*
    00626              * For non-id controls (typically static controls), step
    00627              * through to the next tab item. Keep finding the next tab
    00628              * item until we find a valid id, or we have tried
    00629              * MAX_ATTEMPTS times.
    00630              */
    00631             do {
    00632                 pwndCtrl = GetNextDlgHelpItem( REBASEPWND(pwndMain,spwndParent), pwndMain );
    00633
    00634                 /*
    00635                  * pwndCtrl will be NULL if hwndMain doesn't have a parent,
    00636                  * or if there are no tab stops.
    00637                  */
    00638                 if (!pwndCtrl) {
    00639                     /*
    00640                      * Remember to unlock the control
    00641                      */
    00642                     bResult = FALSE;
    00643                     goto Exit_WinHelp;
    00644                 }
    00645
    00646                 /*
    00647                  * Be careful about the cast below.  We need the ID, which is stored
    00648                  * in the LOWORD of spmenu to be sign extended to an int.
    00649                  * Don't sign extend so IDs like 8008 work
    00650                  */
    00651                 id = (DWORD)(PTR_TO_ID(pwndCtrl->spmenu));
    00652
    00653             } while (((SHORT)id == -1) && (++cAttempts < MAX_ATTEMPTS));
    00654         }
    00655
    00656         if ((SHORT)id == -1) {
    00657             id = -1;
    00658         }
    00659
    00660         /*
    00661          * Find the id value in array of id/help context values
    00662          */
    00663         for (i = 0; pid[i]; i += 2) {
    00664             if ((int) pid[i] == id)
    00665                 break;
    00666         }
    00667
    00668         /*
    00669          * Since no help was specified for the found control, see if
    00670          * the control is one of the known ID (i.e., OK, Cancel...)
    00671          */
    00672         if (!pid[i]) {
    00673             /*
    00674              * Help for the standard controls is in the default
    00675              * help file windows.hlp.  Switch to this file.
    00676              */
    00677             lpszHelp = szDefaultHelpFileA;
    00678
    00679             switch (id) {
    00680             case IDOK:
    00681                 dwData = IDH_OK;
    00682                 break;
    00683
    00684             case IDCANCEL:
    00685                 dwData = IDH_CANCEL;
    00686                 break;
    00687
    00688             case IDHELP:
    00689                 dwData = IDH_HELP;
    00690                 break;
    00691
    00692             default:
    00693                 /*
    00694                  * Unknown control, give a generic missing context info
    00695                  * popup message in windows.hlp.
    00696                  */
    00697                 dwData = IDH_MISSING_CONTEXT;
    00698             }
    00699         } else {
    00700             dwData = pid[i + 1];
    00701             if (dwData == (DWORD)-1) {
    00702                 /*
    00703                  * Remember, to unlock the control
    00704                  */
    00705                 goto Exit_WinHelp;     // caller doesn't want help after all
    00706             }
    00707         }
    00708
    00709         /*
    00710          *  Now that we know the caller wants help for this control, display the
    00711          *  help menu.
    00712          */
    00713         if (uCommand == HELP_CONTEXTMENU)
    00714         {
    00715             int cmd;
    00716
    00717             /*
    00718              * Must lock pwndMain because it may have been reassigned above.
    00719              */
    00720             cmd = HelpMenu(HW(pwndMain), &ptCur);
    00721             if (cmd <= 0)   // probably means user cancelled the menu
    00722                 goto Exit_WinHelp;
    00723         }
    00724
    00725         /*
    00726          * Create WM_WINHELP's HLP data structure for HELP_SETPOPUP_POS
    00727          */
    00728         if (!(lpHlp = HFill(lpszHelp, HELP_SETPOPUP_POS,
    00729                 MAKELONG(pwndMain->rcWindow.left, pwndMain->rcWindow.top)))) {
    00730             /*
    00731              * Remember to unlock pwndMain if needed
    00732              */
    00733             bResult = FALSE;
    00734             goto Exit_WinHelp;
    00735         }
    00736
    00737         /*
    00738          * Tell WinHelp where to put the popup.  This is different than Win95
    00739          * because we try and avoid a recursive call here.  So, we find the
    00740          * WinHlp32 window and send the HELP_SETPOPUP_POS.  No recursion.
    00741          */
    00742         hwndHelp = FindWinHelpWindow( lpwstrHelpWindowClass, dwType, TRUE);
    00743         if (hwndHelp == NULL ) {
    00744             /*
    00745              * Uable to communicate with WinHlp32.exe.
    00746              * Remember to unlock the control
    00747              */
    00748             bResult = FALSE;
    00749             goto Exit_WinHelp;
    00750         }
    00751
    00752         /*
    00753          * Send the WM_WINHELP message to WinHlp32's window.
    00754          */
    00755         lResult = SendWinHelpMessage(hwndHelp, (WPARAM)HW(pwndMain), (LPARAM)lpHlp);
    00756         LocalFree(lpHlp);
    00757         lpHlp = NULL;
    00758
    00759 /* BradG - revalidate  pwndMain? */
    00760
    00761         if (!lResult)
    00762         {
    00763             /*
    00764              * WinHlp32 couldn't process the command.  Bail out!
    00765              */
    00766             bResult = FALSE;
    00767             goto Exit_WinHelp;
    00768         }
    00769
    00770         /*
    00771          * Make HELP_WM_HELP and HELP_CONTEXTMENU act like HELP_CONTEXTPOPUP
    00772          */
    00773         uCommand = HELP_CONTEXTPOPUP;
    00774     }
    00775
    00776
    00777     if (uCommand == HELP_CONTEXTPOPUP ) {
    00778 // MapNullHlpToWindowsHlp:
    00779         /*
    00780          * If no help file was specified, use windows.hlp
    00781          */
    00782         if (lpszHelp == NULL || *lpszHelp == '\0')
    00783             lpszHelp = szDefaultHelpFileA;  // default: use windows.hlp
    00784
    00785         /*
    00786          *  WINHELP.EXE will call SetForegroundWindow on the hwnd that we pass
    00787          *  to it below.  We really want to pass the parent dialog hwnd of the
    00788          *  control so that focus will properly be restored to the dialog and
    00789          *  not the control that wants help.
    00790          */
    00791         pwndTop = GetTopLevelWindow(pwndMain);
    00792     } else {
    00793         pwndTop = pwndMain;
    00794     }
    00795
    00796
    00797     /*
    00798      * Move Help file name to a handle
    00799      */
    00800     if (!(lpHlp = HFill(lpszHelp, uCommand, dwData))) {
    00801         /*
    00802          * Can't allocate memory
    00803          */
    00804         bResult = FALSE;
    00805         goto Exit_WinHelp;
    00806     }
    00807
    00808     /*
    00809      *  Get a pointer to the help window.
    00810      */
    00811     hwndHelp = FindWinHelpWindow( lpwstrHelpWindowClass, dwType, (uCommand != HELP_QUIT));
    00812     if (hwndHelp == NULL) {
    00813         if (uCommand != HELP_QUIT)
    00814             /*
    00815              * Can't find Winhlp
    00816              */
    00817             bResult = FALSE;
    00818         goto Exit_WinHelp;
    00819     }
    00820
    00821     /*
    00822      * Send the WM_WINHELP message to WinHlp32's window
    00823      * Must ThreadLock pwndHelp AND pwndMain (because pwndMain may have been
    00824      * reassigned above).
    00825      */
    00826     SendWinHelpMessage(hwndHelp, (WPARAM)HW(pwndTop), (LPARAM)lpHlp);
    00827
    00828     /*
    00829      * Free the help info data structure (if not already free).
    00830      */
    00831 Exit_WinHelp:
    00832     if (lpHlp != NULL) {
    00833         LocalFree(lpHlp);
    00834     }
    00835
    00836     return bResult;
    00837 }
    00838
    00839
    00840
    00841
    00842
    00843
    00844
    00845
    00846
    00847
    00848
    00849
    00850 #if 0
    00851 /*
    00852  *
    00853  * Communicating with WinHelp involves using Windows SendMessage function
    00854  * to pass blocks of information to WinHelp. The call looks like.
    00855  *
    00856  * SendMessage(hwndHelp, WM_WINHELP, pidSource, pwinhlp);
    00857  *
    00858  * Where:
    00859  *
    00860  * hwndHelp - the window handle of the help application. This
    00861  * is obtained by enumerating all the windows in the
    00862  * system and sending them HELP_FIND commands. The
    00863  * application may have to load WinHelp.
    00864  * pidSource - the process id of the sending process
    00865  * pwinhlp - a pointer to a WINHLP structure
    00866  *
    00867  * The data in the handle will look like:
    00868  *
    00869  * +-------------------+
    00870  * |     cbData        |
    00871  * |    ulCommand      |
    00872  * |    hwndHost       |
    00873  * |     ulTopic       |
    00874  * |    ulReserved     |
    00875  * |   offszHelpFile   |\ - offsets measured from beginning
    00876  * | offaData          | \ of header.
    00877  * +-------------------+ /
    00878  * | Help file name    |/
    00879  * | and path          |
    00880  * +-------------------+
    00881  * | Other data        |
    00882  * |    (keyword)      |
    00883  * +-------------------+
    00884  *
    00885  * hwndMain - the handle to the main window of the application
    00886  * calling help
    00887  *
    00888  * The defined commands are:
    00889  *
    00890  * HELP_CONTEXT 0x0001 Display topic in ulTopic
    00891  * HELP_KEY 0x0101 Display topic for keyword in offabData
    00892  * HELP_QUIT 0x0002 Terminate help
    00893  *
    00894  */
    00895
    00896 BOOL WinHelpA(
    00897     HWND hwndMain,
    00898     LPCSTR lpszHelp,
    00899     UINT uCommand,
    00900     ULONG_PTR dwData)
    00901 {
    00902     LPHLP lpHlp;
    00903     BOOL fSuccess;
    00904     DWORD dwType;
    00905     HWND hwndHelp; /* Handle of help's main window */
    00906     LPWSTR lpwstr;
    00907     PWND pwndMain;
    00908
    00909     pwndMain = ValidateHwnd(hwndMain);
    00910
    00911     if ((uCommand == HELP_CONTEXTMENU) || (uCommand == HELP_CONTEXTPOPUP) ||
    00912             (uCommand == HELP_SETPOPUP_POS) || (uCommand == HELP_WM_HELP)) {
    00913         dwType = TYPE_POPUP;
    00914     } else {
    00915         dwType = TYPE_NORMAL;
    00916     }
    00917
    00918     switch (uCommand) {
    00919
    00920     case HELP_WM_HELP:
    00921         {
    00922 #if 0
    00923         // Tell WinHelp where to put the popup
    00924
    00925         if (!WinHelpA(hwndMain, lpszHelp, HELP_SETPOPUP_POS,
    00926                 MAKELONG(pwndMain->rcWindow.left, pwndMain->rcWindow.top)))
    00927             return FALSE;
    00928
    00929         /*
    00930          * Unlike HELP_CONTEXTMENU, with this command we pop up the help
    00931          * topic immediately instead of making the user go through a menu.
    00932          */
    00933 #endif
    00934
    00935         goto MapIdToHelp;
    00936
    00937         }
    00938         break;
    00939
    00940     case HELP_CONTEXTMENU:
    00941         {
    00942         int cmd;
    00943         int id;
    00944         int i;
    00945         POINT ptCur;
    00946         LPDWORD pid;
    00947
    00948         GetCursorPos(&ptCur);
    00949
    00950         if (pwndMain != NULL && pwndMain->fnid == FNID_DIALOG) {
    00951             /*
    00952              * If this is a dialog class, then one of three things has
    00953              * happened:
    00954              *
    00955              *  o   This is a disabled control
    00956              *  o   This is a static text control
    00957              *  o   This is the background of the dialog box.
    00958              *
    00959              * What we do is enumerate the child windows and see if
    00960              * any of them contain the current cursor point. If they do,
    00961              * change our window handle and continue on. Otherwise,
    00962              * return doing nothing -- we don't want context-sensitive
    00963              * help for a dialog background.
    00964              */
    00965
    00966             pwndMain = REBASEPWND(pwndMain, spwndChild);
    00967
    00968             while (pwndMain != NULL
    00969                    && PtInRect(&(pwndMain->rcWindow), ptCur)) {
    00970                 pwndMain = REBASEPWND(pwndMain, spwndNext);
    00971             }
    00972
    00973             if (pwndMain == NULL)
    00974                 return FALSE;
    00975         }
    00976
    00977         cmd = HelpMenu(hwndMain, &ptCur);
    00978
    00979         if (cmd <= 0) { // probably means user cancelled the menu
    00980             return(FALSE);      // !!! mem leak?
    00981
    00982         } else if (cmd == HELP_INDEX) {
    00983             // Search
    00984             uCommand = HELP_FINDER;
    00985         } else {
    00986 MapIdToHelp:
    00987             UserAssert(pwndMain);
    00988
    00989             // Tell WinHelp where to put the popup
    00990
    00991             if (!WinHelpA(hwndMain, lpszHelp, HELP_SETPOPUP_POS,
    00992                     MAKELONG(pwndMain->rcWindow.left, pwndMain->rcWindow.top)))
    00993                 return FALSE;
    00994             id = LOWORD(pwndMain->spmenu);   // get control id
    00995
    00996             pid = (LPDWORD) dwData;
    00997
    00998             if (id == -1) {
    00999                 // static control?
    01000                 PWND pwndCtrl;
    01001                 int cAttempts = 0;
    01002
    01003                 // For non-id controls (typically static controls), step
    01004                 // through to the next tab item. Keep finding the next tab
    01005                 // item until we find a valid id, or we have tried
    01006                 // MAX_ATTEMPTS times.
    01007                 do {
    01008                     pwndCtrl = _GetNextDlgTabItem(REBASEPWND(pwndMain, spwndParent),
    01009                                     pwndMain, FALSE);
    01010
    01011                     // hwndCtrl will be NULL if hwndMain doesn't have a parent,
    01012                     // or if there are no tab stops.
    01013
    01014                     if (!pwndCtrl) {
    01015                         return(FALSE);
    01016                     }
    01017
    01018
    01019
    01020                     id = LOWORD(pwndCtrl->spmenu);
    01021                 }
    01022                 while ((id == -1) && (++cAttempts < MAX_ATTEMPTS));
    01023             }
    01024
    01025             // Find the id value in array of id/help context values
    01026
    01027             for (i = 0; pid[i]; i += 2) {
    01028                 if ((int) pid[i] == id)
    01029                     break;
    01030             }
    01031
    01032             if (!pid[i]) {
    01033
    01034
    01035                 lpszHelp = szDefaultHelpFileA;  // switch to windows.hlp
    01036
    01037                 switch (id) {
    01038                     case IDOK:
    01039                         dwData = IDH_OK;
    01040                         break;
    01041
    01042                     case IDCANCEL:
    01043                         dwData = IDH_CANCEL;
    01044                         break;
    01045
    01046                     case IDHELP:
    01047                         dwData = IDH_HELP;
    01048                         break;
    01049
    01050                     default:
    01051                         dwData = IDH_MISSING_CONTEXT;
    01052                 }
    01053             } else {
    01054                 dwData = pid[i + 1];
    01055                 if (dwData == (DWORD) -1) {
    01056                     return TRUE;   // caller doesn't want help after all
    01057                 }
    01058             }
    01059
    01060             uCommand = HELP_CONTEXTPOPUP;
    01061         }
    01062
    01063         // If no help file was specified, use windows.hlp
    01064
    01065         if (lpszHelp == NULL || *lpszHelp == '\0')
    01066             lpszHelp = szDefaultHelpFileA;  // default: use windows.hlp
    01067         }
    01068     }
    01069
    01070     /*
    01071      * Move Help file name to a handle
    01072      */
    01073     if (!(lpHlp = HFill(lpszHelp, uCommand, dwData)))
    01074         return FALSE;
    01075
    01076     /*
    01077      * Pass it on to WinHelp32
    01078      */
    01079     fSuccess = TRUE;
    01080     if (dwType == TYPE_POPUP)
    01081         lpwstr = szMS_POPUPHELP;
    01082     else
    01083         lpwstr = szMS_WINHELP;
    01084
    01085     if ((hwndHelp = FindWindowEx(NULL, NULL, lpwstr, NULL)) == NULL) {
    01086         if (uCommand == HELP_QUIT)
    01087             fSuccess = TRUE;
    01088
    01089         /*
    01090          * Can't find it --> launch it
    01091          */
    01092         if (!LaunchHelp(dwType) ||
    01093                 (hwndHelp = FindWindowEx(NULL, NULL, lpwstr, NULL)) == NULL)
    01094             fSuccess = FALSE;
    01095     }
    01096
    01097     if (hwndHelp != NULL)
    01098         SendMessage(hwndHelp, WM_WINHELP, (DWORD)hwndMain, (LONG)lpHlp);
    01099
    01100     LocalFree(lpHlp);
    01101
    01102     return fSuccess;
    01103 }
    01104 #endif
    01105
    01106
    01107
    01108 /***************************************************************************\
    01109 * WinHelpW
    01110 *
    01111 * Calls WinHelpA after doing any necessary translation.
    01112 * Our help engine is ASCII only.
    01113 *
    01114 \***************************************************************************/
    01115
    01116 BOOL WinHelpW(
    01117     HWND hwndMain,
    01118     LPCWSTR lpwszHelp,
    01119     UINT uCommand,
    01120     ULONG_PTR dwData)
    01121 {
    01122     BOOL fSuccess = FALSE;
    01123     LPSTR lpAnsiHelp = NULL;
    01124     LPSTR lpAnsiKey = NULL;
    01125     PMULTIKEYHELPA pmkh = NULL;
    01126     PHELPWININFOA phwi = NULL;
    01127     NTSTATUS Status;
    01128
    01129
    01130     /*
    01131      * First convert the string.
    01132      */
    01133     if (lpwszHelp != NULL &&
    01134             !WCSToMB(lpwszHelp, -1, &lpAnsiHelp, -1, TRUE)) {
    01135         return FALSE;
    01136     }
    01137
    01138     /*
    01139      * Then convert dwData if needed
    01140      */
    01141     switch (uCommand) {
    01142     case HELP_MULTIKEY:
    01143         if (!WCSToMB(((PMULTIKEYHELPW)dwData)->szKeyphrase, -1, &lpAnsiKey,
    01144                 -1, TRUE)) {
    01145             goto FreeAnsiHelp;
    01146         }
    01147
    01148         pmkh = (PMULTIKEYHELPA)LocalAlloc(LPTR,
    01149                 sizeof(MULTIKEYHELPA) + strlen(lpAnsiKey));
    01150         if (pmkh == NULL) {
    01151             goto FreeAnsiKeyAndHelp;
    01152         }
    01153
    01154         pmkh->mkSize = sizeof(MULTIKEYHELPA) + strlen(lpAnsiKey);
    01155         Status = RtlUnicodeToMultiByteN((LPSTR)&pmkh->mkKeylist, sizeof(CHAR),
    01156                 NULL, (LPWSTR)&((PMULTIKEYHELPW)dwData)->mkKeylist,
    01157                 sizeof(WCHAR));
    01158         strcpy(pmkh->szKeyphrase, lpAnsiKey);
    01159         if (!NT_SUCCESS(Status)) {
    01160             goto FreeAnsiKeyAndHelp;
    01161         }
    01162
    01163         dwData = (ULONG_PTR)pmkh;
    01164         break;
    01165
    01166     case HELP_SETWINPOS:
    01167         if (!WCSToMB(((PHELPWININFOW)dwData)->rgchMember, -1, &lpAnsiKey,
    01168                 -1, TRUE)) {
    01169             goto FreeAnsiKeyAndHelp;
    01170         }
    01171
    01172         phwi = (PHELPWININFOA)LocalAlloc(LPTR, ((PHELPWININFOW)dwData)->wStructSize);
    01173         if (phwi == NULL) {
    01174             goto FreeAnsiKeyAndHelp;
    01175         }
    01176
    01177         *phwi = *((PHELPWININFOA)dwData);   // copies identical parts
    01178         strcpy(phwi->rgchMember, lpAnsiKey);
    01179         dwData = (ULONG_PTR)phwi;
    01180         break;
    01181
    01182     case HELP_KEY:
    01183     case HELP_PARTIALKEY:
    01184     case HELP_COMMAND:
    01185         if (!WCSToMB((LPCTSTR)dwData, -1, &lpAnsiKey, -1, TRUE)) {
    01186             goto FreeAnsiKeyAndHelp;
    01187         }
    01188
    01189         dwData = (ULONG_PTR)lpAnsiKey;
    01190         break;
    01191     }
    01192
    01193     /*
    01194      * Call the Ansi version
    01195      */
    01196     fSuccess = WinHelpA(hwndMain, lpAnsiHelp, uCommand, dwData);
    01197
    01198     if (pmkh) {
    01199         LocalFree(pmkh);
    01200     }
    01201
    01202     if (phwi) {
    01203         LocalFree(phwi);
    01204     }
    01205
    01206 FreeAnsiKeyAndHelp:
    01207     if (lpAnsiKey) {
    01208         LocalFree(lpAnsiKey);
    01209     }
    01210
    01211
    01212 FreeAnsiHelp:
    01213     if (lpAnsiHelp)
    01214         LocalFree(lpAnsiHelp);
    01215
    01216     return fSuccess;
    01217 }