327 lines
11 KiB
C
327 lines
11 KiB
C
/*****************************************************************************
|
|
* *
|
|
* HELPCALL.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1989. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Program Description: Sample interface to windows help *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Revision History: Created by RKB 11/30/88 *
|
|
* Revised to new API 1/12/88 (RKB) *
|
|
* Added to USER 3/28/89 (BG) *
|
|
* Slight update 6/15/89 (BG) *
|
|
* Clean ugly code 10/30/89 (BG) *
|
|
* GlobalFree if QUIT 1/26/90 (CRC) *
|
|
* *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#define NO_REDEF_SENDMESSAGE
|
|
#include "user.h"
|
|
#define _WINGDIP_ // We need to define these to prevent
|
|
#include "wowcmpat.h" // redefinition of the GACF flags
|
|
|
|
#define WM_WINHELP 0x38
|
|
DWORD API NotifyWow(WORD, LPBYTE);
|
|
|
|
BOOL API
|
|
Win32WinHelp(
|
|
HWND hwndMain,
|
|
LPCSTR lpszHelp,
|
|
UINT usCommand,
|
|
DWORD ulData
|
|
);
|
|
|
|
DWORD WINAPI
|
|
GetWOWCompatFlagsEx(
|
|
void
|
|
);
|
|
|
|
|
|
/* This must match its counterpart in mvdm\inc\wowusr.h */
|
|
#define NW_WINHELP 6 // Internal
|
|
|
|
WORD msgWinHelp = 0;
|
|
char CODESEG szMS_WINHELP[] = "MS_WINHELP";
|
|
|
|
|
|
/*
|
|
|
|
Communicating with WinHelp involves using Windows SendMessage() function
|
|
to pass blocks of information to WinHelp. The call looks like.
|
|
|
|
SendMessage(hwndHelp, msgWinHelp, hwndMain, (LONG)hHlp);
|
|
|
|
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.
|
|
msgWinHelp - the value obtained from a RegisterWindowMessage()
|
|
szWINHELP
|
|
hwndMain - the handle to the main window of the application
|
|
calling help
|
|
hHlp - a handle to a block of data with a HLP structure
|
|
at it head.
|
|
|
|
The data in the handle will look like:
|
|
|
|
+-------------------+
|
|
| cbData |
|
|
| usCommand |
|
|
| ulTopic |
|
|
| ulReserved |
|
|
| offszHelpFile |\ - offsets measured from beginning
|
|
/ | offaData | \ of header.
|
|
/ +-------------------| /
|
|
/ | Help file name |/
|
|
\ | and path |
|
|
\ +-------------------+
|
|
\ | Other data |
|
|
| (keyword) |
|
|
+-------------------+
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
/*******************
|
|
**
|
|
** Name: HFill
|
|
**
|
|
** Purpose: Builds a data block for communicating with help
|
|
**
|
|
** Arguments: lpszHelp - pointer to the name of the help file to use
|
|
** usCommand - command being set to help
|
|
** ulData - data for the command
|
|
**
|
|
** Returns: a handle to the data block or hNIL if the the
|
|
** block could not be created.
|
|
**
|
|
*******************/
|
|
|
|
|
|
HANDLE HFill(LPCSTR lpszHelp, WORD usCommand, DWORD ulData)
|
|
{
|
|
WORD cb; /* Size of the data block */
|
|
HANDLE hHlp; /* Handle to return */
|
|
BYTE bHigh; /* High byte of usCommand */
|
|
LPHLP qhlp; /* Pointer to data block */
|
|
/* Calculate size */
|
|
if (lpszHelp)
|
|
cb = sizeof(HLP) + lstrlen(lpszHelp) + 1;
|
|
else
|
|
cb = sizeof(HLP);
|
|
|
|
bHigh = (BYTE)HIBYTE(usCommand);
|
|
|
|
if (bHigh == 1)
|
|
cb += lstrlen((LPSTR)ulData) + 1;
|
|
else if (bHigh == 2)
|
|
cb += *((int far *)ulData);
|
|
|
|
/* Get data block */
|
|
if (!(hHlp = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD)cb)))
|
|
return NULL;
|
|
|
|
if (!(qhlp = (LPHLP)GlobalLock(hHlp)))
|
|
{
|
|
GlobalFree(hHlp);
|
|
return NULL;
|
|
}
|
|
|
|
qhlp->cbData = cb; /* Fill in info */
|
|
qhlp->usCommand = usCommand;
|
|
qhlp->ulReserved = 0;
|
|
if (lpszHelp)
|
|
{
|
|
qhlp->offszHelpFile = sizeof(HLP);
|
|
lstrcpy((LPSTR)(qhlp+1), lpszHelp);
|
|
}
|
|
else
|
|
qhlp->offszHelpFile = 0;
|
|
|
|
switch(bHigh)
|
|
{
|
|
case 0:
|
|
qhlp->offabData = 0;
|
|
qhlp->ulTopic = ulData;
|
|
break;
|
|
case 1:
|
|
qhlp->offabData = sizeof(HLP) + lstrlen(lpszHelp) + 1;
|
|
lstrcpy((LPSTR)qhlp + qhlp->offabData, (LPSTR)ulData);
|
|
break;
|
|
case 2:
|
|
qhlp->offabData = sizeof(HLP) + lstrlen(lpszHelp) + 1;
|
|
LCopyStruct((LPSTR)ulData, (LPSTR)qhlp + qhlp->offabData, *((int far *)ulData));
|
|
break;
|
|
}
|
|
|
|
GlobalUnlock(hHlp);
|
|
return hHlp;
|
|
}
|
|
|
|
|
|
|
|
char CODESEG szEXECHELP[] = "\\WINHELP -x";
|
|
|
|
BOOL _fastcall LaunchHelper(LPSTR lpfile)
|
|
{
|
|
int len;
|
|
|
|
len = lstrlen(lpfile);
|
|
|
|
if (lpfile[len-1]=='\\')
|
|
/* Are we at the root?? If so, skip over leading backslash in text
|
|
* string. */
|
|
lstrcat(lpfile, szEXECHELP+1);
|
|
else
|
|
lstrcat(lpfile, szEXECHELP);
|
|
|
|
return ((HINSTANCE)WinExec(lpfile, SW_SHOW) > HINSTANCE_ERROR);
|
|
}
|
|
|
|
|
|
BOOL LaunchHelp(VOID)
|
|
{
|
|
char szFile[128];
|
|
|
|
/* Search in windows directory */
|
|
GetWindowsDirectory(szFile, sizeof(szFile));
|
|
if (LaunchHelper(szFile))
|
|
return(TRUE);
|
|
|
|
/* Search system directory */
|
|
GetSystemDirectory(szFile, sizeof(szFile));
|
|
if (LaunchHelper(szFile))
|
|
return(TRUE);
|
|
|
|
/* Last ditch: simply let dos do it */
|
|
lstrcpy(szFile, szEXECHELP+1);
|
|
return ((HINSTANCE)WinExec(szFile, SW_SHOW) > HINSTANCE_ERROR);
|
|
}
|
|
|
|
|
|
/*******************
|
|
**
|
|
** Name: WinHelp
|
|
**
|
|
** Purpose: Displays help
|
|
**
|
|
** Arguments:
|
|
** hwndMain handle to main window of application
|
|
** lpszHelp path (if not current directory) and file
|
|
** to use for help topic.
|
|
** usCommand Command to send to help
|
|
** ulData Data associated with command:
|
|
** HELP_QUIT - no data (undefined)
|
|
** HELP_LAST - no data (undefined)
|
|
** HELP_CONTEXT - context number to display
|
|
** HELP_KEY - string ('\0' terminated)
|
|
** use as keyword to topic
|
|
** to display
|
|
** HELP_FIND - no data (undefined)
|
|
**
|
|
** Returns: TRUE iff success
|
|
**
|
|
*******************/
|
|
|
|
BOOL API IWinHelp(hwndMain, lpszHelp, usCommand, ulData)
|
|
HWND hwndMain;
|
|
LPCSTR lpszHelp;
|
|
UINT usCommand;
|
|
DWORD ulData;
|
|
{
|
|
register HANDLE hHlp;
|
|
DWORD dwHelpPid; /* loword is hwndHelp */
|
|
/* hiword TRUE if hwndHelp is of this process */
|
|
DWORD dwWOWCompatFlagsEx;
|
|
|
|
|
|
/* RAID BUG 394455
|
|
Some apps have problems loading their help files with 16 bit winhelp. Hard coded paths,
|
|
32 bit helper dlls, etc. These issues can be fixed by redirecting the call to winhelp32.
|
|
Check to see if the compatibility bit has been set for this app. */
|
|
dwWOWCompatFlagsEx = GetWOWCompatFlagsEx();
|
|
|
|
if (dwWOWCompatFlagsEx & WOWCFEX_USEWINHELP32) {
|
|
return Win32WinHelp(hwndMain, lpszHelp, usCommand, ulData);
|
|
}
|
|
|
|
if (msgWinHelp == 0) {
|
|
|
|
/* Register private WinHelp message for communicating to WinHelp via
|
|
* WinHelp api.
|
|
*/
|
|
char static CODESEG szWM_WINHELP[] = "WM_WINHELP";
|
|
msgWinHelp = RegisterWindowMessage(szWM_WINHELP);
|
|
}
|
|
|
|
/* Move Help file name to a handle */
|
|
if (!(hHlp = HFill(lpszHelp, usCommand, ulData)))
|
|
return(FALSE);
|
|
|
|
if ((dwHelpPid = (DWORD)NotifyWow(NW_WINHELP, szMS_WINHELP)) == (DWORD)NULL)
|
|
{
|
|
if (usCommand == HELP_QUIT) /* Don't bother to load HELP just to*/
|
|
{
|
|
GlobalFree(hHlp);
|
|
return(TRUE);
|
|
}
|
|
|
|
/* Can't find it --> launch it */
|
|
if (!LaunchHelp() || ((dwHelpPid = (DWORD)NotifyWow(NW_WINHELP, szMS_WINHELP)) == (DWORD)NULL))
|
|
{
|
|
/* Can't find help, or not enough memory to load help.*/
|
|
GlobalFree(hHlp);
|
|
return(FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
// if winhelp.exe was launched from this process, normal sendmessage else
|
|
// we need to thunk the data across WOWVDM processes and the format is
|
|
// msg = WM_WINHELP, a private msg
|
|
// wparam = 0 instead of hwndMain, (note 1)
|
|
// lparam = LPHLP
|
|
//
|
|
// note 1: winhelp, calls GetWindowWord(wParam, GWW_HINSTANCE) when it receives HELP_QUIT
|
|
// command. If this matches a value in its table and is the only registered instance
|
|
// winhelp will close - this is quite ok undernormal circumstances (just one WOWVDM)
|
|
// but under multiple WOWVDM, numeric value of hinstances could be same for different
|
|
// hwnds.
|
|
//
|
|
// So we workaround this by passing a NULL hwnd in wParam and by not sending HELP_QUIT
|
|
// message - which effectively implies that WinHelp will close only if there are no
|
|
// references to it from the same WOWVDM (as itself).
|
|
//
|
|
// This is the best compromise I could comeup with for running "only one WinHelp for all
|
|
// WOWVDMs".
|
|
//
|
|
// - nanduri
|
|
|
|
if (HIWORD(dwHelpPid)) {
|
|
SendMessage((HWND)LOWORD(dwHelpPid), msgWinHelp, (WPARAM)hwndMain, MAKELPARAM(hHlp, 0));
|
|
}
|
|
else {
|
|
if (usCommand != HELP_QUIT) {
|
|
SendMessage((HWND)LOWORD(dwHelpPid), WM_WINHELP, (WPARAM)0, (LPARAM)GlobalLock(hHlp));
|
|
GlobalUnlock(hHlp);
|
|
}
|
|
}
|
|
|
|
GlobalFree(hHlp);
|
|
return(TRUE);
|
|
}
|