/****************************************************************************/
/* WebWorks Help API Platform Functions                                     */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

/*$Id: wwhwin.c,v 1.21 2006/11/29 19:24:38 allums Exp $*/


#include "wwhelp/wwhplat.h"

#include "wwhelp/wwhapi.h"
#include "wwhelp/wwhurl.h"
#include "wwhelp/wwhwin.h"
#include <wtypes.h>
#include <ddeml.h>
#include <winreg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define CINTERFACE  1
#define COBJMACROS  1

#include <exdisp.h>


/****************************************************************************/
/* Platform specific datatypes, variables, and function prototypes          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

#ifdef __cplusplus
extern "C"
{
#endif

extern char* sgpWWHelp_Base;

#define WWHELP_TEMPFILENAME_MAXLENGTH    4096
#define WWHELP_DDECOMMAND_MAXLENGTH      255
#define WWHELP_DDECOMMAND_TIMEOUT        10000  /* 10000 milliseconds equals 10 seconds */

static DWORD  sgDDEInstance = 0;
static char   sgWWHAPIService[1024];
static char   sgWWHelp_Platform_TempFilename[WWHELP_TEMPFILENAME_MAXLENGTH];
static GUID   sgShellWindowsGUID;

int  private_WWHelp_DefineGUID(const char* pParamGUIDString,
                                     GUID* pParamGUID);

int  private_WWHelp_Platform_BSTRToCString(const BSTR   ParamBSTR,
                                                 char** hParamCString);

int  private_WWHelp_Platform_CharPtrToBSTR(const char*   pParamString,
                                           const size_t  ParamStringLength,
                                                 BSTR*   pParamBSTR);

int  private_WWHelp_Platform_SetLocationID(WWHelp_BrowserInfo* pParamBrowserInfo);

void  private_WWHelp_Platform_ToLowerCase(char* pParamString);

int  private_WWHelp_Platform_FindWWHelpBrowser(      IShellWindows* pParamIShellWindows,
                                               const char*          pParamURLFragment,
                                                     IWebBrowser2** hParamMatchingWebBrowser);

int  private_WWHelp_Platform_COMSend(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                     const char*               pParamURL,
                                     const size_t              ParamURLLength);

HDDEDATA FAR PASCAL  private_WWHelp_Platform_DDECallback(UINT      ParamType,
                                                         UINT      ParamFormat,
                                                         HCONV     ParamHCONV,
                                                         HSZ       ParamHSZ1,
                                                         HSZ       ParamHSZ2,
                                                         HDDEDATA  ParamData,
                                                         DWORD     ParamDW1,
                                                         DWORD     ParamDW2);

int  private_WWHelp_Platform_DDESend_BuildDDECommand(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                                     const char*               pParamURL,
                                                     const size_t              ParamURLLength,
                                                           char*               pParamDDECommand);

int  private_WWHelp_Platform_DDESend_WWW_OpenURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                                 const char*               pParamURL,
                                                 const size_t              ParamURLLength);

int  private_WWHelp_Platform_DDESend_WWW_Activate(const WWHelp_BrowserInfo* pParamBrowserInfo);

int  private_WWHelp_Platform_DDESend(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                     const char*               pParamURL,
                                     const size_t              ParamURLLength);


/****************************************************************************/
/* Visual Basic callable interface                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

WWHEXPORT long __stdcall  WWHelp_VB_Startup(const char* pParamBaseURL);
WWHEXPORT long __stdcall  WWHelp_VB_Shutdown();

WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelp(const char* pParamContext,
                                                const char* pParamTopic);
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpWithNavigation(const char* pParamContext,
                                                              const char* pParamTopic);
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpWithContents(const char* pParamContext,
                                                            const char* pParamTopic);
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpWithIndex(const char* pParamContext,
                                                         const char* pParamTopic);
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpWithSearch(const char* pParamContext,
                                                          const char* pParamTopic);
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpWithFavorites(const char* pParamContext,
                                                             const char* pParamTopic);
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpNavigation();
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpContents();
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpIndex();
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpSearch();
WWHEXPORT long __stdcall  WWHelp_VB_DisplayHelpFavorites();

#ifdef __cplusplus
}
#endif


/****************************************************************************/
/* private_WWHelp_DefineGUID                                                */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int  private_WWHelp_DefineGUID(const char* pParamGUIDString,
                                     GUID* pParamGUID)
{
  int             ReturnCode = -1;
  int             Index;
  int             Offset;
  char            ByteString[3];
  unsigned char   GUIDValues[16];
  unsigned long   DataLong;
  unsigned short  DataShort;


  /* Take GUID strings of the format        */
  /* "9BA05972-F6A8-11CF-A442-00A0C90A8F39" */
  /* and parse them into the GUID struct    */
  /*                                        */
  if ((pParamGUIDString != NULL) &&
      (pParamGUID != NULL) &&
      (strlen(pParamGUIDString) == 36))
  {
    /* Parse GUID format into an array of values   */
    /*                                             */
    Offset = 0;
    for (Index = 0 ; Index < 16 ; Index++)
    {
      /* Set offset */
      /*            */
      switch (Index)
      {
        case 4:
        case 6:
        case 8:
        case 10:
          Offset++;
          break;
      }

      /* Put value into array */
      /*                       */
      memcpy(ByteString, &(pParamGUIDString[Index * 2 + Offset]), 2);
      ByteString[2] = '\0';
      sscanf(ByteString, "%x", &DataLong);
      GUIDValues[Index] = (unsigned char)DataLong;
    }

    /* Set GUID struct using values from array */
    /*                                         */
    pParamGUID->Data1 = 0;
    DataLong = GUIDValues[0]; pParamGUID->Data1 = (pParamGUID->Data1 | (DataLong << 24));
    DataLong = GUIDValues[1]; pParamGUID->Data1 = (pParamGUID->Data1 | (DataLong << 16));
    DataLong = GUIDValues[2]; pParamGUID->Data1 = (pParamGUID->Data1 | (DataLong << 8));
    DataLong = GUIDValues[3]; pParamGUID->Data1 = (pParamGUID->Data1 | (DataLong << 0));

    pParamGUID->Data2 = 0;
    DataShort = GUIDValues[4]; pParamGUID->Data2 = (pParamGUID->Data2 | (DataShort << 8));
    DataShort = GUIDValues[5]; pParamGUID->Data2 = (pParamGUID->Data2 | (DataShort << 0));

    pParamGUID->Data3 = 0;
    DataShort = GUIDValues[6]; pParamGUID->Data3 = (pParamGUID->Data3 | (DataShort << 8));
    DataShort = GUIDValues[7]; pParamGUID->Data3 = (pParamGUID->Data3 | (DataShort << 0));

    for (Index = 0 ; Index < 8 ; Index++)
    {
      pParamGUID->Data4[Index] = GUIDValues[8 + Index];
    }

    /* Success! */
    /*          */
    ReturnCode = 0;
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_BSTRToCString                                    */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_BSTRToCString(const BSTR   ParamBSTR,
                                            char** hParamCString)
{
  int    ReturnCode = -1;
  ULONG  BSTRLength;
  ULONG  CStringLength;
  ULONG  CharactersWritten;


  BSTRLength = SysStringLen(ParamBSTR);
  if (BSTRLength > 0)
  {
    /* Determine size of C string */
    /*                            */
    CStringLength = WideCharToMultiByte(CP_ACP, 0, ParamBSTR, BSTRLength,
                                        NULL, 0, NULL, NULL);
    if (CStringLength > 0)
    {
      *hParamCString = (char*)malloc(CStringLength + 1);
      if (*hParamCString != NULL)
      {
        /* Perform conversion */
        /*                    */
        CharactersWritten = WideCharToMultiByte(CP_ACP, 0, ParamBSTR, BSTRLength,
                                                *hParamCString, CStringLength,
                                                NULL, NULL);
        if (CharactersWritten == CStringLength)
        {
          /* NULL terminate C string */
          /*                         */
          (*hParamCString)[CStringLength] = '\0';

          /* Success! */
          /*          */
          ReturnCode = 0;
        }
        else
        {
          free(*hParamCString);
          *hParamCString = NULL;
        }
      }
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_CharPtrToBSTR                                    */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_CharPtrToBSTR(const char*   pParamString,
                                      const size_t  ParamStringLength,
                                            BSTR*   pParamBSTR)
{
  int    ReturnCode = -1;
  ULONG  BSTRLength;
  ULONG  CharactersWritten;


  if (ParamStringLength > 0)
  {
    /* Determine size of BSTR string */
    /*                               */
    BSTRLength = MultiByteToWideChar(CP_ACP, 0,
                                     pParamString,
                                     (int)ParamStringLength,
                                     NULL, 0);
    if (BSTRLength > 0)
    {
      *pParamBSTR = SysAllocStringLen(NULL, BSTRLength);
      if (*pParamBSTR != NULL)
      {
        /* Perform conversion */
        /*                    */
        CharactersWritten = MultiByteToWideChar(CP_ACP, 0,
                                                pParamString,
                                                (int)ParamStringLength,
                                                *pParamBSTR, BSTRLength);
        if (CharactersWritten == BSTRLength)
        {
          /* Success! */
          /*          */
          ReturnCode = 0;
        }
        else
        {
          SysFreeString(*pParamBSTR);
          *pParamBSTR = NULL;
        }
      }
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_SetLocationURL                                   */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_SetLocationID(WWHelp_BrowserInfo* pParamBrowserInfo)
{
  int   ReturnCode = -1;
  char* pPastProtocol;


  /* Record location ID so that open browsers pointing */
  /* to the same help set can be reused.               */
  /*                                                   */
  if (sgpWWHelp_Base != NULL)
  {
    /* Release previous location ID */
    /*                              */
    if (pParamBrowserInfo->mpLocationID != NULL)
    {
      free(pParamBrowserInfo->mpLocationID);
      pParamBrowserInfo->mpLocationID = NULL;
    }

    /* Locate just past base URL protocol */
    /*                                    */
    pPastProtocol = strstr(sgpWWHelp_Base, ":");
    pPastProtocol++;
    while (*pPastProtocol == '/')
    {
      pPastProtocol++;
    }

    /* Set location ID */
    /*                 */
    pParamBrowserInfo->mpLocationID = (char*)malloc(strlen(pPastProtocol) + 1);
    if (pParamBrowserInfo->mpLocationID != NULL)
    {
      strcpy(pParamBrowserInfo->mpLocationID, pPastProtocol);

      /* Success! */
      /*          */
      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_ToLowerCase                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

void
private_WWHelp_Platform_ToLowerCase(char* pParamString)
{
  size_t  MaxIndex;
  size_t  Index;


  for (MaxIndex = strlen(pParamString), Index = 0 ; Index < MaxIndex ; Index++)
  {
    pParamString[Index] = (char)tolower(pParamString[Index]);
  }
}


/****************************************************************************/
/* private_WWHelp_Platform_FindWWHelpBrowser                                */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_FindWWHelpBrowser(      IShellWindows* pParamIShellWindows,
                                          const char*          pParamURLFragment,
                                                IWebBrowser2** hParamMatchingWebBrowser)
{
  int           ReturnCode = -1;
  HRESULT       Error;
  long          NumberOfWindows;
  long          WindowNumber;
  VARIANT       WindowSelector;
  IDispatch*    pIShellWindow;
  IWebBrowser2* pIWebBrowser2;
  BSTR          WebBrowserFullName;
  char*         pFullName;
  BSTR          WebBrowserLocationURL;
  char*         pLocationURL;
  char*         pURLFragmentLowerCase;
  char*         pLocationURLLowerCase;
  char*         pUnescapedLocationURL;
  size_t        UnescapedLocationURLLength;
  char*         pUnescapedLocationURLLowerCase;


  /* Initialize return value */
  /*                         */
  *hParamMatchingWebBrowser = NULL;

  /* Determine number of windows to iterate */
  /*                                        */
  Error = IShellWindows_get_Count(pParamIShellWindows, &NumberOfWindows);
  if (Error == ERROR_SUCCESS)
  {
    WindowNumber = 0;
    while ((*hParamMatchingWebBrowser == NULL) &&
           (WindowNumber < NumberOfWindows))
    {
      /* Access a single shell window */
      /*                              */
#if __STDC__ || defined(NONAMELESSUNION)
      WindowSelector.n1.n2.vt      = VT_I4;
      WindowSelector.n1.n2.n3.lVal = WindowNumber;
#else
      WindowSelector.vt   = VT_I4;
      WindowSelector.lVal = WindowNumber;
#endif
      Error = IShellWindows_Item(pParamIShellWindows, WindowSelector,
                                 &pIShellWindow);
      if (Error == S_OK)
      {
        /* See if this window is an Explorer window */
        /*                                          */
        Error = IShellWindows_QueryInterface(pIShellWindow,
                                             &IID_IWebBrowser2, (void **)&pIWebBrowser2);
        if (Error == S_OK)
        {
          /* Confirm window is an Internet Explorer window */
          /*                                               */
          Error = IWebBrowser2_get_FullName(pIWebBrowser2, &WebBrowserFullName);
          if (Error == ERROR_SUCCESS)
          {
            /* Convert full name to a C string */
            /*                                 */
            Error = private_WWHelp_Platform_BSTRToCString(WebBrowserFullName, &pFullName);
            if (Error == 0)
            {
              /* Is this an Internet Explorer window ?*/
              /*                                      */
              if ((strstr(pFullName, "iexplore.exe") != 0) ||
                  (strstr(pFullName, "IEXPLORE.EXE") != 0))
              {
                /* See if a browser is already displaying the target help set */
                /*                                                            */
                Error = IWebBrowser2_get_LocationURL(pIWebBrowser2, &WebBrowserLocationURL);
                if (Error == 0)
                {
                  Error = private_WWHelp_Platform_BSTRToCString(WebBrowserLocationURL,
                                                                &pLocationURL);
                  if (Error == 0)
                  {
                    /* Convert to lower case */
                    /*                       */
                    pURLFragmentLowerCase = (char*)malloc((strlen(pParamURLFragment) + 1) * sizeof(char));
                    if (pURLFragmentLowerCase != NULL)
                    {
                      strcpy(pURLFragmentLowerCase, pParamURLFragment);
                      private_WWHelp_Platform_ToLowerCase(pURLFragmentLowerCase);
                    }
                    pLocationURLLowerCase = (char*)malloc((strlen(pLocationURL) + 1) * sizeof(char));
                    if (pLocationURLLowerCase != NULL)
                    {
                      strcpy(pLocationURLLowerCase, pLocationURL);
                      private_WWHelp_Platform_ToLowerCase(pLocationURLLowerCase);
                    }

                    /* Check for match against original string or lowercase version */
                    /*                                                              */
                    if ((strstr(pLocationURL, pParamURLFragment) != NULL) ||
                        ((pURLFragmentLowerCase != NULL) &&
                         (pLocationURLLowerCase != NULL) &&
                         (strstr(pLocationURLLowerCase, pURLFragmentLowerCase) != NULL)))
                    {
                      /* Found match for browser displaying help! */
                      /*                                          */
                      *hParamMatchingWebBrowser = pIWebBrowser2;
                      pIWebBrowser2 = NULL;  /* Prevent interface release */

                      ReturnCode = 0;
                    }
                    else
                    {
                      /* Try to match using unescaped URL */
                      /*                                  */
                      Error = WWHelp_URL_Unescape(pLocationURL, strlen(pLocationURL),
                                                  &pUnescapedLocationURL, &UnescapedLocationURLLength);
                      if (Error == 0)
                      {
                        /* Convert to lower case */
                        /*                       */
                        pUnescapedLocationURLLowerCase = (char*)malloc((strlen(pUnescapedLocationURL) + 1) * sizeof(char));
                        if (pUnescapedLocationURLLowerCase != NULL)
                        {
                          strcpy(pUnescapedLocationURLLowerCase, pUnescapedLocationURL);
                          private_WWHelp_Platform_ToLowerCase(pUnescapedLocationURLLowerCase);
                        }

                        /* Check for match against original string or lowercase version */
                        /*                                                              */
                        if ((strstr(pUnescapedLocationURL, pParamURLFragment) != NULL) ||
                            ((pURLFragmentLowerCase != NULL) &&
                             (pUnescapedLocationURLLowerCase != NULL) &&
                             (strstr(pUnescapedLocationURLLowerCase, pURLFragmentLowerCase) != NULL)))
                        {
                          /* Found match for browser displaying help! */
                          /*                                          */
                          *hParamMatchingWebBrowser = pIWebBrowser2;
                          pIWebBrowser2 = NULL;  /* Prevent interface release */

                          ReturnCode = 0;
                        }

                        if (pUnescapedLocationURLLowerCase != NULL)
                        {
                          free(pUnescapedLocationURLLowerCase);
                        }

                        free(pUnescapedLocationURL);
                      }
                    }

                    if (pURLFragmentLowerCase != NULL)
                    {
                      free(pURLFragmentLowerCase);
                    }
                    if (pLocationURLLowerCase != NULL)
                    {
                      free(pLocationURLLowerCase);
                    }

                    free(pLocationURL);
                  }

                  SysFreeString(WebBrowserLocationURL);
                }
              }

              free(pFullName);
            }

            SysFreeString(WebBrowserFullName);
          }

          /* Release interface if it won't be used */
          /*                                       */
          if (pIWebBrowser2 != NULL)
          {
            IShellWindows_Release(pIWebBrowser2);
          }
        }

        /* Release window */
        /*                */
        IShellWindows_Release(pIShellWindow);
      }

      WindowNumber++;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_COMSend                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_COMSend(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                const char*               pParamURL,
                                const size_t              ParamURLLength)
{
  int            ReturnCode = -1;
  HRESULT        Error;
  IShellWindows* pIShellWindowsObject;
  IWebBrowser2*  pIWebBrowser2;
  BSTR           NavigateURL;
  VARIANT        NavigateFlags       = {0};
  VARIANT        NavigateTargetFrame = {0};
  VARIANT        NavigatePostData    = {0};
  VARIANT        NavigateHeaders     = {0};
  VARIANT_BOOL   bBrowserBusy;
  HWND           hBrowserWindow;


  /* Create COM object */
  /*                   */
  Error = CoCreateInstance(&sgShellWindowsGUID, NULL, CLSCTX_ALL,
                           &IID_IShellWindows, (void**)&pIShellWindowsObject);
  if (Error == S_OK)
  {
    /* Attempt to reuse an exist browser display the correct help set */
    /*                                                                */
    if (pParamBrowserInfo->mpLocationID != NULL)
    {
      Error = private_WWHelp_Platform_FindWWHelpBrowser(pIShellWindowsObject,
                                                        pParamBrowserInfo->mpLocationID,
                                                        &pIWebBrowser2);

      /* Open URL in target browser if found */
      /*                                     */
      if ((Error == 0) &&
          (pIWebBrowser2 != NULL))
      {
        Error = private_WWHelp_Platform_CharPtrToBSTR(pParamURL, ParamURLLength, &NavigateURL);
        if (Error == 0)
        {
          /* Invoke Navigate COM method */
          /*                            */
          Error = IWebBrowser2_Navigate(pIWebBrowser2, NavigateURL,
                                        &NavigateFlags, &NavigateTargetFrame,
                                        &NavigatePostData, &NavigateHeaders);
          if (Error == 0)
          {
            /* Wait for browser to load the URL (check to see if it is busy) */
            /*                                                               */
            Sleep(100);  /* Wait a tenth of a second */
            Error = IWebBrowser2_get_Busy(pIWebBrowser2, &bBrowserBusy);
            while ((Error == 0) &&
                   (bBrowserBusy == VARIANT_TRUE))
            {
              Sleep(100);  /* Wait a tenth of a second */
              Error = IWebBrowser2_get_Busy(pIWebBrowser2, &bBrowserBusy);
            }

            /* Success! */
            /*          */
            ReturnCode = 0;

            /* Bring browser to front */
            /*                        */
            Error = IWebBrowser2_get_HWND(pIWebBrowser2, (long*)(&hBrowserWindow));
            if (Error == S_OK)
            {
              if (IsIconic(hBrowserWindow))
              {
                OpenIcon(hBrowserWindow);
              }
              else
              {
                SetForegroundWindow(hBrowserWindow);
              }
            }
          }

          SysFreeString(NavigateURL);
        }

        /* Release interface */
        /*                   */
        IShellWindows_Release(pIWebBrowser2);
      }
    }

    /* Release instance */
    /*                  */
    IShellWindows_Release(pIShellWindowsObject);
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_DDECallback                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

HDDEDATA CALLBACK
private_WWHelp_Platform_DDECallback(UINT      ParamType,
                                    UINT      ParamFormat,
                                    HCONV     ParamHCONV,
                                    HSZ       ParamHSZ1,
                                    HSZ       ParamHSZ2,
                                    HDDEDATA  ParamData,
                                    DWORD     ParamDW1,
                                    DWORD     ParamDW2)
{
  return NULL;
}


/****************************************************************************/
/* private_WWHelp_Platform_DDESend_BuildDDECommand                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_DDESend_BuildDDECommand(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                                const char*               pParamURL,
                                                const size_t              ParamURLLength,
                                                      char*               pParamDDECommand)
{
  int     ReturnCode = -1;
  char    WindowIDString[256];
  HANDLE  FileHandle;
  DWORD   BytesWritten;


  /* Convert Window ID to a string */
  /*                               */
  sprintf(WindowIDString, "%lu", pParamBrowserInfo->mDDEWindowID);

  /* Build DDE message */
  /*                   */
  pParamDDECommand[0] = '\0';
  strcat(pParamDDECommand, "\"");
  strncat(pParamDDECommand, pParamURL, ParamURLLength);
  strcat(pParamDDECommand, "\",,");
  strcat(pParamDDECommand, WindowIDString);
  strcat(pParamDDECommand, ",0,,,");
  strcat(pParamDDECommand, sgWWHAPIService);

  /* See if file redirect mechanism should be used */
  /*                                               */
  if (strlen(pParamDDECommand) <= WWHELP_DDECOMMAND_MAXLENGTH)
  {
    /* Success! */
    /*          */
    ReturnCode = 0;
  }
  else if (pParamBrowserInfo->mbSupportsDDEWithFile)
  {
    /* Write URL to a temporary file */
    /*                               */
    FileHandle = CreateFile(sgWWHelp_Platform_TempFilename, GENERIC_WRITE, 0, NULL,
                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (FileHandle != INVALID_HANDLE_VALUE)
    {
      if (WriteFile(FileHandle, pParamURL, (DWORD)ParamURLLength, &BytesWritten, NULL))
      {
        /* Build DDE message */
        /*                   */
        pParamDDECommand[0] = '\0';
        strcat(pParamDDECommand, "\"^");
        strcat(pParamDDECommand, sgWWHelp_Platform_TempFilename);
        strcat(pParamDDECommand, "\",,");
        strcat(pParamDDECommand, WindowIDString);
        strcat(pParamDDECommand, ",0,,,");
        strcat(pParamDDECommand, sgWWHAPIService);

        /* Success! */
        /*          */
        ReturnCode = 0;
      }

      /* Insure clean file close */
      /*                         */
      if ( ! CloseHandle(FileHandle))
      {
        ReturnCode = -1;
      }
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_DDESend_WWW_OpenURL                              */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_DDESend_WWW_OpenURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                            const char*               pParamURL,
                                            const size_t              ParamURLLength)
{
  static const char* spWWWOpenURL = "WWW_OpenURL";

  int       ReturnCode = -1;
  DWORD*    pDDEWindowID;
  int       bTry;
  HSZ       Service;
  HSZ       Topic;
  HCONV     Conversation;
  int       Error;
  char      OpenURLCommand[WWHELP_OPENCOMMAND_MAXLENGTH];
  HSZ       Command;
  HDDEDATA  DataHandle;
  LPBYTE    DataPtr;
  DWORD     DataSize;
  DWORD     ReturnedWindowID;


  /* Access window ID and use workaround for const declaration */
  /*                                                           */
  pDDEWindowID = (DWORD*)&(pParamBrowserInfo->mDDEWindowID);

  /* Define DDE service name */
  /*                         */
  Service = DdeCreateStringHandle(sgDDEInstance, pParamBrowserInfo->mDDEServiceName, CP_WINANSI);
  if (Service != NULL)
  {
    /* Define DDE topic name */
    /*                       */
    Topic = DdeCreateStringHandle(sgDDEInstance, spWWWOpenURL, CP_WINANSI);
    if (Topic != NULL)
    {
      /* Open connection */
      /*                 */
      Conversation = DdeConnect(sgDDEInstance, Service, Topic, NULL);
      if (Conversation != NULL)
      {
        /* May need to retry with a different window ID */
        /*                                              */
        bTry = 1;
        while (bTry)
        {
          bTry = 0;

          /* Create DDE message */
          /*                    */
          Error = private_WWHelp_Platform_DDESend_BuildDDECommand(pParamBrowserInfo,
                                                                  pParamURL, ParamURLLength,
                                                                  OpenURLCommand);
          if (Error == 0)
          {
            /* Create DDE command string */
            /*                           */
            Command = DdeCreateStringHandle(sgDDEInstance, OpenURLCommand, CP_WINANSI);
            if (Command != NULL)
            {
              /* Send OpenURL command */
              /*                      */
              DataHandle = DdeClientTransaction(NULL, 0,
                                                Conversation, Command,
                                                CF_TEXT, XTYP_REQUEST,
                                                WWHELP_DDECOMMAND_TIMEOUT, NULL);
              if (DataHandle != NULL)
              {
                /* Record window ID for future use */
                /*                                 */
                DataPtr = DdeAccessData(DataHandle, &DataSize);
                if (DataPtr != NULL)
                {
                  if (DataSize == sizeof(DWORD))
                  {
                    ReturnedWindowID = *((DWORD*)DataPtr);

                    /* See if window ID was valid */
                    /*                            */
                    if (ReturnedWindowID == 0)
                    {
                      /* If window ID is not -1 (frontmost window), try again */
                      /*                                                      */
                      if (*pDDEWindowID != -1)
                      {
                        *pDDEWindowID = -1;

                        bTry = 1;
                      }
                    }
                    else if (ReturnedWindowID != -1)
                    {
                      /* Update Window ID                     */
                      /*                                      */
                      *pDDEWindowID = ReturnedWindowID;

                      /* Success! */
                      /*          */
                      ReturnCode = 0;
                    }
                  }

                  /* Release data access */
                  /*                     */
                  DdeUnaccessData(DataHandle);
                }

                /* Release returned data */
                /*                       */
                DdeFreeDataHandle(DataHandle);
              }

              /* Release DDE command */
              /*                     */
              DdeFreeStringHandle(sgDDEInstance, Command);
            }
          }
        }

        /* Close connection */
        /*                  */
        DdeDisconnect(Conversation);
      }

      /* Release DDE topic name */
      /*                        */
      DdeFreeStringHandle(sgDDEInstance, Topic);
    }

    /* Release DDE service name */
    /*                        */
    DdeFreeStringHandle(sgDDEInstance, Service);
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_DDESend_WWW_Activate                             */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_DDESend_WWW_Activate(const WWHelp_BrowserInfo* pParamBrowserInfo)
{
  static const char* spWWWActivate = "WWW_Activate";

  int       ReturnCode = -1;
  char      ActivateCommand[WWHELP_DDECOMMAND_MAXLENGTH];
  HSZ       Service;
  HSZ       Topic;
  HCONV     Conversation;
  HSZ       Command;
  HDDEDATA  DataHandle;


  /* Create DDE command */
  /*                    */
  sprintf(ActivateCommand, "%lu,0", pParamBrowserInfo->mDDEWindowID);

  /* Define DDE service name */
  /*                         */
  Service = DdeCreateStringHandle(sgDDEInstance, pParamBrowserInfo->mDDEServiceName, CP_WINANSI);
  if (Service != NULL)
  {
    /* Define DDE topic name */
    /*                       */
    Topic = DdeCreateStringHandle(sgDDEInstance, spWWWActivate, CP_WINANSI);
    if (Topic != NULL)
    {
      /* Open connection */
      /*                 */
      Conversation = DdeConnect(sgDDEInstance, Service, Topic, NULL);
      if (Conversation != NULL)
      {
        /* Create DDE command */
        /*                    */
        Command = DdeCreateStringHandle(sgDDEInstance, ActivateCommand, CP_WINANSI);
        if (Command != NULL)
        {
          /* Send Activate command */
          /*                       */
          DataHandle = DdeClientTransaction(NULL, 0,
                                            Conversation, Command,
                                            CF_TEXT, XTYP_REQUEST,
                                            WWHELP_DDECOMMAND_TIMEOUT, NULL);
          if (DataHandle != NULL)
          {
            /* Success! */
            /*          */
            ReturnCode = 0;

            /* Release returned data */
            /*                       */
            DdeFreeDataHandle(DataHandle);
          }

          /* Release DDE command */
          /*                     */
          DdeFreeStringHandle(sgDDEInstance, Command);
        }

        /* Close connection */
        /*                  */
        DdeDisconnect(Conversation);
      }

      /* Release DDE topic name */
      /*                        */
      DdeFreeStringHandle(sgDDEInstance, Topic);
    }

    /* Release DDE service name */
    /*                        */
    DdeFreeStringHandle(sgDDEInstance, Service);
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_DDESend                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_DDESend(const WWHelp_BrowserInfo* pParamBrowserInfo,
                                const char*               pParamURL,
                                const size_t              ParamURLLength)
{
  int  ReturnCode = -1;
  int  Error;


  /* Confirm DDE service name is defined */
  /*                                     */
  if (strlen(pParamBrowserInfo->mDDEServiceName) > 0)
  {
    /* Browser is already running */
    /*                            */
    Error = private_WWHelp_Platform_DDESend_WWW_OpenURL(pParamBrowserInfo,
                                                        pParamURL, ParamURLLength);
    if (Error == 0)
    {
      /* Bring window to foreground                                        */
      /* Ignore any errors here as Mozilla/Firefox do not support activate */
      /*                                                                   */
      Error = private_WWHelp_Platform_DDESend_WWW_Activate(pParamBrowserInfo);

      /* Success! */
      /*          */
      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_Startup                                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_Startup()
{
  const char* pWWHAPIServiceBaseName = "WWHAPI";

  int    ReturnCode = -1;
  UINT   Error;
  DWORD  PathLength;
  char   TempPath[WWHELP_TEMPFILENAME_MAXLENGTH];
  int    bTempDirectoryExists;
  DWORD  FileAttributes;


  ReturnCode = 0;

  /* Reset temporary filename */
  /*                          */
  sgWWHelp_Platform_TempFilename[0] = '\0';

  /* Initialize DDE instance */
  /*                         */
  if (ReturnCode == 0)
  {
    ReturnCode = -1;

    Error = DdeInitialize(&sgDDEInstance, (PFNCALLBACK)private_WWHelp_Platform_DDECallback,
                          APPCLASS_STANDARD | APPCMD_CLIENTONLY, 0);
    if (Error == DMLERR_NO_ERROR)
    {
      /* Success! */
      /*          */
      ReturnCode = 0;
    }
    else
    {
      sgDDEInstance = 0;
    }
  }

  /* Define service name */
  /*                     */
  sgWWHAPIService[0] = '\0';
  sprintf(sgWWHAPIService, "%s%X", pWWHAPIServiceBaseName, time(NULL));

  /* Create filename for temporary file for Netscape DDE file communications */
  /*                                                                         */
  if (ReturnCode == 0)
  {
    ReturnCode = -1;

    /* Locate temporary directory */
    /*                            */
    PathLength = GetTempPath(WWHELP_TEMPFILENAME_MAXLENGTH, TempPath);
    if ((PathLength > 0) &&
        (PathLength < WWHELP_TEMPFILENAME_MAXLENGTH))
    {
      /* Confirm temporary directory exists */
      /*                                    */
      bTempDirectoryExists = 0;
      FileAttributes = GetFileAttributes(TempPath);
      if (FileAttributes == 0xFFFFFFFF)
      {
        /* Temporary directory does not exist, so create it! */
        /*                                                   */
        if (CreateDirectory(TempPath, NULL))
        {
          bTempDirectoryExists = 1;
        }
      }
      else if (FileAttributes && FILE_ATTRIBUTE_DIRECTORY)
      {
        /* Temporary directory exists at given location and is a directory */
        /*                                                                 */
        bTempDirectoryExists = 1;
      }

      if (bTempDirectoryExists)
      {
        /* Create temporary filename */
        /*                           */
        if (GetTempFileName(TempPath, "wwhapi", 0, sgWWHelp_Platform_TempFilename) != 0)
        {
          /* Set .htm extension for use with IE 7.0 redirects */
          /*                                                  */
          strcat(sgWWHelp_Platform_TempFilename, ".htm");

          /* Success! */
          /*          */
          ReturnCode = 0;
        }
      }
    }
  }

  /* Define GUIDs for required objects */
  /*                                   */
  if (ReturnCode == 0)
  {
    ReturnCode = -1;

    /* GUIDs from <ComDef.h>                                */
    /* ShellWindows  "9BA05972-F6A8-11CF-A442-00A0C90A8F39" */
    /*                                                      */
    if (private_WWHelp_DefineGUID("9BA05972-F6A8-11CF-A442-00A0C90A8F39",
                                   &sgShellWindowsGUID) == 0)
    {
      /* Success! */
      /*          */
      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_Shutdown                                                 */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_Shutdown()
{
  int  ReturnCode = -1;


  /* Remove temporary file used for DDE */
  /*                                    */
  if (strlen(sgWWHelp_Platform_TempFilename) > 0)
  {
    DeleteFile(sgWWHelp_Platform_TempFilename);
    sgWWHelp_Platform_TempFilename[0] = '\0';
  }

  /* Release DDE instance */
  /*                      */
  DdeUninitialize(sgDDEInstance);
  sgDDEInstance = 0;

  /* Success! */
  /*          */
  ReturnCode = 0;

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_SetDefaultBrowserInfo                                    */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_SetDefaultBrowserInfo(WWHelp_BrowserInfo* pParamBrowserInfo)
{
  int         ReturnCode = -1;
  HRESULT     Error;
  const char* pClassName;
  HKEY        HTMLHandlerKey;
  char        HTMLHandlerValue[256];
  LONG        HTMLHandleValueMaxLength = 256;
  char        ApplicationCommandPath[512];
  HKEY        ApplicationCommandKey;
  char        ApplicationServicePath[512];
  HKEY        ApplicationServiceKey;
  LONG        MaxValueLength;


  /* Reset browser info */
  /*                    */
  WWHelp_Comm_ClearBrowserInfo(pParamBrowserInfo);

  /* All Windows browsers support smart URLs */
  /*                                         */
  pParamBrowserInfo->mbAllowSmartURLs = 1;

  /* Lookup handler for .htm files */
  /*                               */
  pClassName = ".htm";
  Error = RegOpenKeyEx(HKEY_CLASSES_ROOT, pClassName, 0,
                       KEY_QUERY_VALUE, &HTMLHandlerKey);
  if (Error != ERROR_SUCCESS)
  {
    /* Lookup handler for .html files if .htm failed */
    /*                                               */
    pClassName = ".html";
    Error = RegOpenKeyEx(HKEY_CLASSES_ROOT, pClassName, 0,
                         KEY_QUERY_VALUE, &HTMLHandlerKey);
  }
  if (Error != ERROR_SUCCESS)
  {
    /* Lookup handler for htmlfile if .html failed */
    /*                                              */
    pClassName = "htmlfile";
    Error = RegOpenKeyEx(HKEY_CLASSES_ROOT, pClassName, 0,
                         KEY_QUERY_VALUE, &HTMLHandlerKey);
  }

  if (Error == ERROR_SUCCESS)
  {
    /* Access handler name */
    /*                     */
    Error = RegQueryValue(HTMLHandlerKey, "",
                          HTMLHandlerValue, &HTMLHandleValueMaxLength);
    if (Error == ERROR_SUCCESS)
    {
      /* Determine whether or not to use Netscape style smart URLs */
      /*                                                           */
      if (strcmp(HTMLHandlerValue, "NetscapeMarkup") == 0)
      {
        pParamBrowserInfo->mbNetscapeBrowser = 1;
        pParamBrowserInfo->mbSupportsDDEWithFile = 1;
      }
      else if (strcmp(HTMLHandlerValue, "MozillaHTML") == 0)
      {
        pParamBrowserInfo->mbNetscapeBrowser = 1;
      }
      else if (strcmp(HTMLHandlerValue, "FirefoxHTML") == 0)
      {
        pParamBrowserInfo->mbNetscapeBrowser = 1;
      }

      /* Access HTML handler's open command */
      /*                                    */
      strcpy(ApplicationCommandPath, HTMLHandlerValue);
      strcat(ApplicationCommandPath, "\\shell\\open\\command");
      Error = RegOpenKeyEx(HKEY_CLASSES_ROOT, ApplicationCommandPath, 0,
                           KEY_QUERY_VALUE, &ApplicationCommandKey);

      /* Lookup with class name if handler failed */
      /*                                          */
      if (Error != ERROR_SUCCESS)
      {
        strcpy(ApplicationCommandPath, pClassName);
        strcat(ApplicationCommandPath, "\\shell\\open\\command");
        Error = RegOpenKeyEx(HKEY_CLASSES_ROOT, ApplicationCommandPath, 0,
                             KEY_QUERY_VALUE, &ApplicationCommandKey);
      }
      if (Error == ERROR_SUCCESS)
      {
        MaxValueLength = WWHELP_OPENCOMMAND_MAXLENGTH;
        Error = RegQueryValue(ApplicationCommandKey, "",
                              pParamBrowserInfo->mOpenCommand, &MaxValueLength);
        if (Error == ERROR_SUCCESS)
        {
          /* Access HTML handler's DDE service name */
          /*                                        */
          strcpy(ApplicationServicePath, HTMLHandlerValue);
          strcat(ApplicationServicePath, "\\shell\\open\\ddeexec\\Application");
          Error = RegOpenKeyEx(HKEY_CLASSES_ROOT, ApplicationServicePath, 0,
                               KEY_QUERY_VALUE, &ApplicationServiceKey);

          /* Lookup with class name if handler failed */
          /*                                          */
          if (Error != ERROR_SUCCESS)
          {
            strcpy(ApplicationServicePath, pClassName);
            strcat(ApplicationServicePath, "\\shell\\open\\ddeexec\\Application");
            Error = RegOpenKeyEx(HKEY_CLASSES_ROOT, ApplicationServicePath, 0,
                                 KEY_QUERY_VALUE, &ApplicationServiceKey);
          }
          if (Error == ERROR_SUCCESS)
          {
            MaxValueLength = WWHELP_DDESERVICENAME_MAXLENGTH;
            Error = RegQueryValue(ApplicationServiceKey, "",
                                  pParamBrowserInfo->mDDEServiceName, &MaxValueLength);
            if (Error == ERROR_SUCCESS)
            {
              /* Internet Explorer uses COM for communication */
              /*                                              */
              if (strcmp(pParamBrowserInfo->mDDEServiceName, "IExplore") == 0)
              {
                pParamBrowserInfo->mbSendWithCOM = 1;

                /* Set location ID for finding running browser windows */
                /*                                                     */
                ReturnCode = private_WWHelp_Platform_SetLocationID(pParamBrowserInfo);
              }
              else
              {
                /* Success! */
                /*          */
                ReturnCode = 0;
              }
            }

            /* Close application service key */
            /*                               */
            RegCloseKey(ApplicationServiceKey);
          }
          else
          {
            /* Netscape/Mozilla doesn't seem to register this properly */
            /*                                                         */
            if (strcmp(HTMLHandlerValue, "NetscapeMarkup") == 0)
            {
              strcpy(pParamBrowserInfo->mDDEServiceName, "NETSCAPE");

              /* Success! */
              /*          */
              ReturnCode = 0;
            }
            else
            {
              /* No DDE service name, but browser found. */
              /*                                         */
              strcpy(pParamBrowserInfo->mDDEServiceName, "");

              /* Success! */
              /*          */
              ReturnCode = 0;
            }
          }
        }

        /* Close application command key */
        /*                               */
        RegCloseKey(ApplicationCommandKey);
      }
    }

    /* Close HTML handler key */
    /*                        */
    RegCloseKey(HTMLHandlerKey);
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_CheckBrowserRunning                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_CheckBrowserRunning(const WWHelp_BrowserInfo* pParamBrowserInfo)
{
  static const char* spWWWVersion = "WWW_Version";

  int       ReturnCode = -1;
  HSZ       Service;
  HSZ       Topic;
  HCONV     Conversation;
  HSZ       Command;
  HDDEDATA  DataHandle;


  /* Confirm DDE service name is defined */
  /*                                     */
  if (strlen(pParamBrowserInfo->mDDEServiceName) > 0)
  {
    /* Define DDE service name */
    /*                         */
    Service = DdeCreateStringHandle(sgDDEInstance, pParamBrowserInfo->mDDEServiceName, CP_WINANSI);
    if (Service != NULL)
    {
      /* Define DDE topic name */
      /*                       */
      Topic = DdeCreateStringHandle(sgDDEInstance, spWWWVersion, CP_WINANSI);
      if (Topic != NULL)
      {
        /* Open connection */
        /*                 */
        Conversation = DdeConnect(sgDDEInstance, Service, Topic, NULL);
        if (Conversation != NULL)
        {
          /* Create DDE command */
          /*                    */
          Command = DdeCreateStringHandle(sgDDEInstance, spWWWVersion, CP_WINANSI);
          if (Command != NULL)
          {
            /* Send Version command */
            /*                      */
            DataHandle = DdeClientTransaction(NULL, 0,
                                              Conversation, Command,
                                              CF_TEXT, XTYP_REQUEST,
                                              TIMEOUT_ASYNC, NULL);
            if (DataHandle != NULL)
            {
              /* Release returned data */
              /*                       */
              DdeFreeDataHandle(DataHandle);

              /* Success! */
              /*          */
              ReturnCode = 0;
            }

            /* Release DDE command */
            /*                     */
            DdeFreeStringHandle(sgDDEInstance, Command);
          }

          /* Close connection */
          /*                  */
          DdeDisconnect(Conversation);
        }

        /* Release DDE topic name */
        /*                        */
        DdeFreeStringHandle(sgDDEInstance, Topic);
      }

      /* Release DDE service name */
      /*                        */
      DdeFreeStringHandle(sgDDEInstance, Service);
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_SendURL                                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_SendURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                        const char*               pParamURL,
                        const size_t              ParamURLLength)
{
  int  ReturnCode = -1;
  int  Error;


  /* Send URL and activate the window in the target browser */
  /*                                                        */
  if (pParamBrowserInfo->mbSendWithCOM)
  {
    /* Use COM for IE mechanism */
    /*                          */
    Error = private_WWHelp_Platform_COMSend(pParamBrowserInfo,
                                            pParamURL, ParamURLLength);
  }
  else
  {
    /* Use DDE mechanism */
    /*                   */
    Error = private_WWHelp_Platform_DDESend(pParamBrowserInfo,
                                            pParamURL, ParamURLLength);
  }

  if (Error == 0)
  {
    /* Success! */
    /*          */
    ReturnCode = 0;
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_WriteRedirectHTML                                        */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_WriteRedirectHTML(const char*   pParamURL,
                                  const size_t  ParamURLLength)
{
  int     ReturnCode = -1;
  char*   pHTMLFilePrefix = "<html>\n <head>\n  <title>Redirect</title>\n\n  <script type=\"text/javascript\" language=\"JavaScript1.2\">\n   <!--\n    function  WWHRedirect()\n    {\n      location.replace(\"";
  char*   pHTMLFileSuffix = "\");\n    }\n   // -->\n  </script>\n </head>\n <body onload=\"WWHRedirect();\">\n </body>\n</html>";
  HANDLE  FileHandle;
  DWORD   BytesWritten;

  /* Write URL to a temporary HTML file */
  /*                                    */
  FileHandle = CreateFile(sgWWHelp_Platform_TempFilename, GENERIC_WRITE, 0, NULL,
                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  if (FileHandle != INVALID_HANDLE_VALUE)
  {
    if ((WriteFile(FileHandle, pHTMLFilePrefix, (DWORD)strlen(pHTMLFilePrefix), &BytesWritten, NULL)) &&
        (WriteFile(FileHandle, pParamURL, (DWORD)ParamURLLength, &BytesWritten, NULL)) &&
        (WriteFile(FileHandle, pHTMLFileSuffix, (DWORD)strlen(pHTMLFileSuffix), &BytesWritten, NULL)))
    {
      /* Success! */
      /*          */
      ReturnCode = 0;
    }

    /* Insure clean file close */
    /*                         */
    if ( ! CloseHandle(FileHandle))
    {
      ReturnCode = -1;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_LaunchWithURL                                            */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_LaunchWithURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                              const char*               pParamURL,
                              const size_t              ParamURLLength)
{
  int                  ReturnCode = -1;
  int                  bSuccess;
  char                 VarURL[WWHELP_OPENCOMMAND_MAXLENGTH];
  char*                pPercentOne;
  size_t               PrefixLength;
  size_t               SuffixLength;
  char                 OpenCommand[WWHELP_OPENCOMMAND_MAXLENGTH];
  STARTUPINFO          StartupInfo;
  PROCESS_INFORMATION  ProcessInfo;


  /* Redirect through temporary file if on the local file system */
  /*                                                             */
  if (( ! pParamBrowserInfo->mbNetscapeBrowser) &&
      (strncmp(pParamURL, "file:", 5) == 0))
  {
    // Create redirect file
    //
    if (WWHelp_Platform_WriteRedirectHTML(pParamURL, ParamURLLength) == 0)
    {
      strcpy(VarURL, sgWWHelp_Platform_TempFilename);
      bSuccess = 1;
    }
  }
  else
  {
    /* Use original URL as is */
    /*                        */
    memcpy(VarURL, pParamURL, ParamURLLength);
    VarURL[ParamURLLength] = '\0';
    bSuccess = 1;
  }

  // Continue?
  //
  if (bSuccess)
  {
    /* Locate %1 in open command so we can replace it */
    /*                                                */
    pPercentOne = strstr(pParamBrowserInfo->mOpenCommand, "%1");
    if (pPercentOne == NULL)
    {
      /* Just append URL to end of command */
      /*                                   */
      strcpy(OpenCommand, pParamBrowserInfo->mOpenCommand);
      strcat(OpenCommand, " \"");
      strcpy(&(OpenCommand[strlen(OpenCommand)]), VarURL);
      strcat(OpenCommand, "\"");
    }
    else
    {
      /* Replace %1 with URL */
      /*                     */
      PrefixLength = pPercentOne - pParamBrowserInfo->mOpenCommand;
      SuffixLength = &(pParamBrowserInfo->mOpenCommand[strlen(pParamBrowserInfo->mOpenCommand)]) - pPercentOne - 2;
      memcpy(OpenCommand, pParamBrowserInfo->mOpenCommand, PrefixLength);
      strcpy(&(OpenCommand[PrefixLength]), VarURL);
      memcpy(&(OpenCommand[PrefixLength + strlen(VarURL)]), &(pParamBrowserInfo->mOpenCommand[PrefixLength + 2]), SuffixLength);
      OpenCommand[PrefixLength + strlen(VarURL) + SuffixLength] = '\0';
    }

    /* Execute open command */
    /*                      */
    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
    StartupInfo.cb = sizeof(StartupInfo);
    ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
    bSuccess = CreateProcess(NULL, OpenCommand, NULL, NULL, 0, DETACHED_PROCESS,
                             NULL, NULL, &StartupInfo, &ProcessInfo);
    if (bSuccess)
    {
      /* Clean up process handles */
      /*                          */
      CloseHandle(ProcessInfo.hProcess);
      CloseHandle(ProcessInfo.hThread);

      /* Success! */
      /*          */
      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* BSTR WebWorks Help API functions                                         */
/*                                                                          */
/****************************************************************************/

int
WWHelp_BSTR_Startup(const BSTR  ParamBaseURL)
{
  int   VarReturnCode = -1;
  int   VarResult;
  char* pVarBaseURL;


  /* Convert base URL to a C string */
  /*                               */
  VarResult = private_WWHelp_Platform_BSTRToCString(ParamBaseURL, &pVarBaseURL);
  if (VarResult == 0)
  {
    /* Call WWHelp API */
    /*                 */
    VarReturnCode = WWHelp_Startup(pVarBaseURL);

    free(pVarBaseURL);
  }

  return VarReturnCode;
}

int
WWHelp_BSTR_Shutdown()
{
  int  VarReturnCode = -1;


  VarReturnCode = WWHelp_Shutdown();

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelp(const BSTR  ParamContext,
                        const BSTR  ParamTopic)
{
  int   VarReturnCode = -1;
  int   VarResult;
  char* pVarContext;
  char* pVarTopic;


  /* Convert context to a C string */
  /*                               */
  VarResult = private_WWHelp_Platform_BSTRToCString(ParamContext, &pVarContext);
  if (VarResult == 0)
  {
    /* Convert topic to a C string */
    /*                               */
    VarResult = private_WWHelp_Platform_BSTRToCString(ParamTopic, &pVarTopic);
    if (VarResult == 0)
    {
      /* Call WWHelp API */
      /*                 */
      VarReturnCode = WWHelp_DisplayHelp(pVarContext, pVarTopic);

      free(pVarTopic);
    }

    free(pVarContext);
  }

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpWithNavigation(const BSTR  ParamContext,
                                      const BSTR  ParamTopic)
{
  int   VarReturnCode = -1;
  int   VarResult;
  char* pVarContext;
  char* pVarTopic;


  /* Convert context to a C string */
  /*                               */
  VarResult = private_WWHelp_Platform_BSTRToCString(ParamContext, &pVarContext);
  if (VarResult == 0)
  {
    /* Convert topic to a C string */
    /*                               */
    VarResult = private_WWHelp_Platform_BSTRToCString(ParamTopic, &pVarTopic);
    if (VarResult == 0)
    {
      /* Call WWHelp API */
      /*                 */
      VarReturnCode = WWHelp_DisplayHelpWithNavigation(pVarContext, pVarTopic);

      free(pVarTopic);
    }

    free(pVarContext);
  }

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpWithContents(const BSTR  ParamContext,
                                    const BSTR  ParamTopic)
{
  int   VarReturnCode = -1;
  int   VarResult;
  char* pVarContext;
  char* pVarTopic;


  /* Convert context to a C string */
  /*                               */
  VarResult = private_WWHelp_Platform_BSTRToCString(ParamContext, &pVarContext);
  if (VarResult == 0)
  {
    /* Convert topic to a C string */
    /*                               */
    VarResult = private_WWHelp_Platform_BSTRToCString(ParamTopic, &pVarTopic);
    if (VarResult == 0)
    {
      /* Call WWHelp API */
      /*                 */
      VarReturnCode = WWHelp_DisplayHelpWithContents(pVarContext, pVarTopic);

      free(pVarTopic);
    }

    free(pVarContext);
  }

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpWithIndex(const BSTR  ParamContext,
                                 const BSTR  ParamTopic)
{
  int   VarReturnCode = -1;
  int   VarResult;
  char* pVarContext;
  char* pVarTopic;


  /* Convert context to a C string */
  /*                               */
  VarResult = private_WWHelp_Platform_BSTRToCString(ParamContext, &pVarContext);
  if (VarResult == 0)
  {
    /* Convert topic to a C string */
    /*                               */
    VarResult = private_WWHelp_Platform_BSTRToCString(ParamTopic, &pVarTopic);
    if (VarResult == 0)
    {
      /* Call WWHelp API */
      /*                 */
      VarReturnCode = WWHelp_DisplayHelpWithIndex(pVarContext, pVarTopic);

      free(pVarTopic);
    }

    free(pVarContext);
  }

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpWithSearch(const BSTR  ParamContext,
                                  const BSTR  ParamTopic)
{
  int   VarReturnCode = -1;
  int   VarResult;
  char* pVarContext;
  char* pVarTopic;


  /* Convert context to a C string */
  /*                               */
  VarResult = private_WWHelp_Platform_BSTRToCString(ParamContext, &pVarContext);
  if (VarResult == 0)
  {
    /* Convert topic to a C string */
    /*                               */
    VarResult = private_WWHelp_Platform_BSTRToCString(ParamTopic, &pVarTopic);
    if (VarResult == 0)
    {
      /* Call WWHelp API */
      /*                 */
      VarReturnCode = WWHelp_DisplayHelpWithSearch(pVarContext, pVarTopic);

      free(pVarTopic);
    }

    free(pVarContext);
  }

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpWithFavorites(const BSTR  ParamContext,
                                     const BSTR  ParamTopic)
{
  int   VarReturnCode = -1;
  int   VarResult;
  char* pVarContext;
  char* pVarTopic;


  /* Convert context to a C string */
  /*                               */
  VarResult = private_WWHelp_Platform_BSTRToCString(ParamContext, &pVarContext);
  if (VarResult == 0)
  {
    /* Convert topic to a C string */
    /*                               */
    VarResult = private_WWHelp_Platform_BSTRToCString(ParamTopic, &pVarTopic);
    if (VarResult == 0)
    {
      /* Call WWHelp API */
      /*                 */
      VarReturnCode = WWHelp_DisplayHelpWithFavorites(pVarContext, pVarTopic);

      free(pVarTopic);
    }

    free(pVarContext);
  }

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpNavigation()
{
  int  VarReturnCode = -1;


  VarReturnCode = WWHelp_DisplayHelpNavigation();

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpContents()
{
  int  VarReturnCode = -1;


  VarReturnCode = WWHelp_DisplayHelpContents();

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpIndex()
{
  int  VarReturnCode = -1;


  VarReturnCode = WWHelp_DisplayHelpIndex();

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpSearch()
{
  int  VarReturnCode = -1;


  VarReturnCode = WWHelp_DisplayHelpSearch();

  return VarReturnCode;
}

int
WWHelp_BSTR_DisplayHelpFavorites()
{
  int  VarReturnCode = -1;


  VarReturnCode = WWHelp_DisplayHelpFavorites();

  return VarReturnCode;
}


/****************************************************************************/
/* Visual Basic callable interface                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

long __stdcall
WWHelp_VB_Startup(const char* pParamBaseURL)
{
  return WWHelp_Startup(pParamBaseURL);
}

long __stdcall
WWHelp_VB_Shutdown()
{
  return WWHelp_Shutdown();
}

long __stdcall
WWHelp_VB_DisplayHelp(const char* pParamContext,
                      const char* pParamTopic)
{
  return WWHelp_DisplayHelp(pParamContext, pParamTopic);
}

long __stdcall
WWHelp_VB_DisplayHelpWithNavigation(const char* pParamContext,
                                    const char* pParamTopic)
{
  return WWHelp_DisplayHelpWithNavigation(pParamContext, pParamTopic);
}

long __stdcall
WWHelp_VB_DisplayHelpWithContents(const char* pParamContext,
                                  const char* pParamTopic)
{
  return WWHelp_DisplayHelpWithContents(pParamContext, pParamTopic);
}

long __stdcall
WWHelp_VB_DisplayHelpWithIndex(const char* pParamContext,
                               const char* pParamTopic)
{
  return WWHelp_DisplayHelpWithIndex(pParamContext, pParamTopic);
}

long __stdcall
WWHelp_VB_DisplayHelpWithSearch(const char* pParamContext,
                                const char* pParamTopic)
{
  return WWHelp_DisplayHelpWithSearch(pParamContext, pParamTopic);
}

long __stdcall
WWHelp_VB_DisplayHelpWithFavorites(const char* pParamContext,
                                   const char* pParamTopic)
{
  return WWHelp_DisplayHelpWithFavorites(pParamContext, pParamTopic);
}

long __stdcall
WWHelp_VB_DisplayHelpNavigation()
{
  return WWHelp_DisplayHelpNavigation();
}

long __stdcall
WWHelp_VB_DisplayHelpContents()
{
  return WWHelp_DisplayHelpContents();
}

long __stdcall
WWHelp_VB_DisplayHelpIndex()
{
  return WWHelp_DisplayHelpIndex();
}

long __stdcall
WWHelp_VB_DisplayHelpSearch()
{
  return WWHelp_DisplayHelpSearch();
}

long __stdcall
WWHelp_VB_DisplayHelpFavorites()
{
  return WWHelp_DisplayHelpFavorites();
}
