Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

555 lines
18 KiB

  1. // Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.
  2. // This code is called by hh.exe -- we put it here to gain access to
  3. // the ITSS IStorage code that hhctrl already supports.
  4. #include "header.h"
  5. #include "hha_strtable.h"
  6. #include <shellapi.h>
  7. #include "system.h"
  8. #include "htmlhelp.h"
  9. #include "fsclient.h"
  10. #include "strtable.h"
  11. #include "hhshell.h"
  12. #include "contain.h"
  13. #include "secwin.h"
  14. #include "resource.h"
  15. //#define __TEST_HH_SET_GLOBAL_PROPRERTY_API
  16. //#define __TEST_GPROPID_UI_LANGUAGE__
  17. #include "hhpriv.h"
  18. void SetRegKey(LPCTSTR pszKey, LPCTSTR pszValue);
  19. void DeCompile(PCSTR pszFolder, PCSTR pszCompiledFile);
  20. int doInternalWinMain(HINSTANCE hinstApp, PCSTR lpszCmdLine) ;
  21. // Defined in htmlhelp.cpp
  22. bool InitializeSession(UNALIGNED DWORD_PTR* pCookie) ;
  23. bool UninitializeSession(DWORD_PTR Cookie) ;
  24. extern "C" {
  25. DWORD WINAPI HhWindowThread(LPVOID pParam);
  26. }
  27. void RegisterHH(PCSTR pszHHPath); // in ipserver.cpp
  28. static const char txtCmdTitle[] = "title";
  29. static const char txtCmd800[] = "800"; // maximum 800 x 600 window
  30. static const char txtCmdBrowser[] = "browser"; // display in browser
  31. static const char txtCmdRegister[] = "register";
  32. static const char txtCmdUnRegister[] = "unregister";
  33. static const char txtCmdDecompile[] = "decompile";
  34. static const char txtApiWindow[] = "api";
  35. static const char txtMapID[] = "mapid";
  36. static const char txtGlobalSubset[] = "subset" ;
  37. extern BOOL g_fStandAlone; // no need for threading in standalone version
  38. /*
  39. Command line switches
  40. -register
  41. registers hh, hhctrl, itss, and itircl
  42. -unregister
  43. unregisters hh, hhctrl, itss, and itircl
  44. -decompile folder chm
  45. decompiles the CHM file into specified folder
  46. -800
  47. Creates an 800 x 600 window, without covering the tray. Ignored
  48. if there is a default window type
  49. -title text
  50. Specifies the title to use for the 800 x 600 window
  51. -mapid id
  52. Equivalent to callint HH_HELP_CONTEXT with map id
  53. */
  54. extern "C"
  55. int doWinMain(HINSTANCE hinstApp, PCSTR lpszCmdLine)
  56. {
  57. int iReturn = -1 ;
  58. DWORD_PTR dwCookie = NULL ;
  59. if (InitializeSession(&dwCookie))
  60. {
  61. iReturn = doInternalWinMain(hinstApp, lpszCmdLine) ;
  62. UninitializeSession(dwCookie) ;
  63. }
  64. return iReturn ;
  65. }
  66. int doInternalWinMain(HINSTANCE hinstApp, PCSTR lpszCmdLine)
  67. {
  68. int retval = 0;
  69. BOOL fDisplayInBrowser = FALSE;
  70. BOOL fTriPane = FALSE;
  71. BOOL fRegister = FALSE;
  72. BOOL fDecompile = FALSE;
  73. CStr cszTitle;
  74. BOOL f800 = FALSE;
  75. int mapID = -1;
  76. #if 0 // test the set toolbar margin API Global property
  77. HH_GLOBAL_PROPERTY prop ;
  78. prop.id = HH_GPROPID_TOOLBAR_MARGIN;
  79. VariantInit(&prop.var);
  80. prop.var.vt = VT_UI4;
  81. prop.var.ulVal = MAKELONG(80, 0);
  82. HtmlHelp(NULL, NULL, HH_SET_GLOBAL_PROPERTY, (DWORD)&prop) ;
  83. VariantClear(&prop.var);
  84. #endif
  85. PCSTR pszCommand = FirstNonSpace(lpszCmdLine);
  86. if (IsEmptyString(pszCommand)) {
  87. doAuthorMsg(IDSHHA_NO_COMMAND_LINE, "");
  88. return -1;
  89. }
  90. #ifdef __TEST_HH_SET_GLOBAL_PROPRERTY_API
  91. HH_GLOBAL_PROPERTY prop ;
  92. prop.id = HH_GPROPID_SINGLETHREAD ;
  93. VariantInit(&prop.var) ;
  94. prop.var.vt = VT_BOOL ;
  95. prop.var.boolVal = VARIANT_TRUE ;
  96. HtmlHelp(NULL, NULL, HH_SET_GLOBAL_PROPERTY, (DWORD)&prop) ; // Call hhshell.cpp version.
  97. VariantClear(&prop.var);
  98. #endif
  99. while (*pszCommand == '-') {
  100. pszCommand = FirstNonSpace(pszCommand + 1);
  101. if (IsSamePrefix(pszCommand, txtCmdBrowser, sizeof(txtCmdBrowser) - 1)) {
  102. pszCommand += (sizeof(txtCmdBrowser) - 1);
  103. fDisplayInBrowser = TRUE;
  104. }
  105. else if (IsSamePrefix(pszCommand, txtCmd800, sizeof(txtCmd800) - 1)) {
  106. pszCommand += (sizeof(txtCmd800) - 1);
  107. f800 = TRUE;
  108. }
  109. else if (IsSamePrefix(pszCommand, txtCmdRegister, sizeof(txtCmdRegister) - 1)) {
  110. pszCommand += (sizeof(txtCmdBrowser) - 1);
  111. fRegister = TRUE;
  112. }
  113. else if (IsSamePrefix(pszCommand, txtCmdTitle, sizeof(txtCmdTitle) - 1)) {
  114. pszCommand += (sizeof(txtCmdTitle) - 1);
  115. pszCommand = cszTitle.GetArg(pszCommand);
  116. }
  117. else if (IsSamePrefix(pszCommand, txtCmdDecompile, sizeof(txtCmdDecompile) - 1)) {
  118. pszCommand += (sizeof(txtCmdDecompile) - 1);
  119. pszCommand = cszTitle.GetArg(pszCommand);
  120. fDecompile = TRUE;
  121. }
  122. else if (hinstApp != _Module.GetModuleInstance() && IsSamePrefix(pszCommand, txtApiWindow, sizeof(txtApiWindow) - 1)) {
  123. HhWindowThread(NULL);
  124. return 0;
  125. }
  126. else if (IsSamePrefix(pszCommand, txtMapID, sizeof(txtMapID) - 1)) {
  127. pszCommand += (sizeof(txtMapID) - 1);
  128. pszCommand = FirstNonSpace(pszCommand);
  129. mapID = Atoi(pszCommand);
  130. }
  131. else if (IsSamePrefix(pszCommand, txtGlobalSubset, sizeof(txtGlobalSubset)-1))
  132. {
  133. // Change the subset. This is for test purposes ONLY!
  134. pszCommand += (sizeof(txtGlobalSubset) - 1);
  135. char *pstart = FirstNonSpace(pszCommand);
  136. char *pend = strchr(pstart, ' ') ;
  137. char save = *pend;
  138. *pend = '\0' ;
  139. CWStr subset(pstart) ;
  140. *pend = save ;
  141. pszCommand = pend ;
  142. HH_GLOBAL_PROPERTY prop ;
  143. prop.id = HH_GPROPID_CURRENT_SUBSET;
  144. VariantInit(&prop.var) ;
  145. prop.var.vt = VT_BSTR;
  146. prop.var.bstrVal = ::SysAllocString(subset);
  147. HtmlHelp(NULL, NULL, HH_SET_GLOBAL_PROPERTY, (DWORD_PTR)&prop) ; // Call hhshell.cpp version.
  148. VariantClear(&prop.var);
  149. }
  150. // step past any text
  151. while (*pszCommand && !IsSpace(*pszCommand))
  152. pszCommand = CharNext(pszCommand);
  153. // step past any whitespace
  154. while (*pszCommand && IsSpace(*pszCommand))
  155. pszCommand++;
  156. }
  157. char szFullPath[MAX_PATH + 10];
  158. if (fRegister) {
  159. ::GetModuleFileName(hinstApp, szFullPath, MAX_PATH);
  160. RegisterHH(szFullPath);
  161. return 0;
  162. }
  163. if (IsEmptyString(pszCommand)) {
  164. AuthorMsg(IDSHHA_NO_COMMAND_LINE, "", NULL, NULL);
  165. retval = -1;
  166. }
  167. if (fDecompile) {
  168. // BUGBUG: nag author if we don't have both parameters
  169. if (!cszTitle.IsEmpty() && !IsEmptyString(pszCommand))
  170. DeCompile(cszTitle, pszCommand);
  171. return 0;
  172. }
  173. PSTR pszFileName = NULL;
  174. BOOL fSystemFile = FALSE;
  175. /*
  176. * We need to deal with all the ways we can be called:
  177. * hh full path
  178. * hh file.chm
  179. * hh file.chm::/foo.htm
  180. * hh mk:@MSItstore:file.chm::/foo.htm
  181. * hh its:file.chm::/foo.htm
  182. * hh its:c:\foo\file.chm
  183. * etc.
  184. */
  185. CStr cszFile;
  186. if (*pszCommand == CH_QUOTE) {
  187. pszCommand = lcStrDup(pszCommand + 1);
  188. PSTR pszEndQuote = StrChr(pszCommand, CH_QUOTE);
  189. if (pszEndQuote)
  190. *pszEndQuote = '\0';
  191. }
  192. /*
  193. * First see if it is a compiled HTML file, and if so, call again to
  194. * get it's location.
  195. */
  196. BOOL bCollection = IsCollectionFile(pszCommand);
  197. if (bCollection || IsCompiledHtmlFile(pszCommand, NULL))
  198. {
  199. if (!bCollection && !IsCompiledHtmlFile(pszCommand, &cszFile))
  200. return -1;
  201. if (bCollection)
  202. cszFile = pszCommand;
  203. CStr cszCompressed;
  204. PCSTR pszFilePortion;
  205. CStr cszFilePortion;
  206. if ( (pszFilePortion = GetCompiledName(cszFile, &cszCompressed)) )
  207. cszFilePortion = pszFilePortion;
  208. CHmData* pchm = FindCurFileData(cszCompressed);
  209. if (pchm == NULL)
  210. {
  211. MsgBox(IDS_FILE_ERROR, cszFile, MB_OK);
  212. return -1;
  213. }
  214. if (bCollection && pchm)
  215. cszCompressed = pchm->GetCompiledFile();
  216. CStr cszWindow(g_phmData[g_curHmData]->GetDefaultWindow() ?
  217. g_phmData[g_curHmData]->GetDefaultWindow() : txtDefWindow);
  218. HH_WINTYPE* phhWinType;
  219. #if 0
  220. // For testing The INFOTYPE API
  221. {
  222. HH_ENUM_IT enum_IT;
  223. PHH_ENUM_IT penum_IT = &enum_IT;
  224. HH_ENUM_CAT enum_cat;
  225. PHH_ENUM_CAT penum_cat=&enum_cat;
  226. HH_SET_INFOTYPE set_IT;
  227. PHH_SET_INFOTYPE pset_IT=&set_IT;
  228. HWND ret;
  229. enum_IT.cbStruct = sizeof(HH_ENUM_IT);
  230. CWStr cszW("c:\\wintools\\docs\\htmlhelp\\htmlhelp.chm");
  231. do{
  232. ret = xHtmlHelpW(NULL, cszW, HH_ENUM_INFO_TYPE, (DWORD)&penum_IT);
  233. }while(ret != (HWND)-1 );
  234. set_IT.cbStruct = sizeof(HH_SET_INFOTYPE);
  235. set_IT.pszCatName = "";
  236. CWStr cszIT = "Web";
  237. set_IT.pszInfoTypeName = (PCSTR)cszIT.pw;//"Web";
  238. ret = xHtmlHelpW(NULL, cszW, HH_SET_INFO_TYPE, (DWORD)&pset_IT);
  239. enum_cat.cbStruct = sizeof(HH_ENUM_CAT);
  240. do {
  241. ret = xHtmlHelpW(NULL, cszW, HH_ENUM_CATEGORY, (DWORD)&penum_cat);
  242. }while ( ret != (HWND)-1 );
  243. enum_IT.pszCatName = "cat 1";
  244. do {
  245. ret = xHtmlHelpW(NULL, cszW, HH_ENUM_CATEGORY_IT, (DWORD)&penum_IT);
  246. } while (ret != (HWND)-1);
  247. ret = xHtmlHelpW(NULL, cszW, HH_SET_EXCLUSIVE_FILTER, NULL);
  248. ret = xHtmlHelpW(NULL, cszW, HH_RESET_IT_FILTER, NULL);
  249. ret = xHtmlHelpW(NULL, cszW, HH_SET_INCLUSIVE_FILTER, NULL);
  250. }
  251. // End INFOTYPE API TEST
  252. #endif
  253. /*
  254. * We need to inlcude the name of the .CHM file with the window
  255. * type name in order to know which .CHM file to read/create the
  256. * window type from.
  257. */
  258. if (!(*cszWindow.psz == '>'))
  259. cszCompressed += ">";
  260. cszCompressed += cszWindow.psz;
  261. if (xHtmlHelpA(NULL, cszCompressed, HH_GET_WIN_TYPE, (DWORD_PTR) &phhWinType) == (HWND) -1)
  262. {
  263. CreateDefaultWindowType(pchm->GetCompiledFile(), cszWindow);
  264. xHtmlHelpA(NULL, cszCompressed, HH_GET_WIN_TYPE, (DWORD_PTR) &phhWinType);
  265. }
  266. if (hinstApp != _Module.GetModuleInstance()) {
  267. phhWinType->fsWinProperties |= HHWIN_PROP_POST_QUIT;
  268. phhWinType->fsValidMembers |= HHWIN_PARAM_PROPERTIES;
  269. }
  270. if (cszFilePortion.psz) {
  271. PSTR pszWinPos = StrChr(cszCompressed.psz, '>');
  272. if (pszWinPos)
  273. *pszWinPos = '\0';
  274. cszCompressed += txtSepBack;
  275. cszCompressed += (*cszFilePortion.psz == '/' ?
  276. cszFilePortion.psz + 1: cszFilePortion.psz);
  277. if (!(*cszWindow.psz == '>'))
  278. cszCompressed += ">";
  279. cszCompressed += cszWindow.psz;
  280. }
  281. else if (mapID == -1 && !phhWinType->pszFile && g_phmData[g_curHmData]->GetDefaultHtml()) {
  282. PSTR pszWinPos = StrChr(cszCompressed.psz, '>');
  283. if (pszWinPos)
  284. *pszWinPos = '\0';
  285. if (IsCompiledHtmlFile(g_phmData[g_curHmData]->GetDefaultHtml())) {
  286. cszCompressed = g_phmData[g_curHmData]->GetDefaultHtml();
  287. }
  288. else {
  289. cszCompressed += txtSepBack;
  290. cszCompressed += *g_phmData[g_curHmData]->GetDefaultHtml() == '/' ?
  291. g_phmData[g_curHmData]->GetDefaultHtml() + 1 :
  292. g_phmData[g_curHmData]->GetDefaultHtml();
  293. }
  294. if (!(*cszWindow.psz == '>'))
  295. cszCompressed += ">";
  296. cszCompressed += cszWindow.psz;
  297. }
  298. // BUGBUG This is probably not the correct place for this code but it will do until
  299. // Ralph can complete the code necessary to get hhctrl onto it's own message loop.
  300. //
  301. HWND hwnd;
  302. if (mapID != -1)
  303. hwnd = xHtmlHelpA(NULL, cszCompressed, HH_HELP_CONTEXT, mapID);
  304. else
  305. hwnd = OnDisplayTopic(NULL, cszCompressed, 0);
  306. AWMessagePump(hwnd);
  307. return retval;
  308. }
  309. /*
  310. * Try to call the browser with "foo.htm" and it will think you meant
  311. * "http:foo.htm", so we need to attempt to convert the file to a full
  312. * path.
  313. */
  314. else if (!stristr(pszCommand, txtHttpHeader) && !stristr(pszCommand, txtFtpHeader) &&
  315. *pszCommand != '\\' && (*pszCommand == '.' || pszCommand[1] != ':'))
  316. {
  317. if (GetFullPathName(pszCommand, sizeof(szFullPath), szFullPath, &pszFileName) != 0)
  318. {
  319. pszCommand = lcStrDup(szFullPath);
  320. }
  321. }
  322. // REVIEW: If we reach here, we will NOT have a COL or CHM.
  323. if (!fDisplayInBrowser)
  324. {
  325. HH_WINTYPE hhWinType;
  326. ZERO_STRUCTURE(hhWinType);
  327. hhWinType.cbStruct = sizeof(HH_WINTYPE);
  328. hhWinType.pszType = txtGlobalDefWindow;
  329. hhWinType.fNotExpanded = TRUE;
  330. hhWinType.fsWinProperties =
  331. (HHWIN_PROP_POST_QUIT | HHWIN_PROP_TRI_PANE |
  332. HHWIN_PROP_CHANGE_TITLE
  333. #ifdef DEBUG
  334. // REVIEW: this should only be added if FTI is enabled
  335. | HHWIN_PROP_TAB_SEARCH
  336. #endif
  337. );
  338. hhWinType.fsValidMembers =
  339. (HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_EXPANSION |
  340. HHWIN_PARAM_TB_FLAGS);
  341. hhWinType.fsToolBarFlags =
  342. (HHWIN_BUTTON_BACK | HHWIN_BUTTON_STOP | HHWIN_BUTTON_REFRESH |
  343. HHWIN_BUTTON_PRINT | HHWIN_BUTTON_OPTIONS);
  344. if (f800) {
  345. hhWinType.rcWindowPos.left = 0;
  346. hhWinType.rcWindowPos.right = 800;
  347. hhWinType.rcWindowPos.top = 0;
  348. hhWinType.rcWindowPos.bottom = 600;
  349. hhWinType.pszCaption = (cszTitle.IsEmpty() ? "" : cszTitle.psz);
  350. hhWinType.fsWinProperties = HHWIN_PROP_POST_QUIT;
  351. hhWinType.fsValidMembers =
  352. HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_RECT |
  353. HHWIN_PARAM_EXPANSION;
  354. hhWinType.fNotExpanded = TRUE;
  355. fTriPane = FALSE;
  356. }
  357. #if 0 // 28 Apr 98 [dalero] dead code if'd out.
  358. // REVIEW: fTriPane is ALWAYS false.
  359. if (fTriPane) {
  360. // BUGBUG: this should be pulled from the window definition
  361. CStr cszCommand(pszCommand);
  362. PSTR pszSep = strstr(cszCommand, txtDoubleColonSep);
  363. ASSERT(pszSep);
  364. pszSep[2] = '\0';
  365. cszCommand += "/";
  366. if (g_phmData[g_curHmData]->m_pszDefToc) {
  367. CStr csz(cszCommand.psz);
  368. csz += g_phmData[g_curHmData]->m_pszDefToc;
  369. hhWinType.pszToc = lcStrDup(csz.psz);
  370. }
  371. if (g_phmData[g_curHmData]->GetDefaultIndex()) {
  372. CStr csz(cszCommand.psz);
  373. csz += g_phmData[g_curHmData]->GetDefaultIndex();
  374. hhWinType.pszIndex = lcStrDup(csz.psz);
  375. if (!g_phmData[g_curHmData]->m_pszDefToc) {
  376. hhWinType.curNavType = HHWIN_NAVTYPE_INDEX;
  377. hhWinType.fsValidMembers |= HHWIN_PARAM_TABPOS;
  378. }
  379. }
  380. if (g_phmData[g_curHmData]->GetDefaultHtml()) {
  381. CStr csz(cszCommand.psz);
  382. csz += g_phmData[g_curHmData]->GetDefaultHtml();
  383. hhWinType.pszHome = lcStrDup(csz.psz);
  384. }
  385. hhWinType.fNotExpanded = FALSE;
  386. hhWinType.fsToolBarFlags |= HHWIN_BUTTON_EXPAND;
  387. hhWinType.fsValidMembers |= (HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_RECT);
  388. hhWinType.fsWinProperties |= (HHWIN_PROP_TRI_PANE | HHWIN_PROP_AUTO_SYNC
  389. | HHWIN_PROP_TAB_SEARCH
  390. );
  391. }
  392. #endif
  393. if (!hhWinType.pszCaption)
  394. hhWinType.pszCaption = lcStrDup(GetStringResource(IDS_DEF_WINDOW_CAPTION));
  395. // This is a HTM file or some other type of file...so we use a global wintype. The wintype will not change.
  396. xHtmlHelpA(NULL, NULL /*Uses a global wintype*/, HH_SET_WIN_TYPE, (DWORD_PTR) &hhWinType);
  397. CStr csz(pszCommand);
  398. csz += txtGlobalDefWindow;
  399. HWND hwnd = OnDisplayTopic(NULL, csz, 0);
  400. AWMessagePump(hwnd);
  401. }
  402. else { // display in default browser
  403. char szValue[MAX_PATH];
  404. LONG cbValue = sizeof(szValue);
  405. if (RegQueryValue(HKEY_CLASSES_ROOT, txtOpenCmd, szValue,
  406. &cbValue) == ERROR_SUCCESS && szValue[0] == '\042') {
  407. #if 0
  408. CStr csz(szValue);
  409. csz += " ";
  410. csz += pszCommand;
  411. WinExec(csz, SW_SHOW);
  412. #else
  413. PSTR psz = StrChr(szValue + 1, '\042');
  414. if (psz) {
  415. *psz = '\0';
  416. CStr csz(FirstNonSpace(psz + 1));
  417. csz += " ";
  418. csz += pszCommand;
  419. ShellExecute(NULL, NULL, szValue + 1, csz, NULL, SW_SHOW);
  420. }
  421. #endif
  422. }
  423. }
  424. return retval;
  425. }
  426. void DeCompile(PCSTR pszFolder, PCSTR pszCompiledFile)
  427. {
  428. CFSClient fsls;
  429. if (fsls.Initialize(pszCompiledFile)) {
  430. fsls.WriteStorageContents(pszFolder, NULL);
  431. }
  432. }
  433. CBusy g_Busy;
  434. void WINAPI AWMessagePump(HWND hwnd)
  435. {
  436. if (hwnd)
  437. {
  438. MSG msg;
  439. BOOL fMsg;
  440. BOOL fUnicodeMsg;
  441. for (;;)
  442. {
  443. // Check for messages.
  444. fMsg = PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE);
  445. // Remove W/A according to the type of window the message is directed to.
  446. if (fMsg)
  447. {
  448. if (msg.hwnd && IsWindowUnicode(msg.hwnd))
  449. {
  450. fUnicodeMsg = TRUE;
  451. fMsg = PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE);
  452. }
  453. else
  454. {
  455. fUnicodeMsg = FALSE;
  456. fMsg = PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE);
  457. }
  458. }
  459. if (fMsg)
  460. {
  461. // We got a message, lets go process it
  462. if (msg.message == WM_QUIT) {
  463. if( g_Busy.IsBusy() )
  464. continue;
  465. else
  466. break; // exit current loop.
  467. }
  468. if (!hhPreTranslateMessage(&msg))
  469. {
  470. TranslateMessage(&msg); // TranslateMessage doesn't have A/W flavors
  471. if (fUnicodeMsg)
  472. DispatchMessageW(&msg);
  473. else
  474. DispatchMessageA(&msg);
  475. }
  476. }
  477. else
  478. {
  479. if (!PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE))
  480. {
  481. WaitMessage();
  482. }
  483. }
  484. }
  485. }
  486. }