// Copyright (C) Microsoft Corporation 1996-1997, All Rights reserved. #include "header.h" #ifdef _DEBUG #undef THIS_FILE static const char THIS_FILE[] = __FILE__; #endif #include "strtable.h" #include "hha_strtable.h" #include "resource.h" #include "contain.h" #include "system.h" #include "secwin.h" #include "popup.h" #include "highlite.h" // Get hhPreTranslateMessage #include "hhshell.h" // Internal API definitions. #include "hhpriv.h" // HH_GET_LAST_ERROR support #include "lasterr.h" ///////////////////////////////////////////////////////////////////// // // Internal function prototypes. // HWND ReactivateDisplayTopic(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData) ; HWND InternalHelpContext(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR id, HH_COLLECTION_CONTEXT* pContext); HWND OnHelpContextInCollection(HWND hwndCaller, LPCSTR pszColFile, DWORD_PTR dwData); bool SetGlobalProperty(HH_GLOBAL_PROPERTY* prop, CHHWinType*) ; bool GetNameAndWinType(LPCSTR pszFile, CStr& pszName, CStr& pszWindow) ; HWND OnReloadNavData(HWND hwndCaller, LPCSTR pszFile, HH_NAVDATA* pNavData) ; bool GetBrowserInterface(HWND hWndBrowserParent, IDispatch** ppWebBrowser); // Used by hh.cpp bool InitializeSession(UNALIGNED DWORD_PTR* pCookie) ; bool UninitializeSession(DWORD_PTR Cookie) ; ///////////////////////////////////////////////////////////////////// // // Constants // static const char txtWinHelpFileExt[] = ".hlp"; static const char txtForceWindowType[] = "$Lee"; // Pointer to global array of window types. HWND xHtmlHelpW(HWND hwndCaller, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData) { CStr cszFile; if (pszFile) cszFile = (WCHAR*) pszFile; switch (uCommand) { case HH_DISPLAY_TEXT_POPUP: { HH_POPUP* ppopup = (HH_POPUP*) dwData; CStr cszString; if (!ppopup->idString) { cszString = (WCHAR*) ppopup->pszText; ppopup->pszText = cszString.psz; } CStr cszFont; if (ppopup->pszFont) { cszFont = (WCHAR*) ppopup->pszFont; ppopup->pszFont = cszFont.psz; } return xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); } case HH_DISPLAY_INDEX: case HH_GET_WIN_HANDLE: case HH_DISPLAY_TOPIC: if (dwData && !IsBadReadPtr((LPCSTR) dwData, sizeof(LPCSTR))) { char szURL[MAX_PATH]; szURL[WideCharToMultiByte(CP_ACP, 0, (const WCHAR*) dwData, -1, szURL, MAX_PATH, NULL, NULL)] = '\0'; return xHtmlHelpA(hwndCaller, cszFile, uCommand, (DWORD_PTR) szURL); } else return xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); case HH_KEYWORD_LOOKUP: { HH_AKLINK* pakLink = (HH_AKLINK*) dwData; if (IsBadReadPtr(pakLink, sizeof(HH_AKLINK*))) // BUGBUG: nag the help author return NULL; pakLink->fReserved = TRUE; return xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); } case HH_SET_INFO_TYPE: { PHH_SET_INFOTYPE pSetInfoType = *(PHH_SET_INFOTYPE *)dwData; CStr cszType; CStr cszCat; if (*pSetInfoType->pszCatName != NULL) { cszCat = (WCHAR*) pSetInfoType->pszCatName; pSetInfoType->pszCatName = cszCat.psz; } if (*pSetInfoType->pszInfoTypeName != NULL) { cszType = (WCHAR*) pSetInfoType->pszInfoTypeName; pSetInfoType->pszInfoTypeName = cszType.psz; } return xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); } case HH_ENUM_INFO_TYPE: { PHH_ENUM_IT penumIT; penumIT = (PHH_ENUM_IT)(*(PHH_ENUM_IT*)dwData); HWND ret = xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); if ( ret != (HWND)-1 ) { CHmData* phmData = FindCurFileData( cszFile.psz); if ( phmData == NULL ) return (HWND)-1; phmData->PopulateUNICODETables(); penumIT->pszITName = phmData->m_ptblIT->GetPointer((int)(DWORD_PTR)ret); penumIT->pszITDescription = phmData->m_ptblIT_Desc->GetPointer((int)(DWORD_PTR)ret); } return ret; } case HH_ENUM_CATEGORY: { PHH_ENUM_CAT penumCat; HWND ret = xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); if ( ret != (HWND)-1 ) { penumCat = *(PHH_ENUM_CAT*) dwData; CHmData* phmData = FindCurFileData( cszFile.psz); if ( phmData == NULL ) return (HWND)-1; phmData->PopulateUNICODETables(); penumCat->pszCatName = phmData->m_ptblCat->GetPointer((int)(DWORD_PTR)ret+1); penumCat->pszCatDescription = phmData->m_ptblCat_Desc->GetPointer((int)(DWORD_PTR)ret+1); } return ret; } case HH_ENUM_CATEGORY_IT: { PHH_ENUM_IT penumIT; CStr csz; penumIT = *(PHH_ENUM_IT*)dwData; csz = (WCHAR*) penumIT->pszCatName; penumIT->pszCatName = csz.psz; HWND ret = xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); if ( ret != (HWND)-1 ) { CHmData* phmData = FindCurFileData( cszFile.psz); if ( phmData == NULL ) return (HWND)-1; phmData->PopulateUNICODETables(); penumIT->pszITName = phmData->m_ptblIT->GetPointer((int)(DWORD_PTR)ret); penumIT->pszITDescription = phmData->m_ptblIT_Desc->GetPointer((int)(DWORD_PTR)ret); } return ret; } case HH_SET_GUID: { // Set all CHM files matching this process ID to use this GUID // BUGBUG: should NOT be using a global for this -- breaks // the moment you share instances of HHCTRL accross processes if (g_pszDarwinGuid) lcFree(g_pszDarwinGuid); // Convert to ANSI CStr pszGuid((LPCWSTR) dwData); // Save pointer. pszGuid.TransferPointer(&g_pszDarwinGuid ) ; } return NULL; case HH_SET_BACKUP_GUID: { // Set all CHM files matching this process ID to use this GUID // BUGBUG: should NOT be using a global for this -- breaks // the moment you share instances of HHCTRL accross processes if (g_pszDarwinBackupGuid) lcFree(g_pszDarwinBackupGuid); // Convert to ANSI CStr pszGuid((LPCWSTR) dwData); // Save pointer. pszGuid.TransferPointer(&g_pszDarwinBackupGuid) ; } return NULL; default: return xHtmlHelpA(hwndCaller, cszFile, uCommand, dwData); } return NULL; } HWND xHtmlHelpA(HWND hwndCaller, LPCSTR pszFile, UINT uCommand, DWORD_PTR dwData) { #if 0 #ifdef _DEBUG static int count = 0; if (!count) DebugBreak(); count++; #endif #endif switch (uCommand) { case HH_RESERVED1: // this is a WinHelp HELP_CONTEXTMENU call WinHelp(hwndCaller, pszFile, HELP_CONTEXTMENU, dwData); return NULL; case HH_RESERVED2: // this is a WinHelp HELP_FINDER call WinHelp(hwndCaller, pszFile, HELP_FINDER, 0); return NULL; case HH_RESERVED3: // this is a WinHelp HELP_WM_HELP call WinHelp(hwndCaller, pszFile, HELP_WM_HELP, dwData); return NULL; case HH_DISPLAY_TOPIC: DBWIN("HH_DISPLAY_TOPIC"); return OnDisplayTopic(hwndCaller, pszFile, dwData); case HH_SET_WIN_TYPE: DBWIN("HH_SET_WIN_TYPE"); return SetWinType(pszFile, (HH_WINTYPE*) dwData) ; case HH_GET_WIN_TYPE: DBWIN("HH_GET_WIN_TYPE"); return GetWinType(hwndCaller, pszFile, (HH_WINTYPE**)dwData) ; case HH_DISPLAY_SEARCH: DBWIN("HH_DISPLAY_SEARCH"); return doDisplaySearch(hwndCaller, pszFile, (HH_FTS_QUERY*) dwData) ; case HH_DISPLAY_INDEX: DBWIN("HH_DISPLAY_INDEX"); return doDisplayIndex(hwndCaller, pszFile, (LPCTSTR)dwData); case HH_DISPLAY_TOC: DBWIN("HH_DISPLAY_TOC"); if (IsThisAWinHelpFile(hwndCaller, pszFile)) { WinHelp(hwndCaller, pszFile, HELP_CONTEXT, dwData); return NULL; } return doDisplayToc(hwndCaller, pszFile, dwData); case HH_DISPLAY_TEXT_POPUP: DBWIN("HH_DISPLAY_TEXT_POPUP"); return doDisplayTextPopup(hwndCaller, pszFile, (HH_POPUP*)dwData) ; case HH_TP_HELP_WM_HELP: DBWIN("HH_TP_HELP_WM_HELP"); if (IsThisAWinHelpFile(hwndCaller, pszFile)) { WinHelp(hwndCaller, pszFile, HELP_WM_HELP, dwData); return NULL; } return doTpHelpWmHelp(hwndCaller, pszFile, dwData); case HH_TP_HELP_CONTEXTMENU: DBWIN("HH_TP_HELP_CONTEXTMENU"); if (IsThisAWinHelpFile(hwndCaller, pszFile)) { WinHelp(hwndCaller, pszFile, HELP_CONTEXTMENU, dwData); return NULL; } return doTpHelpContextMenu(hwndCaller, pszFile, dwData); case HH_GET_WIN_HANDLE: { DBWIN("HH_GET_WIN_HANDLE"); if (!dwData || IsBadReadPtr((LPCSTR) dwData, sizeof(LPCSTR))) { return NULL; } if (!pszFile || IsEmptyString(pszFile)) { return NULL ; } // Need to include compiled filename with window lookup // since there can be two or more .CHM files with identical window type // names, but different definitions CHHWinType* phh = FindWindowType(FirstNonSpace((PCSTR) dwData), hwndCaller, pszFile); if (!phh) { return NULL; } return phh->GetHwnd(); } case HH_SYNC: { DBWIN("HH_SYNC"); // pszFile has two pieces of information which we need. The filename and the window type name. // Window type provided? CStr cszChmName ; CStr cszWindow ; if (!GetNameAndWinType(pszFile, cszChmName, cszWindow)) { return NULL; } /* REVIEW: 27 Apr 98 [dalero] - I'm adding the code to make FindOrCreateWindowSlot take a filename parameter, and I have to say that this function makes no sense to me. Why does HH_SYNC require a window type? Shouldn't it figure one out if you don't give it one? Leave as is for now... */ CHHWinType* phh = FindWindowType(cszWindow, hwndCaller, cszChmName); if (phh->m_aNavPane[HH_TAB_CONTENTS]) { CStr cszUrl((PCSTR) dwData); CToc* ptoc = reinterpret_cast(phh->m_aNavPane[HH_TAB_CONTENTS]) ; // HACKHACK: Should use dynamic cast. ptoc->Synchronize(cszUrl); } return phh->GetHwnd(); } case HH_KEYWORD_LOOKUP: DBWIN("HH_KEYWORD_LOOKUP"); return OnKeywordSearch(hwndCaller, pszFile, (HH_AKLINK*) dwData); case HH_ALINK_LOOKUP: DBWIN("HH_ALINK_LOOKUP"); return OnKeywordSearch(hwndCaller, pszFile, (HH_AKLINK*) dwData, FALSE); case HH_HELP_CONTEXT: DBWIN("HH_HELP_CONTEXT"); return OnHelpContext(hwndCaller, pszFile, dwData); case HH_CLOSE_ALL: { DBWIN("HH_CLOSE_ALL"); DeleteWindows() ; // This deletes everything. Windows and chm data. } return NULL; case HH_GET_LAST_ERROR: DBWIN("HH_GET_LAST_ERROR"); if (SUCCEEDED(hhGetLastError((HH_LAST_ERROR*)(dwData)))) return (HWND)TRUE; else return NULL; case HH_ENUM_INFO_TYPE: { DBWIN("HH_ENUM_INFO_TYPE"); static HH_ENUM_IT ITData; static PHH_ENUM_IT pITData=&ITData; CHmData* phmData = FindCurFileData( pszFile ) ; if ( phmData == NULL ) return (HWND)-1; if ( phmData->m_cur_IT == 0 ) phmData->m_cur_IT = 1; if ( !phmData->m_pInfoType ) { phmData->m_pInfoType = new CInfoType(); phmData->m_pInfoType->CopyTo( phmData ); } // get the information type if ( phmData->m_cur_IT > phmData->m_pInfoType->HowManyInfoTypes() ) { phmData->m_cur_IT = 0; return (HWND)-1; } ITData.iType = IT_INCLUSIVE; if ( phmData->m_pInfoType->IsHidden( phmData->m_cur_IT) ) ITData.iType = IT_HIDDEN; else if ( phmData->m_pInfoType->IsExclusive( phmData->m_cur_IT ) ) ITData.iType = IT_EXCLUSIVE; ITData.pszITName = phmData->m_pInfoType->GetInfoTypeName(phmData->m_cur_IT); ITData.pszITDescription = phmData->m_pInfoType->GetInfoTypeDescription(phmData->m_cur_IT); ITData.cbStruct = sizeof(HH_ENUM_IT); memcpy(*(PHH_ENUM_IT*)dwData, (PHH_ENUM_IT)(pITData), sizeof(HH_ENUM_IT) ); phmData->m_cur_IT++; return reinterpret_cast((DWORD_PTR)(phmData->m_cur_IT-1)); } case HH_SET_INFO_TYPE: DBWIN("HH_SET_INFO_TYPE"); if (IsThisAWinHelpFile(hwndCaller, pszFile)) { WinHelp(hwndCaller, pszFile, HELP_CONTEXTPOPUP, dwData); return NULL; } { HH_SET_INFOTYPE set_type; CStr cszIT; CHmData* phmData = FindCurFileData( pszFile ); if ( phmData == NULL ) return (HWND)-1; memcpy(&set_type, *(PHH_SET_INFOTYPE*)dwData, (int)(**(int**)dwData)); if ( set_type.pszCatName && *set_type.pszCatName!= NULL) { cszIT = set_type.pszCatName; cszIT+=":"; } if ( set_type.pszInfoTypeName ) if ( cszIT.IsEmpty() ) cszIT = set_type.pszInfoTypeName; else cszIT += set_type.pszInfoTypeName; else return (HWND)-1; if ( !phmData->m_pInfoType ) { phmData->m_pInfoType = new CInfoType(); phmData->m_pInfoType->CopyTo( phmData ); } int IT = phmData->m_pInfoType->GetInfoType( cszIT.psz ); if ( IT <= 0 ) return (HWND)-1; if (!phmData->m_pAPIInfoTypes ) phmData->m_pAPIInfoTypes = (INFOTYPE*)lcCalloc(phmData->m_pInfoType->InfoTypeSize()); AddIT(IT, phmData->m_pAPIInfoTypes); return reinterpret_cast((DWORD_PTR)(IT)); } case HH_ENUM_CATEGORY: DBWIN("HH_ENUM_CATEGORY"); { static HH_ENUM_CAT ITData; static PHH_ENUM_CAT pITData = &ITData; CHmData* phmData = FindCurFileData( pszFile ); if ( phmData == NULL ) return (HWND)-1; if ( !phmData->m_pInfoType ) { phmData->m_pInfoType = new CInfoType(); phmData->m_pInfoType->CopyTo( phmData ); } if ( phmData->m_cur_Cat+1 > phmData->m_pInfoType->HowManyCategories() ) { phmData->m_cur_Cat = 0; return (HWND)-1; } ITData.pszCatName = phmData->m_pInfoType->GetCategoryString(phmData->m_cur_Cat+1); ITData.pszCatDescription = phmData->m_pInfoType->GetCategoryDescription(phmData->m_cur_Cat+1); ITData.cbStruct = sizeof(HH_ENUM_CAT); memcpy(*(PHH_ENUM_CAT*)dwData, (PHH_ENUM_CAT)(pITData), sizeof(HH_ENUM_CAT) ); phmData->m_cur_Cat++; return reinterpret_cast((DWORD_PTR)(phmData->m_cur_Cat-1)); } case HH_ENUM_CATEGORY_IT: { DBWIN("HH_ENUM_CATEGORY_IT"); static HH_ENUM_IT ITData; static PHH_ENUM_IT pITData = &ITData; CHmData* phmData = FindCurFileData( pszFile ); if ( phmData == NULL ) return (HWND)-1; if ( phmData->m_cur_IT == -1 ) { phmData->m_cur_Cat = 0; phmData->m_cur_IT = 0; } if ( !phmData->m_pInfoType ) { phmData->m_pInfoType = new CInfoType(); phmData->m_pInfoType->CopyTo( phmData ); } if ( !dwData ) return (HWND)-1; memcpy(&ITData, *(PHH_ENUM_IT*)dwData, (int)(**(int**)dwData)); phmData->m_cur_Cat = phmData->m_pInfoType->GetCatPosition(ITData.pszCatName); if( phmData->m_cur_Cat == -1 ) return (HWND)-1; if ( phmData->m_cur_IT ==0 ) phmData->m_cur_IT = phmData->m_pInfoType->GetFirstCategoryType( phmData->m_cur_Cat-1 ); else phmData->m_cur_IT = phmData->m_pInfoType->GetNextITinCategory( ); if ( phmData->m_cur_IT == -1 ) return (HWND)-1; ITData.iType = IT_INCLUSIVE; if ( phmData->m_pInfoType->IsHidden( phmData->m_cur_IT) ) ITData.iType = IT_HIDDEN; else if ( phmData->m_pInfoType->IsExclusive( phmData->m_cur_IT ) ) ITData.iType = IT_EXCLUSIVE; ITData.pszITName = phmData->m_pInfoType->GetInfoTypeName( phmData->m_cur_IT ); ITData.pszITDescription = phmData->m_pInfoType->GetInfoTypeDescription( phmData->m_cur_IT ); memcpy(*(PHH_ENUM_IT*)dwData, (PHH_ENUM_IT)(pITData), sizeof(HH_ENUM_IT) ); return reinterpret_cast((DWORD_PTR)(phmData->m_cur_IT)); } case HH_SET_INCLUSIVE_FILTER: DBWIN("HH_SET_INCLUSIVE_FILTER"); { CHmData* phmData=NULL; phmData = FindCurFileData( pszFile ); if ( phmData == NULL ) return (HWND)-1; if ( !phmData->m_pInfoType ) { phmData->m_pInfoType = new CInfoType(); phmData->m_pInfoType->CopyTo( phmData ); } if ( !phmData->m_pAPIInfoTypes ) return (HWND)-1; *phmData->m_pAPIInfoTypes &= ~1L; // turn bit zero off for inclusive filtering; ie to 0. return NULL; } break; case HH_SET_EXCLUSIVE_FILTER: DBWIN("HH_SET_EXCLUSIVE_FILTER"); { CHmData* phmData=NULL; phmData = FindCurFileData( pszFile ); if ( phmData == NULL ) return (HWND)-1; if ( !phmData->m_pInfoType ) { phmData->m_pInfoType = new CInfoType(); phmData->m_pInfoType->CopyTo( phmData ); } if ( !phmData->m_pAPIInfoTypes ) return (HWND)-1; *phmData->m_pAPIInfoTypes |= 1L; // turn bit zero on for exclusive filtering; ie to 1. return NULL; } break; case HH_RESET_IT_FILTER: DBWIN("HH_RESET_IT_FILTER"); { CHmData* phmData=NULL; BOOL fExclusive; phmData = FindCurFileData( pszFile ); if ( phmData == NULL ) return (HWND)-1; if ( !phmData->m_pInfoType ) { phmData->m_pInfoType = new CInfoType(); phmData->m_pInfoType->CopyTo( phmData ); } if ( !phmData->m_pAPIInfoTypes ) return NULL; if ( *phmData->m_pAPIInfoTypes & 1L ) fExclusive = TRUE; memset(phmData->m_pAPIInfoTypes, '\0', phmData->m_pInfoType->InfoTypeSize() ); if ( fExclusive ) *phmData->m_pAPIInfoTypes |= 1L; // turn bit zero on for exclusive filtering; ie to 1. return NULL; } case HH_INITIALIZE: return (HWND)(DWORD_PTR)InitializeSession(reinterpret_cast(dwData)) ; case HH_UNINITIALIZE: return (HWND)(DWORD_PTR)UninitializeSession(dwData) ; //--- These were internal and are now external. case HH_PRETRANSLATEMESSAGE: return (HWND)(DWORD_PTR)hhPreTranslateMessage((MSG*)dwData) ; case HH_SET_GLOBAL_PROPERTY: { CHHWinType *phh = FindCurWindow( ); return (HWND)SetGlobalProperty(reinterpret_cast(dwData), phh) ; } //--- Internal HH Commands. case HH_TITLE_PATHNAME : // Get the location of the title from its tag. DBWIN("HH_TITLE_PATHNAME"); return reinterpret_cast((DWORD_PTR)(GetLocationFromTitleTag(NULL/*pszFile*/, reinterpret_cast(dwData)))) ; case HH_PRETRANSLATEMESSAGE2: return (HWND)(DWORD_PTR)hhPreTranslateMessage((MSG*)dwData, hwndCaller); case HH_HELP_CONTEXT_COLLECTION: DBWIN("HH_HELP_CONTEXT_COLLECTION"); return OnHelpContextInCollection(hwndCaller, pszFile, dwData) ; case HH_RELOAD_NAV_DATA: { return OnReloadNavData(hwndCaller, pszFile, reinterpret_cast(dwData)) ; } case HH_GET_BROWSER_INTERFACE: { return reinterpret_cast(GetBrowserInterface(hwndCaller, reinterpret_cast(dwData))) ; } default: DBWIN("Unsupported API call"); return NULL; } } // As per HH bug 7487 NT5 bug 303099 I am placing a check here to see if we // appear to have a valid IE intallation. // BOOL ValidateIE() { BOOL bRet = FALSE; HKEY hkey; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Internet Explorer", 0, KEY_READ, &hkey) == ERROR_SUCCESS) { char szVersion[MAX_URL]; DWORD cbPath = sizeof(szVersion); if ( (RegQueryValueEx(hkey, "Version", NULL, NULL, (LPBYTE) szVersion, &cbPath) == ERROR_SUCCESS) ) bRet = TRUE; else if ( (RegQueryValueEx(hkey, "IVer", NULL, NULL, (LPBYTE) szVersion, &cbPath) == ERROR_SUCCESS) ) bRet = TRUE; else if ( (RegQueryValueEx(hkey, "Build", NULL, NULL, (LPBYTE) szVersion, &cbPath) == ERROR_SUCCESS) ) bRet = TRUE; RegCloseKey(hkey); } return bRet; } /*************************************************************************** FUNCTION: OnDisplayTopic PURPOSE: Display topic in current or specified window, creating the window if necessary. PARAMETERS: hwndCaller -- window requesting this topic pszFile -- file and optionally the window to display dwData -- optional data RETURNS: Window handle on success NULL on failure COMMENTS: A window name is specified by following the filename (if any) with a '>' character immediately followed by the window name: foo.hh>proc4 If a window of the specified type isn't currently created, it will be created and activated. If the window type has been created, but is not active, it will be activated and the topic displayed in that window. MODIFICATION DATES: 27-Feb-1996 [ralphw] ***************************************************************************/ HWND OnDisplayTopic(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData) { if (dwData && IsBadReadPtr((LPCSTR) dwData, sizeof(LPCSTR))) return NULL ; // As per HH bug 7487 NT5 bug 303099 I am placing a check here to see if we // appear to have a valid IE intallation. // if ( ! ValidateIE() ) return NULL; //--- If name has string %SystemRoot% add the windows directory. CStr cszTmp; PSTR psz = stristr(pszFile, txtSysRoot); if (psz) { char szPath[MAX_PATH]; GetRegWindowsDirectory(szPath); strcat(szPath, psz + strlen(txtSysRoot)); cszTmp = szPath; pszFile = (PCSTR) cszTmp.psz; } //--- Find the window name. CStr cszFile(pszFile); CStr cszWindow; PSTR pszWindow = StrChr(cszFile, WINDOW_SEPARATOR); if (pszWindow != NULL) { *pszWindow = '\0'; RemoveTrailingSpaces(cszFile); cszWindow = FirstNonSpace(pszWindow + 1); } // Is the file a collection? BOOL bCollection = IsCollectionFile(pszFile); //--- dwData can point to a particular topic to which to jump. Add this topic. CStr cszExternal; if (dwData) { if (cszFile.IsEmpty()) cszFile = (LPCSTR) dwData; else { //--- Hack: If there is a '::' assume we are specifying a chm file... PCSTR psz = (LPCSTR) dwData; if (bCollection && strstr(psz, "::")) { // Only valid if we are passing in a collection. cszExternal = psz; // If there is a window type at the end, remove it. We get the window type from the collection. PSTR pszWindowType = StrChr(cszExternal, WINDOW_SEPARATOR); if (pszWindowType != NULL) { *pszWindowType = '\0'; } } else { cszFile += txtSepBack; cszFile += (*psz == '/' ? psz + 1 : psz); } } } //--- CHmData used by the CreateWindow call CHmData* pHmDataCreateWindow = NULL ; //--- We have a filename from which we can get the file. CHHWinType* phh = NULL; if (bCollection || IsCompiledHtmlFile(cszFile, &cszFile)) { //--- Initialize the CHmData structure. CStr cszCompressed; PCSTR pszFilePortion = GetCompiledName(cszFile, &cszCompressed); // pszFilePortion is everything is cszFile after the '::', in other words the topic path. if (!FindThisFile(hwndCaller, cszCompressed, &cszCompressed, FALSE)) { //TODO: Move error message into FindThisFile. g_LastError.Set(HH_E_FILENOTFOUND) ; return NULL; } CHmData* phmData = NULL; CExCollection* pCollection = GetCurrentCollection(NULL, pszFile); // // if ( .COL file AND g_Col != returned col ) OR ( .CHM file and g_col == returned col ) // if ( !pCollection || ((bCollection && (pCollection != g_pCurrentCollection)) || (!bCollection && (pCollection == g_pCurrentCollection)) || (!bCollection && (pCollection == g_phmData[0]->m_pTitleCollection)))) //Bug:: May not have been loaded!!! { phmData = FindCurFileData(cszCompressed); } else { phmData = pCollection->m_phmData; } if (phmData == NULL) { g_LastError.Set(HH_E_INVALIDHELPFILE) ; // TODO: FindCurFileData should set this. return NULL; } ASSERT(phmData) ; // See the previous if statement! // IsCompiledHtmlFile has numerous side effects. We need those effects. if (bCollection) { // Get the name of the master chm. cszFile = phmData->GetCompiledFile(); // Parse it to get the various pieces. IsCompiledHtmlFile(cszFile, &cszFile); } // Get the CHmData associated with the specified window type. if (cszWindow.IsNonEmpty() && !IsGlobalWinType(cszWindow.psz)) // Only use the information from window type if its non-global. Currently, means get the default topic from CHM passed in. { phh = FindWindowType(cszWindow, hwndCaller, cszFile); if (phh) { if (phh->m_phmData) // Bug 6799 - This is NULL until the window is created... { phmData = phh->m_phmData; //Review: Is the FindCurFileData above a waste in this case? } } else if (strcmp(cszWindow, txtDefWindow + 1) != 0 && strcmp(cszWindow, txtForceWindowType) != 0) { AuthorMsg(IDSHHA_NO_HH_GET_WIN_TYPE, cszWindow, hwndCaller, NULL); // Get the CHmData associated with the default window. // We could do this in the section outside the outer if, but I'm paranoid about breaking things... // The default window type is used only if it exists. Therefore, if we have to create a window, // will use the correct name. if (phmData->GetDefaultWindow() && FindWindowType(phmData->GetDefaultWindow(), hwndCaller, cszFile)) { AuthorMsg(IDSHHA_NO_HH_GET_WIN_TYPE, cszWindow, hwndCaller, NULL); cszWindow = phmData->GetDefaultWindow(); } } } // Get the CHmData associated with the default window. if (cszWindow.IsEmpty() && phmData->GetDefaultWindow()) cszWindow = phmData->GetDefaultWindow(); //--- If the user didn't supply a topic, try and find one... if (pszFilePortion) { // If we have a topic, make sure that the slashes are correct. ConvertBackSlashToForwardSlash(const_cast(pszFilePortion)) ; // Danger! Danger! Will Robinson! Casting away const on a pointer inside of a CStr... } else { LPCTSTR pTopic = NULL ; if (phh && phh->pszFile) // the window type has a topic { pTopic = phh->pszFile ; } else if (phmData && phmData->GetDefaultHtml()) // the CHmData struct's default html page. { pTopic = phmData->GetDefaultHtml() ; } if (pTopic) { if (*pTopic == '/') // Look for an initial separator. { pTopic++ ; // Skip the initial separator. } cszFile += txtSepBack; cszFile += pTopic ; } } // Get the CHmData structure we want to use with the window. if (cszExternal.IsEmpty()) pHmDataCreateWindow = g_phmData[g_curHmData] ; else pHmDataCreateWindow = phmData ; // CHmdata for the collection. } // if IsCompiledFile. if (cszWindow.IsEmpty() && (!phh || phh->m_pCIExpContainer == NULL)) cszWindow = txtForceWindowType; //--- The user supplied a window type or we have a default window type. if (cszWindow.IsNonEmpty()) { CStr cszChmFile; if (cszFile.IsNonEmpty()) GetCompiledName(cszFile, &cszChmFile); //Have to call this again, because pszFile was built back up. phh = FindWindowType(cszWindow, hwndCaller, cszChmFile); // Review: We may have already gotten the window type. if (!phh) // Can't find the window type, so create it. { if (strcmp(cszWindow, txtDefWindow + 1) != 0 && strcmp(cszWindow, txtForceWindowType) != 0) AuthorMsg(IDSHHA_NO_HH_GET_WIN_TYPE, cszWindow, hwndCaller, NULL); CreateDefaultWindowType(cszChmFile.IsEmpty() ? txtZeroLength : cszChmFile.psz, cszWindow); // This only does a HH_SET_WIN_TYPE phh = FindWindowType(cszWindow, hwndCaller, pszFile); // Try again to get the window type. } // We still don't have a valid window type or we don't have a valid window. if (!phh || !IsValidWindow(phh->GetHwnd())) { // Create the help window. This does a lot of work. phh = CreateHelpWindow(cszWindow, cszChmFile, hwndCaller, pHmDataCreateWindow); if (!phh) { g_LastError.Set(HH_E_INVALIDHELPFILE) ; // TODO: FindCurFileData should set this. return NULL ; } RECT rc; GetClientRect(phh->GetHwnd(), &rc); SendMessage(phh->GetHwnd(), WM_SIZE, SIZE_RESTORED, MAKELONG(RECT_WIDTH(rc),RECT_HEIGHT(rc))); // HTML Help Bug 6334 - For some reason on win95, we have a problem with coming up in the background. // Not sure why, however the following code which is basically what happens if we don't have // a window fixes the issue. if (!IsIconic(phh->GetHwnd())) { phh->SetActiveHelpWindow(); } } else { // Bring the existing window to the front and restore if needed. phh->SetActiveHelpWindow(); } ASSERT(phh) ; // This has to be true by now. // Still futzing with the filename. Review: Reduce the futzing. if (cszFile.IsEmpty()) { if (phh->pszFile) cszFile = phh->pszFile; } } // If we are displaying the file in the collection space, just use the string passed in // the dwData parameter. return ChangeHtmlTopic(cszExternal.IsNonEmpty() ? cszExternal : cszFile, phh->GetHwnd()); } HWND ChangeHtmlTopic(PCSTR pszFile, HWND hwndChild, BOOL bHighlight) { if (IsEmptyString(pszFile)) return NULL; CHHWinType* phh; ASSERT_COMMENT(hwndChild, "It is not valid to call ChangeHtmlTopic with NULL for the calling window handle"); phh = FindHHWindowIndex(hwndChild); CStr cszTmp; if ( IsCompiledURL( pszFile ) ) { if (!strstr(pszFile, txtDoubleColonSep)) { cszTmp = pszFile; cszTmp += txtDefFile; pszFile = cszTmp; } } if (phh->m_pCIExpContainer) { phh->m_fHighlight = bHighlight; phh->m_pCIExpContainer->m_pWebBrowserApp->Navigate(pszFile, NULL, NULL, NULL, NULL); #if 0 if (phh->IsProperty(HHWIN_PROP_AUTO_SYNC)) { if (phh->m_aNavPane[HH_TAB_CONTENTS]) { cszTmp = pszFile; CToc* ptoc = reinterpret_cast(phh->m_aNavPane[HH_TAB_CONTENTS]) ; // HACKHACK: Should use dynamic cast. ptoc->Synchronize(cszTmp); } } #endif if (IsValidWindow(phh->GetHwnd())) { DBWIN("Bringing window to the front"); SetWindowPos(phh->GetHwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } #ifdef DEBUG else DBWIN("Invalid window handle"); #endif return phh->GetHwnd(); } return NULL; } BOOL ConvertToCacheFile(PCSTR pszSrc, PSTR pszDst) { if (!pszSrc) return FALSE; CStr cszTmp; PSTR psz = stristr(pszSrc, txtSysRoot); if (psz) { char szPath[MAX_PATH]; GetRegWindowsDirectory(szPath); strcat(szPath, psz + strlen(txtSysRoot)); cszTmp = szPath; pszSrc = (PCSTR) cszTmp.psz; } if ( IsCompiledURL( pszSrc ) ) { strcpy(pszDst, pszSrc); if ( (GetURLType(pszDst) == HH_URL_UNQUALIFIED) ) { // We need to qualify the .CHM filespec. // CExCollection *pCollection; CExTitle *pTitle; PSTR pszSep; if( (pCollection = GetCurrentCollection(NULL, pszDst)) ) { if ( (SUCCEEDED(pCollection->URL2ExTitle(pszDst, &pTitle))) && pTitle ) { if ( (pszSep = stristr(pszDst, txtDoubleColonSep)) ) { while ( ((*(--pszSep) != ':')) && (pszSep != pszDst) ); if ( *pszSep == ':' ) *(++pszSep) = '\0'; lstrcat(pszDst, pTitle->GetContentFileName()); pszSep = stristr(pszSrc, txtDoubleColonSep); lstrcat(pszDst, pszSep); return TRUE; } } } } else return TRUE; } PCSTR pszChmSep = strstr(pszSrc, txtDoubleColonSep); if (pszChmSep) { if (pszSrc != cszTmp.psz) { cszTmp = pszSrc; int offset = (int)(pszChmSep - pszSrc); pszSrc = cszTmp; pszChmSep = pszSrc + offset; } *(PSTR) pszChmSep = '\0'; // Remove the separator HRESULT hr = URLDownloadToCacheFile(NULL, pszSrc, pszDst, MAX_PATH, 0, NULL); if (!SUCCEEDED(hr)) { CStr cszNew; BOOL fResult = FindThisFile(NULL, pszSrc, &cszNew, FALSE); if (fResult) { strcpy(pszDst, cszNew.psz); *(PSTR) pszChmSep = ':'; // Put the separator back strcat(pszDst, pszChmSep); return TRUE; } } else { // we downloaded it, or have a pointer to it *(PSTR) pszChmSep = ':'; // Put the separator back strcat(pszDst, pszChmSep); return TRUE; } } HRESULT hr = URLDownloadToCacheFile(NULL, pszSrc, pszDst, MAX_PATH, 0, NULL); if (!SUCCEEDED(hr)) { #if 0 if (MessageBox(NULL, pszSrc, "URLDownloadToCacheFile failure", MB_RETRYCANCEL) == IDRETRY) DebugBreak(); #endif return FALSE; } return TRUE; } void doAuthorMsg(UINT idStringFormatResource, PCSTR pszSubString); int (STDCALL *pHhaChangeParentWindow)(HWND hwndNewParent); extern "C" void AuthorMsg(UINT idStringFormatResource, PCSTR pszSubString, HWND hwndParent, void* phhctrl) { if (g_hmodHHA == NULL) { if (g_fTriedHHA || !LoadHHA(hwndParent, _Module.GetModuleInstance())) return; // no HHA.dll, so not a help author } if (!pHhaChangeParentWindow) { (FARPROC&) pHhaChangeParentWindow = GetProcAddress(g_hmodHHA, MAKEINTATOM(311)); if (!pHhaChangeParentWindow) return; } pHhaChangeParentWindow(hwndParent); if (phhctrl) ((CHtmlHelpControl*)phhctrl)->ModalDialog(TRUE); doAuthorMsg(idStringFormatResource, pszSubString); if (phhctrl) ((CHtmlHelpControl*)phhctrl)->ModalDialog(FALSE); } void CreateDefaultWindowType(PCSTR pszCompiledFile, PCSTR pszWindow) { HH_WINTYPE hhWinType; ZERO_STRUCTURE(hhWinType); hhWinType.cbStruct = sizeof(HH_WINTYPE); hhWinType.fsToolBarFlags = HHWIN_DEF_BUTTONS; hhWinType.pszType = pszWindow; CStr cszToc; // use CStr so we automatically free the memory CStr cszIndex; bool bFail = true ; if (!IsEmptyString(pszCompiledFile) && g_phmData && g_phmData[g_curHmData]) { bool bHasFTS = !((g_phmData[g_curHmData]->m_sysflags.fFTI) == 0) == TRUE; bool bHasToc = IsNonEmptyString(g_phmData[g_curHmData]->GetDefaultToc()) == TRUE; bool bHasIndex = IsNonEmptyString(g_phmData[g_curHmData]->GetDefaultIndex()) == TRUE; bool bHasNavData = (bHasFTS || bHasToc || bHasIndex) ; if (bHasNavData) { hhWinType.fsToolBarFlags |= HHWIN_BUTTON_EXPAND; hhWinType.fsValidMembers |= HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_TB_FLAGS; hhWinType.fsWinProperties |= HHWIN_PROP_CHANGE_TITLE | HHWIN_PROP_TRI_PANE ; if (bHasFTS) { hhWinType.fsWinProperties |= HHWIN_PROP_TAB_SEARCH; } if (bHasToc) { cszToc = pszCompiledFile; cszToc += "::/"; cszToc += g_phmData[g_curHmData]->GetDefaultToc(); hhWinType.pszToc = cszToc.psz; hhWinType.fsWinProperties |= HHWIN_PROP_AUTO_SYNC ; } if (bHasIndex) { cszIndex = pszCompiledFile; cszIndex += "::/"; cszIndex += g_phmData[g_curHmData]->GetDefaultIndex(); hhWinType.pszIndex = cszIndex.psz; if (!bHasToc) // REVIEW: 30 Jun 98 : Can this be removed? [dalero] { hhWinType.curNavType = HHWIN_NAVTYPE_INDEX; hhWinType.fsValidMembers |= HHWIN_PARAM_TABPOS; } } if (IsNonEmptyString(g_phmData[g_curHmData]->GetDefaultHtml())) { CStr csz, cszFull; csz = g_phmData[g_curHmData]->GetDefaultHtml(); if (!stristr(csz, txtDoubleColonSep) && !stristr(csz, txtFileHeader) && !stristr(csz, txtHttpHeader)) { cszFull = g_phmData[g_curHmData]->GetCompiledFile(); cszFull += txtSepBack; cszFull += csz.psz; cszFull.TransferPointer(&hhWinType.pszHome); } else csz.TransferPointer(&hhWinType.pszHome); } if (IsNonEmptyString(g_phmData[g_curHmData]->GetDefaultCaption())) hhWinType.pszCaption = g_phmData[g_curHmData]->GetDefaultCaption(); else hhWinType.pszCaption = lcStrDup(GetStringResource(IDS_DEF_WINDOW_CAPTION)); // We've made a nice default window type. bFail = false ; } } if (bFail) { hhWinType.pszCaption = lcStrDup(GetStringResource(IDS_DEF_WINDOW_CAPTION)); } xHtmlHelpA(NULL, pszCompiledFile, HH_SET_WIN_TYPE, (DWORD_PTR) &hhWinType); } BOOL IsThisAWinHelpFile(HWND hwndCaller, PCSTR pszFile) { if (stristr(pszFile, txtWinHelpFileExt)) { CStr cszFile; if (FindThisFile(hwndCaller, pszFile, &cszFile, FALSE)) { HFILE hf = _lopen(cszFile, OF_READ); if (hf != HFILE_ERROR) { BYTE aMagic[2]; _lread(hf, aMagic, sizeof(aMagic)); _lclose(hf); if (aMagic[0] == '?' && aMagic[1] == '_') { // yep, this is a WinHelp file return TRUE; } } } } return FALSE; } /////////////////////////////////////////////////////////////////////////////// // // This does a Context ID lookup in the CollectionSpace. // HWND OnHelpContextInCollection(HWND hwndCaller, LPCSTR pszColFile, DWORD_PTR dwData) { HH_COLLECTION_CONTEXT* pContext = reinterpret_cast(dwData); //TODO: Validate. if (!pContext) { return NULL ; } HWND hwndReturn = InternalHelpContext(hwndCaller, pszColFile, pContext->id, pContext); if (!hwndReturn) { // We can be left in weird states after failures. // So, we are going to be very aggresive about this and dump all the Hmdata if we don't end up // with a window. We do this by deleting all of the CHHWinType structures. // This is somewhat overkill, but the safest solution. DeleteWindows() ; } return hwndReturn ; } /////////////////////////////////////////////////////////////////////////////// // // This is the original OnHelpContext // HWND OnHelpContext(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData) { return InternalHelpContext(hwndCaller, pszFile, dwData, NULL); } /////////////////////////////////////////////////////////////////////////////// // // Do the actual lookup. // HWND InternalHelpContext(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData, HH_COLLECTION_CONTEXT* pContext) { CExTitle* pExTitle; PSTR pszJumpFile; char szMsg[256]; // Is this a WinHelp file? if (IsThisAWinHelpFile(hwndCaller, pszFile)) { WinHelp(hwndCaller, pszFile, HELP_CONTEXT, dwData); return NULL; } PSTR psz = stristr(pszFile, txtSysRoot); CStr cszTmp; if (psz) { char szPath[MAX_PATH]; GetRegWindowsDirectory(szPath); strcat(szPath, psz + strlen(txtSysRoot)); cszTmp = szPath; pszFile = (PCSTR) cszTmp.psz; } CStr cszFile(pszFile); // copy it so we can modify it CStr cszWindow; PSTR pszWindow = StrChr(cszFile, WINDOW_SEPARATOR); if (pszWindow != NULL) { *pszWindow = '\0'; RemoveTrailingSpaces(cszFile); cszWindow = FirstNonSpace(pszWindow + 1); } //--- We want to be able to do a lookup in the context of a CHM. BOOL bCollection = IsCollectionFile(cszFile); if ((bCollection && pContext) // Only allow lookup in the collection space with valid pContext structure. || IsCompiledHtmlFile(cszFile, &cszFile)) { CStr cszCompressed; PCSTR pszFilePortion = GetCompiledName(cszFile, &cszCompressed); if (!FindThisFile(hwndCaller, cszCompressed, &cszCompressed)) { g_LastError.Set(HH_E_FILENOTFOUND) ; // Let FindThisFile do this when it gets a process id. return NULL; } CHmData* phmData = FindCurFileData(cszCompressed); if (!phmData) { g_LastError.Set(HH_E_INVALIDHELPFILE) ; // TODO: FindCurFileData should set this. return NULL ; } // Handle if we have a collection. if ( bCollection ) { // We have to have the tag name. if (IsEmptyString(pContext->szTag)) { // TODO: use pszFilePortion...((*pszFilePortion == '/') ? pszFilePortion+1 : pszFilePortion return NULL ; } //---Get the path to the CHM. pExTitle = phmData->m_pTitleCollection->FindTitleNonExact(pContext->szTag, LANGIDFROMLCID(pContext->lcid)) ; if (!pExTitle) { return NULL ; } // // We're setting cszFile to the FQ .CHM name for use later in this function (only in the collection case). // cszFile = pExTitle->GetFileName(); // // Yes, This has a side affect we're relying on. We want IsCompiledHtmlFile to decorate the URL for us. // //IsCompiledHtmlFile(cszFile, &cszFile); // Doesn't work in CD swaping case. char szTmp[MAX_PATH*4]; //REVIEW:: Inefficient wsprintf(szTmp, "%s%s", (g_bMsItsMonikerSupport ? txtMsItsMoniker : txtMkStore), //TODO: We are adding moniker information. This should be centralized. cszFile.psz); cszFile = szTmp ; } else { if (! phmData->m_pTitleCollection->IsSingleTitle() ) { g_LastError.Set(HH_E_INVALIDHELPFILE) ; // TODO: FindCurFileData should set this. return NULL; } pExTitle = phmData->GetExTitle(); } // // Now translate the context hash int a URL so we can jump! // HRESULT hr = pExTitle->ResolveContextId((DWORD)dwData, &pszJumpFile); if ( hr != S_OK ) { g_LastError.Set(hr); if (IsHelpAuthor(NULL)) { if ( hr == HH_E_NOCONTEXTIDS ) doAuthorMsg(IDS_HHA_NO_MAP_SECTION, ""); else if ( hr == HH_E_CONTEXTIDDOESNTEXIT ) { wsprintf(szMsg, pGetDllStringResource(IDS_HHA_MISSING_MAP_ID), dwData, cszFile.psz); SendStringToParent(szMsg); } } return NULL; } #if 0 // TEST TEST TEST CStr test("vbcmn98.chm::/html\\vbproMaxMinPropertiesActiveXControls.htm") ; pszJumpFile = test ; #endif //TEST TEST TEST BOOL fIsInterFileJump = IsCompiledHtmlFile(pszJumpFile); char szUrl[MAX_PATH*4]; if (bCollection & fIsInterFileJump) { //--- This is an interfile jump in the context of the collection. // We need a full path to this file so that we can find it. CStr pszChmName; PCSTR pszTopic = GetCompiledName(pszJumpFile, &pszChmName); // Remove the extension. PSTR pszExt = stristr(pszChmName, txtDefExtension); // case insensitive search. if (pszExt) *pszExt = '\0' ; // Look in the global col file for this chm. HH_TITLE_FULLPATH tf ; tf.lcid = pContext->lcid ; tf.szTag = pszChmName ; tf.fullpathname = NULL; if (GetLocationFromTitleTag(NULL/*cszCompressed*/, &tf)) { // Found it. CStr full(tf.fullpathname) ; // Convert. wsprintf(szUrl, "%s%s::%s", (g_bMsItsMonikerSupport ? txtMsItsMoniker : txtMkStore), //TODO: We are adding moniker information. This should be centralized. full.psz, pszTopic); ::SysFreeString(tf.fullpathname); // cleanup. } else { // Didn't find it. hope for the best...maybe its in the windows help directory. strcpy(szUrl, pszJumpFile); } } else if (fIsInterFileJump) { // On an interfile jump we are given the name of the chm and the topic: "help.chm::topic.htm" // So we don't combine the two together. // BUGBUG: This will rarely work, since we will not have a fullpath to the chm. // To work, it must be in the windows help directory. strcpy(szUrl, pszJumpFile); } else { // Combine the chm name with the topic name. wsprintf(szUrl, "%s::/%s", cszFile.psz, pszJumpFile); } // Add on the window. if (cszWindow.IsNonEmpty()) { strcat(szUrl, ">"); strcat(szUrl, cszWindow); } if (IsHelpAuthor(NULL)) { wsprintf(szMsg, pGetDllStringResource(IDS_HHA_HH_HELP_CONTEXT), fIsInterFileJump ? "" : cszFile.psz, dwData, pszJumpFile); SendStringToParent(szMsg); } // Send the COL file to HH_DISPLAY_TOPIC. Also send the chm::htm string. if (bCollection) { return OnDisplayTopic(hwndCaller, cszCompressed, (DWORD_PTR)szUrl); } else { return OnDisplayTopic(hwndCaller, szUrl, NULL); } } else { return NULL ; } } /*************************************************************************** FUNCTION: SetWinType PURPOSE: PARAMETERS: hwndCaller -- window requesting this topic pszFile -- file and window bFindCurdata -- if true, it will call FindCurFileData. Only ReadSystemFiles should call this with True. Its main purpose is to prevent re-entering ReadSystemFiles. RETURNS: COMMENTS: MODIFICATION DATES: 27-Apr-98 ***************************************************************************/ HWND SetWinType(LPCSTR pszFile, HH_WINTYPE* phhWinType, CHmData* phmDataOrg /*= NULL*/) { // Check for uninitialized or invalid pointers if (IsBadReadPtr(phhWinType, sizeof(HH_WINTYPE*))) // BUGBUG: nag the help author return FALSE; if (IsBadReadPtr(phhWinType->pszType, sizeof(PCSTR)) || IsEmptyString(phhWinType->pszType)) // BUGBUG: nag the help author return FALSE; if (IsNonEmptyString(pszFile)) { // If a CHM happens to have a global window type in it, we do not want to re-load the window type information // from the CHM if it has already been loaded. Bug 1.1c 5175 if (IsGlobalWinType(phhWinType->pszType)) { // We are attempting to load the win type information from the CHM. // Check to see if it has already been loaded. CHHWinType* phh = FindWindowType(phhWinType->pszType, NULL, NULL); if (phh) { // Don't over write the window type. return FALSE ; } } // Window types are owned by a particular chm. Unless they are global.... if (!phmDataOrg) { FindCurFileData(pszFile); // We aren't getting loaded by SetWinType. So we don't have an Org file... } } else { // No filename specified. Must be a global window type. if (!IsGlobalWinType(phhWinType->pszType)) { // Force it to be a global window type. _Module.m_GlobalWinTypes.Add(phhWinType->pszType) ; } } // The following code will over write any current definitions of this window type. CHHWinType* phh = FindOrCreateWindowSlot(phhWinType->pszType, pszFile); if (!phh) return FALSE ; CHHWinType* phhNew = (CHHWinType*) phhWinType; phh->SetUniCodeStrings(phhWinType); phh->SetCaption(phhWinType); phh->SetToc(phhWinType); phh->SetIndex(phhWinType); phh->SetFile(phhWinType); phh->SetValidMembers(phhWinType); phh->SetProperties(phhWinType); phh->SetStyles(phhWinType); phh->SetExStyles(phhWinType); phh->SetWindowRect(phhWinType); phh->SetDisplayState(phhWinType); phh->SetNavExpansion(phhWinType); phh->SetNavWidth(phhWinType); phh->SetCaller(phhWinType); phh->SetHome(phhWinType); phh->SetToolBar(phhWinType); phh->SetTabPos(phhWinType); phh->SetTabOrder(phhWinType); phh->SetJump1(phhWinType); phh->SetJump2(phhWinType); phh->SetCurNavType(phhWinType); phh->idNotify = phhWinType->idNotify; if (phmDataOrg) { /* We are being loaded from ReadSystemFiles. We need to store the CHMDATA from which we are created, so that we get the custom tab information from the [options] sections of the correct CHM. The m_pChmData paramter isn't good enough since it is arbitarily set during create window and is over written as a result of the HH_RELOAD_NAV_DATA. */ phh->m_phmDataOrg = phmDataOrg ; } // If there isn't a table of contents, turn off various parameters. if (!phh->IsValidNavPane(HH_TAB_CONTENTS)) { if (phh->IsValidMember(HHWIN_PARAM_TB_FLAGS)) { phh->fsToolBarFlags &= ~HHWIN_BUTTON_TOC_PREV ; phh->fsToolBarFlags &= ~HHWIN_BUTTON_TOC_NEXT ; } } // If we are a tri-pane window with nothing to expand, // then shut off the automatic expansion if (!phh->AnyValidNavPane()) { phh->fNotExpanded = TRUE; // Bug 1084 Disable the Expand button also. phh->fsToolBarFlags &= ~HHWIN_BUTTON_EXPAND ; } #ifdef _DEBUG DWORD dwOldStyle; #endif // Does this window type name already exist. if (!phh->GetTypeName()) { phh->SetTypeName(phhWinType); return phh->GetHwnd(); } else { // Change the existing window type. // REVIEW: IsValidWindow(phh->GetHwnd() is called TOO often // Set Caption if (phhNew->GetCaption() && IsValidWindow(phh->GetHwnd())) SetWindowText(phh->GetHwnd(), phh->GetCaption()); // Set window styles bool bStyleChanged = false ; if (phhNew->IsValidMember(HHWIN_PARAM_STYLES)) { phh->AddStyle(WS_VISIBLE | WS_CLIPSIBLINGS); //REVIEW: Why isn't this part of DEFAULT_STYLE? if (IsValidWindow(phh->GetHwnd())) { #ifdef _DEBUG dwOldStyle = GetWindowLong(*phh, GWL_STYLE); #endif SetWindowLong(*phh, GWL_STYLE, phh->GetStyles()); bStyleChanged = true; } } // Change extended window styles. if (phhNew->IsValidMember(HHWIN_PARAM_EXSTYLES)) { if (!(phhNew->IsProperty(HHWIN_PROP_NODEF_EXSTYLES))) { if (IsValidWindow(phh->GetHwnd())) phh->AddStyle(GetWindowLong(phh->GetHwnd(), GWL_EXSTYLE)); if (phhWinType->fsWinProperties & HHWIN_PROP_ONTOP) phh->AddStyle(WS_EX_TOPMOST); } if (IsValidWindow(phh->GetHwnd())) { #ifdef _DEBUG dwOldStyle = GetWindowLong(phh->GetHwnd(), GWL_EXSTYLE); #endif SetWindowLong(phh->GetHwnd(), GWL_EXSTYLE, phh->GetExStyles()); bStyleChanged = true; } } if (bStyleChanged) { // Must call this for SetWindowLong to take affect. See docs for SetWindowPos and SetWindowLong. SetWindowPos(phh->GetHwnd(), NULL, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED) ; } // Change the rect. if (phhNew->IsValidMember(HHWIN_PARAM_RECT)) { phh->GetWindowRect(); if (phhNew->GetLeft() >= 0) phh->rcWindowPos.left = phhNew->GetLeft(); if (phhNew->GetRight() >= 0) phh->rcWindowPos.right = phhNew->GetRight(); if (phhNew->GetTop() >= 0) phh->rcWindowPos.top = phhNew->GetTop(); if (phhNew->GetBottom() >= 0) phh->rcWindowPos.bottom = phhNew->GetBottom(); if (IsValidWindow(*phh)) MoveWindow(*phh, phh->GetLeft(), phh->GetTop(), phh->GetWidth(), phh->GetHeight(), TRUE); } // Change the show state. if (phhNew->IsValidMember(HHWIN_PARAM_SHOWSTATE)) { if (IsValidWindow(phh->GetHwnd())) ShowWindow(phh->GetHwnd(), phh->GetShowState()); } return phh->GetHwnd(); } } /*************************************************************************** FUNCTION: GetWinType PURPOSE: PARAMETERS: hwndCaller -- window requesting this topic pszFile -- file and window RETURNS: COMMENTS: MODIFICATION DATES: 27-Apr-98 ***************************************************************************/ HWND GetWinType(HWND hwndCaller, LPCSTR pszFile, HH_WINTYPE** pphh) { CStr cszChmName ; CStr cszWindow ; // Support global window types // if(pszFile) { LPCSTR pszWinType = pszFile; if(pszWinType[0]=='>') pszWinType++; if(pszWinType && IsGlobalWinType(pszWinType)) { CHHWinType* phh = FindWindowType(pszWinType, hwndCaller, cszChmName); if (!phh) return (HWND) -1; *pphh = phh; return phh->GetHwnd(); } } // A filename and window type name are required to get the window type. if (!GetNameAndWinType(pszFile, cszChmName, cszWindow)) { return (HWND) -1; } // Read in the chm information. if (!IsCompiledHtmlFile(cszChmName, &cszChmName)) { return (HWND) -1; } CStr cszCompressed ; GetCompiledName(cszChmName, &cszCompressed); CHmData* phm = FindCurFileData(cszCompressed); if (!phm) { return (HWND) -1; } // Need to include compiled filename with window lookup // since there can be two or more .CHM files with identical window type // names, but different definitions CHHWinType* phh = FindWindowType(cszWindow, hwndCaller, cszChmName); if (!phh) { //BUG 5004: Not sure what the purpose of all of this stuff is. However, // cszWindow doesn't have the '>' prefix and txtDefWindow does...[dalero] if (strcmp(cszWindow, (txtDefWindow+1)) != 0) { AuthorMsg(IDSHHA_NO_HH_GET_WIN_TYPE, cszWindow, hwndCaller, NULL); } return (HWND) -1; } *pphh = phh; return phh->GetHwnd(); } /*************************************************************************** FUNCTION: doDisplaySearch PURPOSE: Save as HH_DISPLAY_TOPIC, but forces search tab to front. PARAMETERS: hwndCaller -- window requesting this topic pszFile -- file and optionally the window to display pFtsQuery -- the query information structure. RETURNS: Window handle on success NULL on failure COMMENTS: MODIFICATION DATES: 26-Jun-97 ***************************************************************************/ HWND doDisplaySearch(HWND hwndCaller, LPCSTR pszFile, HH_FTS_QUERY* pFtsQuery) { // // Pre-conditions // // Null pointers ASSERT(IsNonEmptyString(pszFile)); ASSERT(pFtsQuery); if (IsEmptyString(pszFile) || IsBadReadPtr(pFtsQuery, sizeof(HH_FTS_QUERY*))) { FAIL("HH_DISPLAY_SEARCH: pszFile or pFtsQuery is invalid.") ; return NULL; } // Incorrect structure size. if (pFtsQuery->cbStruct != sizeof(HH_FTS_QUERY)) { // REVIEW: means all previous versions fail the instant we change the // structure size. We should just fill in the parts of the structure // we don't know about. FAIL("HH_DISPLAY_SEARCH: pFtsQuery points to structure with incorrect size."); return NULL ; } // // Handle parameters. // // String pointer can be NULL, if search is not seeded. // TODO - Query string. // Change Proximity if (pFtsQuery->iProximity != HH_FTS_DEFAULT_PROXIMITY) { } // Convert Strings if (pFtsQuery->fUniCodeStrings) { FAIL("HH_DISPLAY_SEARCH: fUniCodeStrings is not yet implemented. Ignoring.") ; } // Manipulate options if (pFtsQuery->fStemmedSearch) { FAIL("HH_DISPLAY_SEARCH: fStemmedSearch is not yet implemented. Ignoring.") ; } if (pFtsQuery->fTitleOnly) { FAIL("HH_DISPLAY_SEARCH: fTitleOnly is not yet implemented. Ignoring.") ; } // Can only Execute if there is a search string. if (pFtsQuery->fExecute) { FAIL("HH_DISPLAY_SEARCH: fExecute is not yet implemented. Ignoring.") ; } // Merge the window with the filename. CStr cszFile(pszFile); if (!IsEmptyString(pFtsQuery->pszWindow)) { cszFile += ">"; // BUGBUG: Doesn't handle UNICODE cszFile += pFtsQuery->pszWindow; } // Display the topic HWND hwnd = ReactivateDisplayTopic(hwndCaller, pszFile, 0); // Did it succeed? if (!hwnd || !IsValidWindow(hwnd)) { FAIL("HH_DISPLAY_SEARCH: Could not start help system.") ; return NULL ; } // BUGBUG: 19-Jun-1997 [ralphw] Why are we doing this? It's already on top // Bring help window to front. ::BringWindowToTop(hwnd); // Change the current tab. CHHWinType* phh = FindHHWindowIndex(hwnd); ASSERT(phh); phh->doSelectTab(HH_TAB_SEARCH) ; // TODO: Seed edit box. // Done return hwnd; } /*************************************************************************** FUNCTION: doDisplayIndex PURPOSE: Does a DISPLAY_TOPIC but ensures that the Index tab is selected. PARAMETERS: hwndCaller -- window requesting this topic pszFile -- file and optionally the window to display pszKeyword -- keyword with which to seed edit control RETURNS: Window handle on success NULL on failure COMMENTS: MODIFICATION DATES: 26-Jun-97 [dalero] created. ***************************************************************************/ HWND doDisplayIndex(HWND hwndCaller, LPCSTR pszFile, LPCSTR pszKeyword) { // // Pre-conditions // // Null pointers ASSERT(IsNonEmptyString(pszFile)); if (IsEmptyString(pszFile)) { FAIL("HH_DISPLAY_SEARCH: pszFile or pFtsQuery is invalid.") ; return NULL; } // Display the topic HWND hwnd = ReactivateDisplayTopic(hwndCaller, pszFile, 0); // Did it succeed? if (!hwnd || !IsValidWindow(hwnd)) { FAIL("HH_DISPLAY_SEARCH: Could not start help system.") ; return NULL ; } // Bring help window to front. ::BringWindowToTop(hwnd); // Change the current tab. CHHWinType* phh = FindHHWindowIndex(hwnd); ASSERT(phh); phh->doSelectTab(HH_TAB_INDEX) ; #define _SEED_ON_ #ifdef _SEED_ON_ // Seed Edit Control //ASSERT(phh->m_pindex != NULL) ; if ((phh->m_aNavPane[HH_TAB_INDEX] != NULL) && pszKeyword ) { phh->m_aNavPane[HH_TAB_INDEX]->Seed(pszKeyword) ; } #endif // Done return hwnd; } /*************************************************************************** FUNCTION: doDisplayToc PURPOSE: Does a DISPLAY_TOPIC but ensures that the TOC tab is selected. PARAMETERS: hwndCaller -- window requesting this topic pszFile -- file and optionally the window to display RETURNS: Window handle on success NULL on failure COMMENTS: MODIFICATION DATES: 26-Jun-97 [dalero] created. ***************************************************************************/ HWND doDisplayToc(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData) { // // Pre-conditions // // Null pointers ASSERT(IsNonEmptyString(pszFile)); if (IsEmptyString(pszFile)) { FAIL("HH_DISPLAY_SEARCH: pszFile or pFtsQuery is invalid.") ; return NULL; } HWND hwnd = ReactivateDisplayTopic(hwndCaller, pszFile, dwData); // Did it succeed? if (!hwnd || !IsValidWindow(hwnd)) { FAIL("HH_DISPLAY_SEARCH: Could not start help system.") ; return NULL ; } // Bring help window to front. ::BringWindowToTop(hwnd); // Change the current tab. CHHWinType* phh = FindHHWindowIndex(hwnd); ASSERT(phh); phh->doSelectTab(HH_TAB_CONTENTS) ; // Done return hwnd; } /*************************************************************************** FUNCTION: GetLocationFromTitleTag PURPOSE: Looks in the global col to find the location for this TitleTag PARAMETERS: pszFile --- Filename of the collection to look in. NULL for global.col HH_TITLE_FULLPATH pTitleFullPath RETURNS: NULL on failure COMMENTS: MODIFICATION DATES: 31-Oct-97 [dalero] created. 03-Apr-98 [dalero] the szCollection parameter is currently not used. ***************************************************************************/ int GetLocationFromTitleTag(LPCSTR szCollection, HH_TITLE_FULLPATH* pTitleFullPath) { int iReturn = false ; // Open a dummy collection. CCollection collection ; DWORD e = collection.Open(szCollection ? szCollection : "placeholder") ; if (e == F_OK) { // Locates a title based on id CTitle* pTitle = collection.FindTitleNonExact(pTitleFullPath->szTag, LANGIDFROMLCID(pTitleFullPath->lcid)) ; if (pTitle) { // Always get the last one. LOCATIONHISTORY* pLocationHistory = pTitle->m_pTail;//LOCATIONHISTORY* pLocationHistory = pTitle->GetLocation(/*DWORD Index*/0); if (pLocationHistory && pLocationHistory->FileName) { CWStr wide(pLocationHistory->FileName) ; pTitleFullPath->fullpathname = ::SysAllocString(wide) ; iReturn = true ; } } } return iReturn ; } /*************************************************************************** FUNCTION: ReactivateDisplayTopic PURPOSE: If default window type exists, returns HWND. Otherwise, does an OnDisplayTopic. This allows OnDisplayToc and others to not change the current topic when changing the tab. PARAMETERS: RETURNS: NULL on failure COMMENTS: MODIFICATION DATES: 13 Jan 98 [dalero] created. ***************************************************************************/ HWND ReactivateDisplayTopic(HWND hwndCaller, LPCSTR pszFile, DWORD_PTR dwData) { // A lot of this is copied from OnDisplayTopic... CStr cszFile(pszFile); CStr cszCompressed ; GetCompiledName(cszFile, &cszCompressed); if (FindThisFile(hwndCaller, cszCompressed, &cszCompressed, FALSE)) { // Get the CHmData for this file. CHmData* phmData = FindCurFileData(cszCompressed); // Get the if (phmData) { // Get the info for the default window type structure. CHHWinType* phh = FindWindowType(phmData->GetDefaultWindow(), hwndCaller, cszCompressed); if (phh && phh->GetHwnd() && IsWindow(phh->GetHwnd())) { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(phh->GetHwnd(), &wp); if (wp.showCmd == SW_SHOWMINIMIZED) { ShowWindow(phh->GetHwnd(), SW_RESTORE); } SetForegroundWindow(phh->GetHwnd()); return phh->GetHwnd() ; } } } // Display the topic return OnDisplayTopic(hwndCaller, pszFile, 0); } /*************************************************************************** FUNCTION: ReactivateDisplayTopic PURPOSE: If default window type exists, returns HWND. Otherwise, does an OnDisplayTopic. This allows OnDisplayToc and others to not change the current topic when changing the tab. PARAMETERS: RETURNS: NULL on failure COMMENTS: MODIFICATION DATES: 13 Jan 98 [dalero] created. ***************************************************************************/ bool SetGlobalProperty(HH_GLOBAL_PROPERTY* prop, CHHWinType *phh) { bool bReturn = false ; if (!prop) return bReturn; switch(prop->id) { case HH_GPROPID_SINGLETHREAD: if (prop->var.vt == VT_BOOL) { g_fStandAlone = (prop->var.boolVal == VARIANT_TRUE) ; bReturn = true ; } break ; case HH_GPROPID_TOOLBAR_MARGIN: if ( prop->var.vt == VT_UI4 || prop->var.vt == VT_UINT ) { long L, R; R = HIWORD( prop->var.ulVal ); L = LOWORD( prop->var.ulVal ); if ( (L == g_tbLeftMargin) && (R == g_tbRightMargin) ) return TRUE; g_tbRightMargin = HIWORD( prop->var.ulVal ); g_tbLeftMargin = LOWORD( prop->var.ulVal ); if ( phh && phh->hwndToolBar ) { ::SendMessage(phh->hwndHelp, WM_SIZE, SIZE_RESTORED, (LPARAM)0 ); // Due to a repaint bug in IE 3.02 Comctrl with the toolbar, we need to // reapint the whole toolbar region including the margins on the left and right. RECT rcvoid; rcvoid.top = 0; rcvoid.bottom = RECT_HEIGHT(phh->rcToolBar); rcvoid.left = 0; rcvoid.right = RECT_WIDTH(phh->rcWindowPos); InvalidateRect(phh->hwndHelp, &rcvoid, TRUE); UpdateWindow(phh->hwndHelp); bReturn = true ; } } break; case HH_GPROPID_UI_LANGUAGE:// Set the language for hhctrl's ui. { LANGID request = NULL ; // Convert the val to a LANGID. if (prop->var.vt == VT_I4) { request = static_cast(prop->var.lVal) ; } else if (prop->var.vt = VT_I2) { request = prop->var.iVal ; } else if (prop->var.vt = VT_UI4) { request = prop->var.uiVal ; } else if (prop->var.vt = VT_UI2) { request = static_cast(prop->var.ulVal) ; } if (request) { // Request this langid. LANGID result = _Module.m_Language.SetUiLanguage(request) ; bReturn = (result == request) ; } } break ; case HH_GPROPID_CURRENT_SUBSET: { if ( prop->var.vt == VT_BSTR && prop->var.bstrVal && *(prop->var.bstrVal) ) { WideCharToMultiByte(CP_ACP, 0, prop->var.bstrVal, -1, _Module.szCurSS, MAX_SS_NAME_LEN, NULL, NULL); bReturn = true ; // TODO: Increase robustness! } } break; default: ASSERT(0) ; } return bReturn ; } ////////////////////////////////////////////////////////////////////////// // // GetNameAndWinType ---(from pszfile) Splits out the name and window type. // bool GetNameAndWinType(LPCSTR pszFile, CStr& cszName, CStr& cszWindow) { if (IsEmptyString(pszFile)) { return false ; } // Copy the string. cszName = pszFile; // Parse out window type. PSTR pszWindow = StrChr(cszName, WINDOW_SEPARATOR); if (pszWindow != NULL) { // Terminate the string. *pszWindow = '\0'; RemoveTrailingSpaces(cszName); cszWindow = FirstNonSpace(pszWindow + 1); } else { if(pszFile && IsGlobalWinType(pszFile)) cszWindow = pszFile; } // Must have a window type. if (cszWindow.IsNonEmpty()) { // Must either be a global window type, or have a filename. if (IsGlobalWinType(cszWindow) || cszName.IsNonEmpty()) { return true ; } } return false; } ////////////////////////////////////////////////////////////////////////// // // Reload the nav panes with data from a different CHM. // NOTE: This is a dangerous Hack for Office. // HWND OnReloadNavData(HWND hwndCaller, LPCSTR pszFile, HH_NAVDATA* pNavData) { // Remember that the members of pNavData are unicode... if (IsNonEmptyString(pszFile) // pszFile is not used //|| !IsValidWindow(hwndCaller) --- Allows reparenting. See FindWindowType || !pNavData || IsEmptyStringW(pNavData->pszName) || IsEmptyStringW(pNavData->pszFile) || !IsGlobalWinTypeW(pNavData->pszName) // Window Type must be global! ) { return NULL ; } // Make sure that we have a compiled filename. CStr cszFile(pNavData->pszFile) ; // Convert to ANSI. if (!IsCompiledHtmlFile(cszFile, &cszFile)) { return NULL ; } // Parse out all of the unnecessary bits... GetCompiledName(cszFile, &cszFile); if (cszFile.IsEmpty()) { return NULL ; } // Get the CHmData structure for this file. CHmData* phmdata = FindCurFileData(cszFile) ; if (!phmdata) { return NULL; } // Find the window type. CStr cszName(pNavData->pszName) ; // Convert to ANSI. CHHWinType* phh = FindWindowType(cszName, hwndCaller, NULL) ; // WindowType must be global! if (!phh) { // Couldn't find the window type. It must be defined. return NULL ; } // Go do it! phh->ReloadNavData(phmdata) ; return phh->GetHwnd() ; } ////////////////////////////////////////////////////////////////////////// // // CSession - This is a placeholder class. This will eventually do something. // // This should be moved to its own C++ file... // class CSession { public: CSession(); ~CSession(); private: DWORD* m_dwData ; }; // Constructor CSession::CSession() : m_dwData(0) { //--- Initialize OLE. Competes with process detach/attach. if ( !g_fCoInitialized ) { if (S_FALSE == OleInitialize(NULL)) { // shanemc/dalero // If S_FALSE is returned OLE was already init'd. We don't want to uninit later // because it can hose apps that have called OleInit (like IE). OleUninitialize(); } else { g_fCoInitialized = TRUE; // so that we call CoUninitialize() when dll is unloaded } } } // Destructor CSession::~CSession() { //--- Unitialize OLE. ASSERT(g_fCoInitialized); // Should never be FALSE here. if (g_fCoInitialized) { OleUninitialize(); g_fCoInitialized = FALSE; } } ////////////////////////////////////////////////////////////////////////// // // Initializes everything we need initialized. // bool InitializeSession(UNALIGNED DWORD_PTR* pCookie) { bool bReturn = false ; if (pCookie) { //--- Create a session object. CSession* p = new CSession() ; // The session object is used as a cookie. *pCookie = (DWORD_PTR)p; // Initializing a session, implies standalone. g_fStandAlone = TRUE ; // A-ok bReturn = true ; } return bReturn; } ////////////////////////////////////////////////////////////////////////// // // Uninitalizes everything we need initialized. // bool UninitializeSession(DWORD_PTR Cookie) { bool bReturn = false ; ASSERT(g_fStandAlone) ; // Must be standalone. //--- Need a valid cookie to uninitialize. if (Cookie) { // Convert to a session pointer. CSession* p = reinterpret_cast(Cookie) ; // Do something useful. bReturn = true ; // Delete the session. delete p ; } return bReturn ; } ////////////////////////////////////////////////////////////////////////// // // Private function which returns the interface pointer to the embedded // WebBrowser control given a particular window handle. // bool GetBrowserInterface(HWND hWndBrowserParent, IDispatch** ppWebBrowser) { bool bReturn = false ; if (IsWindow(hWndBrowserParent) && !IsBadReadPtr(ppWebBrowser, sizeof(IDispatch**))) { CHHWinType* phh = FindHHWindowIndex(hWndBrowserParent); if (phh && phh->m_pCIExpContainer && phh->m_pCIExpContainer->m_pWebBrowserApp && phh->m_pCIExpContainer->m_pWebBrowserApp->m_lpDispatch) { *ppWebBrowser = phh->m_pCIExpContainer->m_pWebBrowserApp->m_lpDispatch; bReturn = true ; } } return bReturn ; }