windows-nt/Source/XPSP1/NT/enduser/stuff/hhctrl/hh.cpp
2020-09-26 16:20:57 +08:00

556 lines
18 KiB
C++

// 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 <shellapi.h>
#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();
}
}
}
}
}