/*****************************************************************************
*                                                                            *
*  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"

//
// We define certain things here because including mvdm\inc\*.h files here
// will lead to endless mess.
//

#define WM_WINHELP 0x38
DWORD API NotifyWow(WORD, LPBYTE);
#define FUN_WINHELP         171 //

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;
WORD               usCommand;
DWORD              ulData;
{
  register HANDLE  hHlp;
  DWORD            dwHelpPid;           /* loword is hwndHelp             */
                                        /* hiword TRUE if hwndHelp is of this process */

  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(FUN_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(FUN_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);
}
