You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
891 lines
26 KiB
891 lines
26 KiB
/**************************************************************************\
|
|
* Module Name: help.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* History:
|
|
* 23-May-95 BradG Created to consolidate client-side help routines.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#define MAX_ATTEMPTS 5 // maximum -1 id controls to search through
|
|
char szDefaultHelpFileA[] = "windows.hlp";
|
|
|
|
CONST WCHAR szEXECHELP[] = TEXT("\\winhlp32.exe");
|
|
CONST WCHAR szMS_WINHELP[] = L"MS_WINHELP"; // Application class
|
|
CONST WCHAR szMS_POPUPHELP[] = L"MS_POPUPHELP"; // Popup class
|
|
CONST WCHAR szMS_TCARDHELP[] = L"MS_TCARDHELP"; // Training card class
|
|
|
|
CONST WCHAR gawcWinhelpFlags[] = {
|
|
L'x', // Execute WinHelp as application help
|
|
L'p', // Execute WinHelp as a popup
|
|
L'c', // Execute WinHelp as a training card
|
|
};
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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.
|
|
*/
|
|
cb = sizeof(HLP) + cbStr + cbData;
|
|
|
|
/*
|
|
* Get data block.
|
|
*/
|
|
if ((phlp = (LPHLP)UserLocalAlloc(HEAP_ZERO_MEMORY, cb)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Fill in info.
|
|
*/
|
|
phlp->cbData = (WORD)cb;
|
|
phlp->usCommand = (WORD)ulCommand;
|
|
|
|
/*
|
|
* Fill in file name.
|
|
*/
|
|
if (lpszHelp) {
|
|
phlp->offszHelpFile = sizeof(HLP);
|
|
strcpy((LPSTR)(phlp + 1), lpszHelp);
|
|
}
|
|
|
|
/*
|
|
* Fill in data
|
|
*/
|
|
switch (bType) {
|
|
case HIBYTE(HELP_HB_STRING):
|
|
if (cbData) {
|
|
phlp->offabData = (WORD)(sizeof(HLP) + cbStr);
|
|
strcpy((LPSTR)phlp + phlp->offabData, (LPSTR)ulData);
|
|
}
|
|
break;
|
|
|
|
case HIBYTE(HELP_HB_STRUCT):
|
|
if (cbData) {
|
|
phlp->offabData = (WORD)(sizeof(HLP) + cbStr);
|
|
RtlCopyMemory((LPBYTE)phlp + phlp->offabData,
|
|
(PVOID)ulData,
|
|
*((int*)ulData));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
phlp->ulTopic = ulData;
|
|
break;
|
|
}
|
|
|
|
return phlp;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* LaunchHelp
|
|
*
|
|
* This function launches the WinHlp32 executable with the correct command
|
|
* line arguments.
|
|
*
|
|
* History:
|
|
* 03/23/1995 BradG YAP of new changes from Win95
|
|
* 03/01/2002 JasonSch Changed to only look for winhlp32.exe in %windir%.
|
|
\***************************************************************************/
|
|
BOOL LaunchHelp(
|
|
DWORD dwType)
|
|
{
|
|
WCHAR *pwszPath, wszCommandLine[16];
|
|
BOOL bRet;
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
ULONG cChars;
|
|
|
|
/*
|
|
* Return value of GetSystemWindowsDirectory does not include the
|
|
* terminating NULL, so + 1.
|
|
*/
|
|
cChars = GetSystemWindowsDirectoryW(NULL, 0) + 1;
|
|
pwszPath = UserLocalAlloc(0, (cChars + ARRAY_SIZE(szEXECHELP)) * sizeof(WCHAR));
|
|
if (pwszPath == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
GetSystemWindowsDirectoryW(pwszPath, cChars);
|
|
wcscat(pwszPath, szEXECHELP);
|
|
wsprintf(wszCommandLine, L"%ws -%wc", szEXECHELP + 1, gawcWinhelpFlags[dwType]);
|
|
|
|
/*
|
|
* Launch winhelp.
|
|
*/
|
|
RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
StartupInfo.wShowWindow = SW_SHOW;
|
|
StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
|
|
|
|
bRet = CreateProcessW(pwszPath,
|
|
wszCommandLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NORMAL_PRIORITY_CLASS,
|
|
NULL,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInformation);
|
|
if (bRet) {
|
|
WaitForInputIdle(ProcessInformation.hProcess, 10000);
|
|
NtClose(ProcessInformation.hProcess);
|
|
NtClose(ProcessInformation.hThread);
|
|
}
|
|
|
|
UserLocalFree(pwszPath);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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 takes 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 = FindWindowW(lpwstrHelpWindowClass, NULL)) == NULL) {
|
|
/*
|
|
* Can't find help, or not enough memory to load help.
|
|
* hwndHelp will be NULL at this point.
|
|
*/
|
|
RIPMSG0(RIP_WARNING, "LaunchHelp or FindWindow failed.");
|
|
}
|
|
}
|
|
}
|
|
|
|
return hwndHelp;
|
|
}
|
|
|
|
|
|
/*
|
|
* HWND version of Enumeration function to find 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,
|
|
LPCSTR lpszHelp,
|
|
UINT uCommand,
|
|
ULONG_PTR dwData)
|
|
{
|
|
LPCWSTR 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 (TEST_SRVIF(SRVIF_LASTRITWASKEYBOARD)) {
|
|
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;
|
|
|
|
/*
|
|
* 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) &&
|
|
IS_BUTTON(pwndMain)) {
|
|
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;
|
|
|
|
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)))) {
|
|
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);
|
|
UserLocalFree(lpHlp);
|
|
lpHlp = NULL;
|
|
|
|
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) {
|
|
/*
|
|
* 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) {
|
|
UserLocalFree(lpHlp);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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 = UserLocalAlloc(HEAP_ZERO_MEMORY,
|
|
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 = UserLocalAlloc(HEAP_ZERO_MEMORY,
|
|
((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) {
|
|
UserLocalFree(pmkh);
|
|
}
|
|
|
|
if (phwi) {
|
|
UserLocalFree(phwi);
|
|
}
|
|
|
|
FreeAnsiKeyAndHelp:
|
|
if (lpAnsiKey) {
|
|
UserLocalFree(lpAnsiKey);
|
|
}
|
|
|
|
|
|
FreeAnsiHelp:
|
|
if (lpAnsiHelp) {
|
|
UserLocalFree(lpAnsiHelp);
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|