// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved. // This code is called by hh.exe -- we put it here to gain access to // the ITSS IStorage code that hhctrl already supports. #include "header.h" #include "hha_strtable.h" #include #include "system.h" #include "htmlhelp.h" #include "fsclient.h" #include "strtable.h" #include "hhshell.h" #include "contain.h" #include "secwin.h" #include "resource.h" //#define __TEST_HH_SET_GLOBAL_PROPRERTY_API //#define __TEST_GPROPID_UI_LANGUAGE__ #include "hhpriv.h" void SetRegKey(LPCTSTR pszKey, LPCTSTR pszValue); void DeCompile(PCSTR pszFolder, PCSTR pszCompiledFile); int doInternalWinMain(HINSTANCE hinstApp, PCSTR lpszCmdLine) ; // Defined in htmlhelp.cpp bool InitializeSession(UNALIGNED DWORD_PTR* pCookie) ; bool UninitializeSession(DWORD_PTR Cookie) ; extern "C" { DWORD WINAPI HhWindowThread(LPVOID pParam); } void RegisterHH(PCSTR pszHHPath); // in ipserver.cpp static const char txtCmdTitle[] = "title"; static const char txtCmd800[] = "800"; // maximum 800 x 600 window static const char txtCmdBrowser[] = "browser"; // display in browser static const char txtCmdRegister[] = "register"; static const char txtCmdUnRegister[] = "unregister"; static const char txtCmdDecompile[] = "decompile"; static const char txtApiWindow[] = "api"; static const char txtMapID[] = "mapid"; static const char txtGlobalSubset[] = "subset" ; extern BOOL g_fStandAlone; // no need for threading in standalone version /* Command line switches -register registers hh, hhctrl, itss, and itircl -unregister unregisters hh, hhctrl, itss, and itircl -decompile folder chm decompiles the CHM file into specified folder -800 Creates an 800 x 600 window, without covering the tray. Ignored if there is a default window type -title text Specifies the title to use for the 800 x 600 window -mapid id Equivalent to callint HH_HELP_CONTEXT with map id */ extern "C" int doWinMain(HINSTANCE hinstApp, PCSTR lpszCmdLine) { int iReturn = -1 ; DWORD_PTR dwCookie = NULL ; if (InitializeSession(&dwCookie)) { iReturn = doInternalWinMain(hinstApp, lpszCmdLine) ; UninitializeSession(dwCookie) ; } return iReturn ; } int doInternalWinMain(HINSTANCE hinstApp, PCSTR lpszCmdLine) { int retval = 0; BOOL fDisplayInBrowser = FALSE; BOOL fTriPane = FALSE; BOOL fRegister = FALSE; BOOL fDecompile = FALSE; CStr cszTitle; BOOL f800 = FALSE; int mapID = -1; #if 0 // test the set toolbar margin API Global property HH_GLOBAL_PROPERTY prop ; prop.id = HH_GPROPID_TOOLBAR_MARGIN; VariantInit(&prop.var); prop.var.vt = VT_UI4; prop.var.ulVal = MAKELONG(80, 0); HtmlHelp(NULL, NULL, HH_SET_GLOBAL_PROPERTY, (DWORD)&prop) ; VariantClear(&prop.var); #endif PCSTR pszCommand = FirstNonSpace(lpszCmdLine); if (IsEmptyString(pszCommand)) { doAuthorMsg(IDSHHA_NO_COMMAND_LINE, ""); return -1; } #ifdef __TEST_HH_SET_GLOBAL_PROPRERTY_API HH_GLOBAL_PROPERTY prop ; prop.id = HH_GPROPID_SINGLETHREAD ; VariantInit(&prop.var) ; prop.var.vt = VT_BOOL ; prop.var.boolVal = VARIANT_TRUE ; HtmlHelp(NULL, NULL, HH_SET_GLOBAL_PROPERTY, (DWORD)&prop) ; // Call hhshell.cpp version. VariantClear(&prop.var); #endif while (*pszCommand == '-') { pszCommand = FirstNonSpace(pszCommand + 1); if (IsSamePrefix(pszCommand, txtCmdBrowser, sizeof(txtCmdBrowser) - 1)) { pszCommand += (sizeof(txtCmdBrowser) - 1); fDisplayInBrowser = TRUE; } else if (IsSamePrefix(pszCommand, txtCmd800, sizeof(txtCmd800) - 1)) { pszCommand += (sizeof(txtCmd800) - 1); f800 = TRUE; } else if (IsSamePrefix(pszCommand, txtCmdRegister, sizeof(txtCmdRegister) - 1)) { pszCommand += (sizeof(txtCmdBrowser) - 1); fRegister = TRUE; } else if (IsSamePrefix(pszCommand, txtCmdTitle, sizeof(txtCmdTitle) - 1)) { pszCommand += (sizeof(txtCmdTitle) - 1); pszCommand = cszTitle.GetArg(pszCommand); } else if (IsSamePrefix(pszCommand, txtCmdDecompile, sizeof(txtCmdDecompile) - 1)) { pszCommand += (sizeof(txtCmdDecompile) - 1); pszCommand = cszTitle.GetArg(pszCommand); fDecompile = TRUE; } else if (hinstApp != _Module.GetModuleInstance() && IsSamePrefix(pszCommand, txtApiWindow, sizeof(txtApiWindow) - 1)) { HhWindowThread(NULL); return 0; } else if (IsSamePrefix(pszCommand, txtMapID, sizeof(txtMapID) - 1)) { pszCommand += (sizeof(txtMapID) - 1); pszCommand = FirstNonSpace(pszCommand); mapID = Atoi(pszCommand); } else if (IsSamePrefix(pszCommand, txtGlobalSubset, sizeof(txtGlobalSubset)-1)) { // Change the subset. This is for test purposes ONLY! pszCommand += (sizeof(txtGlobalSubset) - 1); char *pstart = FirstNonSpace(pszCommand); char *pend = strchr(pstart, ' ') ; char save = *pend; *pend = '\0' ; CWStr subset(pstart) ; *pend = save ; pszCommand = pend ; HH_GLOBAL_PROPERTY prop ; prop.id = HH_GPROPID_CURRENT_SUBSET; VariantInit(&prop.var) ; prop.var.vt = VT_BSTR; prop.var.bstrVal = ::SysAllocString(subset); HtmlHelp(NULL, NULL, HH_SET_GLOBAL_PROPERTY, (DWORD_PTR)&prop) ; // Call hhshell.cpp version. VariantClear(&prop.var); } // step past any text while (*pszCommand && !IsSpace(*pszCommand)) pszCommand = CharNext(pszCommand); // step past any whitespace while (*pszCommand && IsSpace(*pszCommand)) pszCommand++; } char szFullPath[MAX_PATH + 10]; if (fRegister) { ::GetModuleFileName(hinstApp, szFullPath, MAX_PATH); RegisterHH(szFullPath); return 0; } if (IsEmptyString(pszCommand)) { AuthorMsg(IDSHHA_NO_COMMAND_LINE, "", NULL, NULL); retval = -1; } if (fDecompile) { // BUGBUG: nag author if we don't have both parameters if (!cszTitle.IsEmpty() && !IsEmptyString(pszCommand)) DeCompile(cszTitle, pszCommand); return 0; } PSTR pszFileName = NULL; BOOL fSystemFile = FALSE; /* * We need to deal with all the ways we can be called: * hh full path * hh file.chm * hh file.chm::/foo.htm * hh mk:@MSItstore:file.chm::/foo.htm * hh its:file.chm::/foo.htm * hh its:c:\foo\file.chm * etc. */ CStr cszFile; if (*pszCommand == CH_QUOTE) { pszCommand = lcStrDup(pszCommand + 1); PSTR pszEndQuote = StrChr(pszCommand, CH_QUOTE); if (pszEndQuote) *pszEndQuote = '\0'; } /* * First see if it is a compiled HTML file, and if so, call again to * get it's location. */ BOOL bCollection = IsCollectionFile(pszCommand); if (bCollection || IsCompiledHtmlFile(pszCommand, NULL)) { if (!bCollection && !IsCompiledHtmlFile(pszCommand, &cszFile)) return -1; if (bCollection) cszFile = pszCommand; CStr cszCompressed; PCSTR pszFilePortion; CStr cszFilePortion; if ( (pszFilePortion = GetCompiledName(cszFile, &cszCompressed)) ) cszFilePortion = pszFilePortion; CHmData* pchm = FindCurFileData(cszCompressed); if (pchm == NULL) { MsgBox(IDS_FILE_ERROR, cszFile, MB_OK); return -1; } if (bCollection && pchm) cszCompressed = pchm->GetCompiledFile(); CStr cszWindow(g_phmData[g_curHmData]->GetDefaultWindow() ? g_phmData[g_curHmData]->GetDefaultWindow() : txtDefWindow); HH_WINTYPE* phhWinType; #if 0 // For testing The INFOTYPE API { HH_ENUM_IT enum_IT; PHH_ENUM_IT penum_IT = &enum_IT; HH_ENUM_CAT enum_cat; PHH_ENUM_CAT penum_cat=&enum_cat; HH_SET_INFOTYPE set_IT; PHH_SET_INFOTYPE pset_IT=&set_IT; HWND ret; enum_IT.cbStruct = sizeof(HH_ENUM_IT); CWStr cszW("c:\\wintools\\docs\\htmlhelp\\htmlhelp.chm"); do{ ret = xHtmlHelpW(NULL, cszW, HH_ENUM_INFO_TYPE, (DWORD)&penum_IT); }while(ret != (HWND)-1 ); set_IT.cbStruct = sizeof(HH_SET_INFOTYPE); set_IT.pszCatName = ""; CWStr cszIT = "Web"; set_IT.pszInfoTypeName = (PCSTR)cszIT.pw;//"Web"; ret = xHtmlHelpW(NULL, cszW, HH_SET_INFO_TYPE, (DWORD)&pset_IT); enum_cat.cbStruct = sizeof(HH_ENUM_CAT); do { ret = xHtmlHelpW(NULL, cszW, HH_ENUM_CATEGORY, (DWORD)&penum_cat); }while ( ret != (HWND)-1 ); enum_IT.pszCatName = "cat 1"; do { ret = xHtmlHelpW(NULL, cszW, HH_ENUM_CATEGORY_IT, (DWORD)&penum_IT); } while (ret != (HWND)-1); ret = xHtmlHelpW(NULL, cszW, HH_SET_EXCLUSIVE_FILTER, NULL); ret = xHtmlHelpW(NULL, cszW, HH_RESET_IT_FILTER, NULL); ret = xHtmlHelpW(NULL, cszW, HH_SET_INCLUSIVE_FILTER, NULL); } // End INFOTYPE API TEST #endif /* * We need to inlcude the name of the .CHM file with the window * type name in order to know which .CHM file to read/create the * window type from. */ if (!(*cszWindow.psz == '>')) cszCompressed += ">"; cszCompressed += cszWindow.psz; if (xHtmlHelpA(NULL, cszCompressed, HH_GET_WIN_TYPE, (DWORD_PTR) &phhWinType) == (HWND) -1) { CreateDefaultWindowType(pchm->GetCompiledFile(), cszWindow); xHtmlHelpA(NULL, cszCompressed, HH_GET_WIN_TYPE, (DWORD_PTR) &phhWinType); } if (hinstApp != _Module.GetModuleInstance()) { phhWinType->fsWinProperties |= HHWIN_PROP_POST_QUIT; phhWinType->fsValidMembers |= HHWIN_PARAM_PROPERTIES; } if (cszFilePortion.psz) { PSTR pszWinPos = StrChr(cszCompressed.psz, '>'); if (pszWinPos) *pszWinPos = '\0'; cszCompressed += txtSepBack; cszCompressed += (*cszFilePortion.psz == '/' ? cszFilePortion.psz + 1: cszFilePortion.psz); if (!(*cszWindow.psz == '>')) cszCompressed += ">"; cszCompressed += cszWindow.psz; } else if (mapID == -1 && !phhWinType->pszFile && g_phmData[g_curHmData]->GetDefaultHtml()) { PSTR pszWinPos = StrChr(cszCompressed.psz, '>'); if (pszWinPos) *pszWinPos = '\0'; if (IsCompiledHtmlFile(g_phmData[g_curHmData]->GetDefaultHtml())) { cszCompressed = g_phmData[g_curHmData]->GetDefaultHtml(); } else { cszCompressed += txtSepBack; cszCompressed += *g_phmData[g_curHmData]->GetDefaultHtml() == '/' ? g_phmData[g_curHmData]->GetDefaultHtml() + 1 : g_phmData[g_curHmData]->GetDefaultHtml(); } if (!(*cszWindow.psz == '>')) cszCompressed += ">"; cszCompressed += cszWindow.psz; } // BUGBUG This is probably not the correct place for this code but it will do until // Ralph can complete the code necessary to get hhctrl onto it's own message loop. // HWND hwnd; if (mapID != -1) hwnd = xHtmlHelpA(NULL, cszCompressed, HH_HELP_CONTEXT, mapID); else hwnd = OnDisplayTopic(NULL, cszCompressed, 0); AWMessagePump(hwnd); return retval; } /* * Try to call the browser with "foo.htm" and it will think you meant * "http:foo.htm", so we need to attempt to convert the file to a full * path. */ else if (!stristr(pszCommand, txtHttpHeader) && !stristr(pszCommand, txtFtpHeader) && *pszCommand != '\\' && (*pszCommand == '.' || pszCommand[1] != ':')) { if (GetFullPathName(pszCommand, sizeof(szFullPath), szFullPath, &pszFileName) != 0) { pszCommand = lcStrDup(szFullPath); } } // REVIEW: If we reach here, we will NOT have a COL or CHM. if (!fDisplayInBrowser) { HH_WINTYPE hhWinType; ZERO_STRUCTURE(hhWinType); hhWinType.cbStruct = sizeof(HH_WINTYPE); hhWinType.pszType = txtGlobalDefWindow; hhWinType.fNotExpanded = TRUE; hhWinType.fsWinProperties = (HHWIN_PROP_POST_QUIT | HHWIN_PROP_TRI_PANE | HHWIN_PROP_CHANGE_TITLE #ifdef DEBUG // REVIEW: this should only be added if FTI is enabled | HHWIN_PROP_TAB_SEARCH #endif ); hhWinType.fsValidMembers = (HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_EXPANSION | HHWIN_PARAM_TB_FLAGS); hhWinType.fsToolBarFlags = (HHWIN_BUTTON_BACK | HHWIN_BUTTON_STOP | HHWIN_BUTTON_REFRESH | HHWIN_BUTTON_PRINT | HHWIN_BUTTON_OPTIONS); if (f800) { hhWinType.rcWindowPos.left = 0; hhWinType.rcWindowPos.right = 800; hhWinType.rcWindowPos.top = 0; hhWinType.rcWindowPos.bottom = 600; hhWinType.pszCaption = (cszTitle.IsEmpty() ? "" : cszTitle.psz); hhWinType.fsWinProperties = HHWIN_PROP_POST_QUIT; hhWinType.fsValidMembers = HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_RECT | HHWIN_PARAM_EXPANSION; hhWinType.fNotExpanded = TRUE; fTriPane = FALSE; } #if 0 // 28 Apr 98 [dalero] dead code if'd out. // REVIEW: fTriPane is ALWAYS false. if (fTriPane) { // BUGBUG: this should be pulled from the window definition CStr cszCommand(pszCommand); PSTR pszSep = strstr(cszCommand, txtDoubleColonSep); ASSERT(pszSep); pszSep[2] = '\0'; cszCommand += "/"; if (g_phmData[g_curHmData]->m_pszDefToc) { CStr csz(cszCommand.psz); csz += g_phmData[g_curHmData]->m_pszDefToc; hhWinType.pszToc = lcStrDup(csz.psz); } if (g_phmData[g_curHmData]->GetDefaultIndex()) { CStr csz(cszCommand.psz); csz += g_phmData[g_curHmData]->GetDefaultIndex(); hhWinType.pszIndex = lcStrDup(csz.psz); if (!g_phmData[g_curHmData]->m_pszDefToc) { hhWinType.curNavType = HHWIN_NAVTYPE_INDEX; hhWinType.fsValidMembers |= HHWIN_PARAM_TABPOS; } } if (g_phmData[g_curHmData]->GetDefaultHtml()) { CStr csz(cszCommand.psz); csz += g_phmData[g_curHmData]->GetDefaultHtml(); hhWinType.pszHome = lcStrDup(csz.psz); } hhWinType.fNotExpanded = FALSE; hhWinType.fsToolBarFlags |= HHWIN_BUTTON_EXPAND; hhWinType.fsValidMembers |= (HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_RECT); hhWinType.fsWinProperties |= (HHWIN_PROP_TRI_PANE | HHWIN_PROP_AUTO_SYNC | HHWIN_PROP_TAB_SEARCH ); } #endif if (!hhWinType.pszCaption) hhWinType.pszCaption = lcStrDup(GetStringResource(IDS_DEF_WINDOW_CAPTION)); // This is a HTM file or some other type of file...so we use a global wintype. The wintype will not change. xHtmlHelpA(NULL, NULL /*Uses a global wintype*/, HH_SET_WIN_TYPE, (DWORD_PTR) &hhWinType); CStr csz(pszCommand); csz += txtGlobalDefWindow; HWND hwnd = OnDisplayTopic(NULL, csz, 0); AWMessagePump(hwnd); } else { // display in default browser char szValue[MAX_PATH]; LONG cbValue = sizeof(szValue); if (RegQueryValue(HKEY_CLASSES_ROOT, txtOpenCmd, szValue, &cbValue) == ERROR_SUCCESS && szValue[0] == '\042') { #if 0 CStr csz(szValue); csz += " "; csz += pszCommand; WinExec(csz, SW_SHOW); #else PSTR psz = StrChr(szValue + 1, '\042'); if (psz) { *psz = '\0'; CStr csz(FirstNonSpace(psz + 1)); csz += " "; csz += pszCommand; ShellExecute(NULL, NULL, szValue + 1, csz, NULL, SW_SHOW); } #endif } } return retval; } void DeCompile(PCSTR pszFolder, PCSTR pszCompiledFile) { CFSClient fsls; if (fsls.Initialize(pszCompiledFile)) { fsls.WriteStorageContents(pszFolder, NULL); } } CBusy g_Busy; void WINAPI AWMessagePump(HWND hwnd) { if (hwnd) { MSG msg; BOOL fMsg; BOOL fUnicodeMsg; for (;;) { // Check for messages. fMsg = PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE); // Remove W/A according to the type of window the message is directed to. if (fMsg) { if (msg.hwnd && IsWindowUnicode(msg.hwnd)) { fUnicodeMsg = TRUE; fMsg = PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE); } else { fUnicodeMsg = FALSE; fMsg = PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE); } } if (fMsg) { // We got a message, lets go process it if (msg.message == WM_QUIT) { if( g_Busy.IsBusy() ) continue; else break; // exit current loop. } if (!hhPreTranslateMessage(&msg)) { TranslateMessage(&msg); // TranslateMessage doesn't have A/W flavors if (fUnicodeMsg) DispatchMessageW(&msg); else DispatchMessageA(&msg); } } else { if (!PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE)) { WaitMessage(); } } } } }