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.

1516 lines
43 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. apphelp.c
  5. Abstract:
  6. This module implements high-level functions to access apphelp information
  7. Author:
  8. dmunsil created sometime in 1999
  9. Revision History:
  10. --*/
  11. #include "sdbp.h"
  12. #define SIZE_WSTRING(pwsz) \
  13. (pwsz == NULL ? 0 : (wcslen((LPCWSTR)(pwsz)) * sizeof(WCHAR) + sizeof(UNICODE_NULL)))
  14. WCHAR g_szHackURL[MAX_PATH];
  15. BOOL
  16. SdbReadApphelpDetailsData(
  17. IN PDB pdb, // apphelp.sdb handle
  18. OUT PAPPHELP_DATA pData // apphelp data, which is filled with various bits of information
  19. )
  20. /*++
  21. Return: TRUE if the string was read, FALSE if not.
  22. Desc: This function retrieves APPHELP details from apphelp.sdb. The database
  23. should have a valid index on HTMLHELPID. In addition, we assume that
  24. the compiler generated unique entries for the htmlhelpids. Tthat means,
  25. no two items have identical indexes. The logic to do so has been
  26. specifically built into shimdbc. If this ever changes, this function will
  27. have to be changed as well.
  28. --*/
  29. {
  30. BOOL bSuccess = FALSE;
  31. TAGID tiApphelp;
  32. TAGID tiAppTitle;
  33. TAGID tiContact;
  34. TAGID tiDetails;
  35. TAGID tiLink;
  36. TAGID tiURL;
  37. TAGID tiLinkText;
  38. FIND_INFO FindInfo;
  39. DWORD crtLangId;
  40. if (!SdbIsIndexAvailable(pdb, TAG_APPHELP, TAG_HTMLHELPID)) {
  41. DBGPRINT((sdlError,
  42. "SdbReadApphelpDetailsData",
  43. "HTMLHELPID index in details database is not available.\n"));
  44. return FALSE;
  45. }
  46. tiApphelp = SdbFindFirstDWORDIndexedTag(pdb,
  47. TAG_APPHELP,
  48. TAG_HTMLHELPID,
  49. pData->dwHTMLHelpID,
  50. &FindInfo);
  51. if (!tiApphelp) {
  52. DBGPRINT((sdlError,
  53. "SdbReadApphelpDetailsData",
  54. "Failed to find HTMLHELPID 0x%x in the details database.\n",
  55. pData->dwHTMLHelpID));
  56. return FALSE;
  57. }
  58. crtLangId = (DWORD)GetUserDefaultUILanguage();
  59. //
  60. // Loop through the items until we get to a match with the current langid
  61. //
  62. while (1) {
  63. TAGID tiHtmlHelpId;
  64. TAGID tiLangId;
  65. DWORD dwHtmlHelpId;
  66. DWORD langId;
  67. tiHtmlHelpId = SdbFindFirstTag(pdb, tiApphelp, TAG_HTMLHELPID);
  68. if (tiHtmlHelpId == TAGID_NULL) {
  69. DBGPRINT((sdlError,
  70. "SdbReadApphelpDetailsData",
  71. "Failed to get HTMLHELPID for entry 0x%x.\n",
  72. tiApphelp));
  73. return FALSE;
  74. }
  75. dwHtmlHelpId = SdbReadDWORDTag(pdb, tiHtmlHelpId, 0);
  76. if (dwHtmlHelpId != pData->dwHTMLHelpID) {
  77. DBGPRINT((sdlError,
  78. "SdbReadApphelpDetailsData",
  79. "No entry with the same HTMLHELPID for 0x%x.\n",
  80. tiApphelp));
  81. return FALSE;
  82. }
  83. tiLangId = SdbFindFirstTag(pdb, tiApphelp, TAG_LANGID);
  84. if (tiLangId == TAGID_NULL) {
  85. break;
  86. }
  87. langId = SdbReadDWORDTag(pdb, tiLangId, 0);
  88. if (langId == crtLangId || langId == 0) {
  89. break;
  90. }
  91. tiApphelp = SdbFindNextDWORDIndexedTag(pdb, &FindInfo);
  92. if (tiApphelp == TAGID_NULL) {
  93. DBGPRINT((sdlError,
  94. "SdbReadApphelpDetailsData",
  95. "No entry for HTMLHELPID 0x%x.\n",
  96. pData->dwHTMLHelpID));
  97. return FALSE;
  98. }
  99. }
  100. //
  101. // Now find the link. We support multiple links but use only one for now.
  102. //
  103. tiLink = SdbFindFirstTag(pdb, tiApphelp, TAG_LINK);
  104. if (tiLink) {
  105. tiURL = SdbFindFirstTag(pdb, tiLink, TAG_LINK_URL);
  106. if (tiURL) {
  107. GUID guidDB = { 0 };
  108. pData->szURL = SdbGetStringTagPtr(pdb, tiURL);
  109. //
  110. // HORIBLE HACK to fix the URL for XP Gold
  111. // Do this horrible hack only when pdb is not a custom
  112. // database; do so by comparing db guid to the standard
  113. // "details" guid
  114. //
  115. if (SdbGetDatabaseID(pdb, &guidDB) &&
  116. (!memcmp(&guidDB, &GUID_APPHELP_SDB, sizeof(GUID)) ||
  117. !memcmp(&guidDB, &GUID_APPHELP_SP_SDB, sizeof(GUID)))) {
  118. if (pData->szURL != NULL &&
  119. *pData->szURL != TEXT('\0') &&
  120. wcsstr(pData->szURL, L"clcid") == NULL) {
  121. _stprintf(g_szHackURL, L"%s&clcid=0x%x", pData->szURL, crtLangId);
  122. pData->szURL = g_szHackURL;
  123. }
  124. }
  125. }
  126. tiLinkText = SdbFindFirstTag(pdb, tiLink, TAG_LINK_TEXT);
  127. if (tiLinkText) {
  128. pData->szLink = SdbGetStringTagPtr(pdb, tiLinkText);
  129. }
  130. }
  131. tiDetails = SdbFindFirstTag(pdb, tiApphelp, TAG_APPHELP_DETAILS);
  132. if (tiDetails) {
  133. pData->szDetails = SdbGetStringTagPtr(pdb, tiDetails);
  134. }
  135. tiContact = SdbFindFirstTag(pdb, tiApphelp, TAG_APPHELP_CONTACT);
  136. if (tiContact) {
  137. pData->szContact = SdbGetStringTagPtr(pdb, tiContact);
  138. }
  139. tiAppTitle = SdbFindFirstTag(pdb, tiApphelp, TAG_APPHELP_TITLE);
  140. if (tiAppTitle) {
  141. pData->szAppTitle = SdbGetStringTagPtr(pdb, tiAppTitle);
  142. }
  143. bSuccess = TRUE;
  144. return bSuccess;
  145. }
  146. BOOL
  147. SdbReadApphelpData(
  148. IN HSDB hSDB, // handle to the database channel
  149. IN TAGREF trExe, // TAGREF of the EXE with data to read
  150. OUT PAPPHELP_DATA pData // data that we read
  151. )
  152. /*++
  153. Return: TRUE if the string was read, FALSE if not.
  154. Desc: Read the data associated with the apphelp entry
  155. into APPHELP_DATA structure. If there are no apphelp data
  156. for this exe, then the function returns FALSE.
  157. One or more members of the APPHELP_DATA structure may
  158. be 0.
  159. --*/
  160. {
  161. TAGID tiAppHelp,
  162. tiAppName,
  163. tiProblemSeverity,
  164. tiFlags,
  165. tiHtmlHelpID;
  166. TAGID tiExe,
  167. tiSP;
  168. PDB pdb;
  169. if (pData) {
  170. RtlZeroMemory(pData, sizeof(APPHELP_DATA));
  171. }
  172. if (!SdbTagRefToTagID(hSDB, trExe, &pdb, &tiExe)) {
  173. DBGPRINT((sdlError,
  174. "SdbReadApphelpData",
  175. "Failed to get the TAGID for TAGREF 0x%x.\n",
  176. trExe));
  177. return FALSE;
  178. }
  179. tiAppHelp = SdbFindFirstTag(pdb, tiExe, TAG_APPHELP);
  180. if (tiAppHelp == TAGID_NULL) {
  181. //
  182. // This is not an apphelp entry
  183. //
  184. DBGPRINT((sdlInfo,
  185. "SdbReadApphelpData",
  186. "This is not an apphelp entry tiExe 0x%x.\n",
  187. tiExe));
  188. return FALSE;
  189. }
  190. //
  191. // if we are just checking for data being present - return now
  192. //
  193. if (pData == NULL) {
  194. return TRUE;
  195. }
  196. pData->trExe = trExe;
  197. tiSP = SdbFindFirstTag(pdb, tiAppHelp, TAG_USE_SERVICE_PACK_FILES);
  198. pData->bSPEntry = (tiSP != TAGID_NULL);
  199. //
  200. // Read supplemental flags.
  201. //
  202. tiFlags = SdbFindFirstTag(pdb, tiAppHelp, TAG_FLAGS);
  203. if (tiFlags != TAGID_NULL) {
  204. pData->dwFlags = SdbReadDWORDTag(pdb, tiFlags, 0);
  205. }
  206. //
  207. // Read problem severity for this app.
  208. //
  209. tiProblemSeverity = SdbFindFirstTag(pdb, tiAppHelp, TAG_PROBLEMSEVERITY);
  210. if (tiProblemSeverity != TAGID_NULL) {
  211. pData->dwSeverity = SdbReadDWORDTag(pdb, tiProblemSeverity, 0);
  212. }
  213. if (pData->dwSeverity == 0) {
  214. DBGPRINT((sdlError,
  215. "SdbReadApphelpData",
  216. "Problem severity for tiExe 0x%x missing.\n",
  217. tiExe));
  218. return FALSE;
  219. }
  220. //
  221. // We should have html help id here.
  222. //
  223. tiHtmlHelpID = SdbFindFirstTag(pdb, tiAppHelp, TAG_HTMLHELPID);
  224. if (tiHtmlHelpID != TAGID_NULL) {
  225. pData->dwHTMLHelpID = SdbReadDWORDTag(pdb, tiHtmlHelpID, 0);
  226. }
  227. //
  228. // While we are at it, include app's name for now. We might need it.
  229. //
  230. tiAppName = SdbFindFirstTag(pdb, tiExe, TAG_APP_NAME);
  231. if (tiAppName != TAGID_NULL) {
  232. pData->szAppName = SdbGetStringTagPtr(pdb, tiAppName);
  233. }
  234. return TRUE;
  235. }
  236. BOOL
  237. SDBAPI
  238. SdbEscapeApphelpURL(
  239. LPWSTR szResult, // escaped string (output)
  240. LPDWORD pdwCount, // count of tchars in the buffer pointed to by szResult
  241. LPCWSTR szToEscape // string to escape
  242. )
  243. {
  244. static const BYTE s_grfbitEscape[] =
  245. {
  246. 0xFF, 0xFF, // 00 - 0F
  247. 0xFF, 0xFF, // 10 - 1F
  248. 0xFF, 0x13, // 20 - 2F
  249. 0x00, 0xFC, // 30 - 3F
  250. 0x00, 0x00, // 40 - 4F
  251. 0x00, 0x78, // 50 - 5F
  252. 0x01, 0x00, // 60 - 6F
  253. 0x00, 0xF8, // 70 - 7F
  254. 0xFF, 0xFF, // 80 - 8F
  255. 0xFF, 0xFF, // 90 - 9F
  256. 0xFF, 0xFF, // A0 - AF
  257. 0xFF, 0xFF, // B0 - BF
  258. 0xFF, 0xFF, // C0 - CF
  259. 0xFF, 0xFF, // D0 - DF
  260. 0xFF, 0xFF, // E0 - EF
  261. 0xFF, 0xFF, // F0 - FF
  262. };
  263. static const WCHAR s_rgchHex[] = L"0123456789ABCDEF";
  264. WCHAR ch;
  265. BOOL bSuccess = FALSE;
  266. DWORD nch = 0;
  267. LPCWSTR lpszURL = szToEscape;
  268. DWORD dwCount = *pdwCount;
  269. // part one -- measure length
  270. while ((ch = *lpszURL++) != L'\0') {
  271. if ((ch & 0xFF00) != 0) { // a unicode char ?
  272. nch += 6;
  273. } else if(s_grfbitEscape[ch >> 3] & (1 << (ch & 7))) {
  274. nch += 3;
  275. } else {
  276. nch += 1;
  277. }
  278. }
  279. nch++; // one more for the final \0
  280. if (dwCount < nch) {
  281. DBGPRINT((sdlError,
  282. "SdbEscapeApphelpURL",
  283. "Not enough storage to escape URL \"%S\" need %ld got %ld\n",
  284. szToEscape,
  285. nch,
  286. dwCount));
  287. *pdwCount = nch;
  288. return FALSE;
  289. }
  290. lpszURL = szToEscape;
  291. while ((ch = *lpszURL++) != L'\0') {
  292. if (ch == L' ') {
  293. *szResult++ = L'+';
  294. } else if ((ch & 0xFF00) != 0) { // a unicode char ?
  295. *szResult++ = L'%';
  296. *szResult++ = L'u';
  297. *szResult++ = s_rgchHex[(ch >> 12) & 0x0F];
  298. *szResult++ = s_rgchHex[(ch >> 8) & 0x0F];
  299. *szResult++ = s_rgchHex[(ch >> 4) & 0x0F];
  300. *szResult++ = s_rgchHex[ ch & 0x0F];
  301. } else if(s_grfbitEscape[ch >> 3] & (1 << (ch & 7))) {
  302. *szResult++ = L'%';
  303. *szResult++ = s_rgchHex[(ch >> 4) & 0x0F];
  304. *szResult++ = s_rgchHex[ ch & 0x0F];
  305. } else {
  306. *szResult++ = ch;
  307. }
  308. }
  309. *szResult = L'\0';
  310. *pdwCount = nch - 1; // do not include the term \0 into a char count
  311. return TRUE;
  312. }
  313. /////////////////////////////////////////////////////////////////////////////////////////////
  314. //
  315. //
  316. // Retrieving apphelp information
  317. //
  318. //
  319. //
  320. PDB
  321. SDBAPI
  322. SdbOpenApphelpDetailsDatabase(
  323. LPCWSTR pwszDetailsDatabasePath
  324. )
  325. {
  326. PDB pdbDetails = NULL;
  327. DWORD dwLength;
  328. WCHAR wszAppHelpSdb[MAX_PATH];
  329. if (pwszDetailsDatabasePath == NULL) {
  330. //
  331. // By default the details database is in %windir%\AppPatch\apphelp.sdb
  332. //
  333. dwLength = SdbpGetStandardDatabasePath(SDB_DATABASE_MAIN_DETAILS,
  334. 0, // retrieve NT_PATH
  335. wszAppHelpSdb,
  336. CHARCOUNT(wszAppHelpSdb));
  337. if (dwLength != 0 && dwLength < CHARCOUNT(wszAppHelpSdb)) {
  338. pdbDetails = SdbOpenDatabase(wszAppHelpSdb, NT_PATH);
  339. }
  340. } else {
  341. pdbDetails = SdbOpenDatabase(pwszDetailsDatabasePath, DOS_PATH);
  342. }
  343. if (pdbDetails == NULL) {
  344. DBGPRINT((sdlError, "SdbOpenApphelpDetailsDatabase", "Failed to open the details database.\n"));
  345. }
  346. return pdbDetails;
  347. }
  348. PDB
  349. SDBAPI
  350. SdbOpenApphelpDetailsDatabaseSP(
  351. void
  352. )
  353. {
  354. PDB pdbDetails = NULL;
  355. DWORD dwLength;
  356. WCHAR wszAppHelpSdb[MAX_PATH];
  357. dwLength = SdbpGetStandardDatabasePath(SDB_DATABASE_MAIN_SP_DETAILS,
  358. 0, // retrieve NT_PATH
  359. wszAppHelpSdb,
  360. CHARCOUNT(wszAppHelpSdb));
  361. if (dwLength != 0 && dwLength < CHARCOUNT(wszAppHelpSdb)) {
  362. pdbDetails = SdbOpenDatabase(wszAppHelpSdb, NT_PATH);
  363. }
  364. if (pdbDetails == NULL) {
  365. DBGPRINT((sdlError,
  366. "SdbOpenApphelpDetailsDatabaseSP",
  367. "Failed to open the SP details database.\n"));
  368. }
  369. return pdbDetails;
  370. }
  371. BOOL
  372. SdbpReadApphelpBasicInfo(
  373. IN PDB pdb,
  374. IN TAGID tiEntry,
  375. OUT TAGID* ptiApphelp,
  376. OUT LPDWORD lpdwHtmlHelpID,
  377. OUT LPDWORD lpdwProblemSeverity,
  378. OUT LPDWORD lpdwFlags
  379. )
  380. {
  381. TAGID tiAppHelp = TAGID_NULL;
  382. TAGID tiHtmlHelpID = TAGID_NULL;
  383. TAGID tiSeverity = TAGID_NULL;
  384. TAGID tiFlags = TAGID_NULL;
  385. DWORD dwHtmlHelpID = 0;
  386. DWORD dwSeverity = 0;
  387. DWORD dwFlags = 0;
  388. BOOL bReturn = FALSE;
  389. if (tiEntry == TAGID_NULL) {
  390. goto out;
  391. }
  392. assert(ptiApphelp != NULL);
  393. tiAppHelp = SdbFindFirstTag(pdb, tiEntry, TAG_APPHELP);
  394. if (tiAppHelp == TAGID_NULL) {
  395. //
  396. // This is not an apphelp entry
  397. //
  398. DBGPRINT((sdlError, "SdbpReadApphelpBasicInfo",
  399. "This is not an apphelp entry tiExe 0x%x.\n", tiEntry));
  400. goto out;
  401. }
  402. if (lpdwHtmlHelpID != NULL) {
  403. tiHtmlHelpID = SdbFindFirstTag(pdb, tiAppHelp, TAG_HTMLHELPID);
  404. if (tiHtmlHelpID != TAGID_NULL) {
  405. dwHtmlHelpID = SdbReadDWORDTag(pdb, tiHtmlHelpID, 0);
  406. }
  407. *lpdwHtmlHelpID = dwHtmlHelpID;
  408. }
  409. if (lpdwProblemSeverity != NULL) {
  410. tiSeverity = SdbFindFirstTag(pdb, tiAppHelp, TAG_PROBLEMSEVERITY);
  411. if (tiSeverity != TAGID_NULL) {
  412. dwSeverity = SdbReadDWORDTag(pdb, tiSeverity, 0);
  413. }
  414. *lpdwProblemSeverity = dwSeverity;
  415. }
  416. //
  417. // Read supplemental flags.
  418. //
  419. if (lpdwFlags != NULL) {
  420. tiFlags = SdbFindFirstTag(pdb, tiAppHelp, TAG_FLAGS);
  421. if (tiFlags != TAGID_NULL) {
  422. dwFlags = SdbReadDWORDTag(pdb, tiFlags, 0);
  423. }
  424. *lpdwFlags = dwFlags;
  425. }
  426. bReturn = TRUE;
  427. out:
  428. // always set the tiApphelp
  429. *ptiApphelp = tiAppHelp;
  430. return bReturn;
  431. }
  432. HAPPHELPINFOCONTEXT
  433. SDBAPI
  434. SdbOpenApphelpInformationByID(
  435. IN HSDB hSDB,
  436. IN TAGREF trEntry,
  437. IN DWORD dwDatabaseType // pass the type of db you are using
  438. )
  439. {
  440. PAPPHELPINFOCONTEXT pApphelpInfoContext = NULL;
  441. DWORD dwSeverity = 0;
  442. DWORD dwHtmlHelpID = 0;
  443. TAGID tiApphelpExe = TAGID_NULL;
  444. PDB pdb = NULL;
  445. TAGID tiExe = TAGID_NULL;
  446. BOOL bSuccess = FALSE;
  447. if (trEntry == TAGREF_NULL) {
  448. return NULL;
  449. }
  450. //
  451. // if we are here, it is apphelp for sure, so we create the context
  452. //
  453. pApphelpInfoContext = (PAPPHELPINFOCONTEXT)SdbAlloc(sizeof(APPHELPINFOCONTEXT));
  454. if (pApphelpInfoContext == NULL) {
  455. DBGPRINT((sdlError, "SdbOpenApphelpInformation",
  456. "Error allocating memory for apphelp info context\n"));
  457. goto out;
  458. }
  459. pApphelpInfoContext->hSDB = hSDB;
  460. pApphelpInfoContext->pdb = pdb;
  461. pApphelpInfoContext->dwContextFlags |= AHC_HSDB_NOCLOSE; // external hsdb, do not touch it
  462. //
  463. // all we care for -- is whether it's "main" database or not
  464. //
  465. pApphelpInfoContext->dwDatabaseType = dwDatabaseType;
  466. // get the guid for this db
  467. if (!SdbTagRefToTagID(hSDB, trEntry, &pdb, &tiExe)) {
  468. DBGPRINT((sdlError, "SdbOpenApphelpInformationByID",
  469. "Error converting tagref to tagref 0x%lx\n", trEntry));
  470. goto out;
  471. }
  472. pApphelpInfoContext->tiExe = tiExe;
  473. if (!SdbGetDatabaseGUID(hSDB, pdb, &pApphelpInfoContext->guidDB)) {
  474. DBGPRINT((sdlError, "SdbOpenApphelpInformationByID",
  475. "Error reading database guid for tagref 0x%lx\n", trEntry));
  476. goto out;
  477. }
  478. if (!SdbpReadApphelpBasicInfo(pdb,
  479. tiExe,
  480. &pApphelpInfoContext->tiApphelpExe,
  481. &pApphelpInfoContext->dwHtmlHelpID,
  482. &pApphelpInfoContext->dwSeverity,
  483. &pApphelpInfoContext->dwFlags)) {
  484. DBGPRINT((sdlError, "SdbOpenApphelpInformationByID",
  485. "Error reading apphelp basic information, apphelp may not be present for 0x%lx\n", trEntry));
  486. goto out;
  487. }
  488. bSuccess = TRUE;
  489. //
  490. out:
  491. if (!bSuccess) {
  492. if (pApphelpInfoContext != NULL) {
  493. SdbFree(pApphelpInfoContext);
  494. pApphelpInfoContext = NULL;
  495. }
  496. }
  497. return pApphelpInfoContext;
  498. }
  499. HAPPHELPINFOCONTEXT
  500. SDBAPI
  501. SdbOpenApphelpInformation(
  502. IN GUID* pguidDB,
  503. IN GUID* pguidID
  504. )
  505. {
  506. WCHAR szDatabasePath[MAX_PATH];
  507. DWORD dwDatabaseType = 0;
  508. DWORD dwLength;
  509. BOOL bInstallPackage = TRUE;
  510. HSDB hSDB = NULL;
  511. PDB pdb = NULL;
  512. TAGID tiMatch = TAGID_NULL;
  513. TAGID tiAppHelp = TAGID_NULL;
  514. TAGID tiHtmlHelpID = TAGID_NULL;
  515. TAGID tiSeverity = TAGID_NULL;
  516. TAGID tiFlags = TAGID_NULL;
  517. DWORD dwHtmlHelpID = 0;
  518. DWORD dwSeverity = 0;
  519. DWORD dwFlags = 0;
  520. FIND_INFO FindInfo;
  521. PAPPHELPINFOCONTEXT pApphelpInfoContext = NULL;
  522. BOOL bSuccess = FALSE;
  523. //
  524. // resolve and open the database
  525. //
  526. hSDB = SdbInitDatabase(HID_NO_DATABASE, NULL);
  527. if (hSDB == NULL) {
  528. DBGPRINT((sdlError, "SdbOpenApphelpInformation",
  529. "Failed to initialize database\n"));
  530. goto out;
  531. }
  532. //
  533. // First, we need to resolve a db
  534. //
  535. dwLength = SdbResolveDatabase(pguidDB,
  536. &dwDatabaseType,
  537. szDatabasePath,
  538. CHARCOUNT(szDatabasePath));
  539. if (dwLength == 0 || dwLength > CHARCOUNT(szDatabasePath)) {
  540. DBGPRINT((sdlError, "SdbOpenApphelpInformation",
  541. "Failed to resolve database path\n"));
  542. goto out;
  543. }
  544. //
  545. // open database
  546. //
  547. if (!SdbOpenLocalDatabase(hSDB, szDatabasePath)) {
  548. DBGPRINT((sdlError, "SdbOpenApphelpInformation",
  549. "Failed to open database \"%s\"\n", szDatabasePath));
  550. goto out;
  551. }
  552. //
  553. // we have database opened, I presume
  554. //
  555. pdb = ((PSDBCONTEXT)hSDB)->pdbLocal;
  556. //
  557. // we search only the LOCAL database in this case
  558. //
  559. tiMatch = SdbFindFirstGUIDIndexedTag(pdb,
  560. TAG_EXE,
  561. TAG_EXE_ID,
  562. pguidID,
  563. &FindInfo);
  564. // if we have a match...
  565. if (tiMatch == TAGID_NULL) {
  566. DBGPRINT((sdlWarning, "SdbOpenApphelpInformation", "guid was not found in the database\n"));
  567. goto out;
  568. }
  569. //
  570. // if we are here, it is apphelp for sure, so we create the context
  571. //
  572. pApphelpInfoContext = (PAPPHELPINFOCONTEXT)SdbAlloc(sizeof(APPHELPINFOCONTEXT));
  573. if (pApphelpInfoContext == NULL) {
  574. DBGPRINT((sdlError, "SdbOpenApphelpInformation",
  575. "Error allocating memory for apphelp info context\n"));
  576. goto out;
  577. }
  578. pApphelpInfoContext->hSDB = hSDB;
  579. pApphelpInfoContext->pdb = pdb;
  580. pApphelpInfoContext->guidID = *pguidID;
  581. pApphelpInfoContext->guidDB = *pguidDB;
  582. pApphelpInfoContext->tiExe = tiMatch;
  583. pApphelpInfoContext->dwDatabaseType = dwDatabaseType;
  584. if (!SdbpReadApphelpBasicInfo(pdb,
  585. tiMatch,
  586. &pApphelpInfoContext->tiApphelpExe,
  587. &pApphelpInfoContext->dwHtmlHelpID,
  588. &pApphelpInfoContext->dwSeverity,
  589. &pApphelpInfoContext->dwFlags)) {
  590. DBGPRINT((sdlError, "SdbOpenApphelpInformation",
  591. "Error reading apphelp basic information, apphelp may not be present for tagid 0x%lx\n", tiMatch));
  592. goto out;
  593. }
  594. bSuccess = TRUE;
  595. //
  596. // we are done now
  597. //
  598. out:
  599. if (!bSuccess) {
  600. if (hSDB != NULL) {
  601. SdbReleaseDatabase(hSDB);
  602. }
  603. if (pApphelpInfoContext != NULL) {
  604. SdbFree(pApphelpInfoContext);
  605. pApphelpInfoContext = NULL;
  606. }
  607. }
  608. return (HAPPHELPINFOCONTEXT)pApphelpInfoContext;
  609. }
  610. BOOL
  611. SDBAPI
  612. SdbCloseApphelpInformation(
  613. HAPPHELPINFOCONTEXT hctx
  614. )
  615. {
  616. PAPPHELPINFOCONTEXT pApphelpInfoContext = (PAPPHELPINFOCONTEXT)hctx;
  617. if (pApphelpInfoContext != NULL) {
  618. if (pApphelpInfoContext->hSDB != NULL &&
  619. !(pApphelpInfoContext->dwContextFlags & AHC_HSDB_NOCLOSE)) {
  620. SdbReleaseDatabase(pApphelpInfoContext->hSDB);
  621. }
  622. if (pApphelpInfoContext->pdbDetails != NULL &&
  623. !(pApphelpInfoContext->dwContextFlags & AHC_DBDETAILS_NOCLOSE)) {
  624. SdbCloseDatabaseRead(pApphelpInfoContext->pdbDetails);
  625. }
  626. if (pApphelpInfoContext->pwszHelpCtrURL != NULL) {
  627. SdbFree(pApphelpInfoContext->pwszHelpCtrURL);
  628. }
  629. RtlFreeUnicodeString(&pApphelpInfoContext->ustrChmFile);
  630. RtlFreeUnicodeString(&pApphelpInfoContext->ustrDetailsDatabase);
  631. SdbFree(pApphelpInfoContext);
  632. }
  633. return TRUE;
  634. }
  635. DWORD
  636. SDBAPI
  637. SdbpReadApphelpString(
  638. PDB pdb,
  639. TAGID tiParent,
  640. TAG tItem,
  641. LPCWSTR* ppwszCache,
  642. LPVOID* ppResult
  643. )
  644. {
  645. DWORD cbResult = 0;
  646. TAGID tiItem;
  647. LPCWSTR pwszItem = NULL;
  648. if (*ppwszCache != NULL) {
  649. pwszItem = *ppwszCache;
  650. } else {
  651. tiItem = SdbFindFirstTag(pdb, tiParent, tItem);
  652. if (tiItem != TAGID_NULL) {
  653. pwszItem = SdbGetStringTagPtr(pdb, tiItem);
  654. if (pwszItem != NULL) {
  655. *ppwszCache = pwszItem;
  656. }
  657. }
  658. }
  659. cbResult = SIZE_WSTRING(pwszItem);
  660. *ppResult = (LPVOID)pwszItem;
  661. return cbResult;
  662. }
  663. BOOL
  664. SDBAPI
  665. SdbpReadApphelpLinkInformation(
  666. PAPPHELPINFOCONTEXT pApphelpInfoContext
  667. )
  668. {
  669. TAGID tiLink;
  670. TAGID tiApphelp = pApphelpInfoContext->tiApphelpDetails;
  671. PDB pdb = pApphelpInfoContext->pdbDetails;
  672. TAGID tiURL;
  673. TAGID tiLinkText;
  674. if (pApphelpInfoContext->tiLink != TAGID_NULL) {
  675. return TRUE;
  676. }
  677. //
  678. // Now find the link. We support multiple links but use only one for now.
  679. //
  680. tiLink = SdbFindFirstTag(pdb, tiApphelp, TAG_LINK);
  681. if (tiLink == TAGID_NULL) {
  682. return FALSE;
  683. }
  684. tiURL = SdbFindFirstTag(pdb, tiLink, TAG_LINK_URL);
  685. if (tiURL) {
  686. pApphelpInfoContext->pwszLinkURL = SdbGetStringTagPtr(pdb, tiURL);
  687. }
  688. tiLinkText = SdbFindFirstTag(pdb, tiLink, TAG_LINK_TEXT);
  689. if (tiLinkText) {
  690. pApphelpInfoContext->pwszLinkText = SdbGetStringTagPtr(pdb, tiLinkText);
  691. }
  692. pApphelpInfoContext->tiLink = tiLink;
  693. return TRUE;
  694. }
  695. BOOL
  696. SDBAPI
  697. SdbpCreateHelpCenterURL(
  698. IN HAPPHELPINFOCONTEXT hctx,
  699. IN BOOL bOfflineContent OPTIONAL, // pass FALSE
  700. IN BOOL bUseHtmlHelp OPTIONAL, // pass FALSE
  701. IN LPCWSTR pwszChmFile OPTIONAL // pass NULL
  702. );
  703. BOOL
  704. SDBAPI
  705. SdbSetApphelpDebugParameters(
  706. IN HAPPHELPINFOCONTEXT hctx,
  707. IN LPCWSTR pszDetailsDatabase OPTIONAL,
  708. IN BOOL bOfflineContent OPTIONAL, // pass FALSE
  709. IN BOOL bUseHtmlHelp OPTIONAL, // pass FALSE
  710. IN LPCWSTR pszChmFile OPTIONAL // pass NULL
  711. )
  712. {
  713. PAPPHELPINFOCONTEXT pApphelpInfoContext = (PAPPHELPINFOCONTEXT)hctx;
  714. if (pApphelpInfoContext == NULL) {
  715. return FALSE;
  716. }
  717. if (bUseHtmlHelp && !bOfflineContent) {
  718. DBGPRINT((sdlError, "SdbSetApphelpDebugParameters",
  719. "Inconsistent parameters: when using html help -- offline content flag should also be set\n"));
  720. bOfflineContent = TRUE;
  721. }
  722. RtlFreeUnicodeString(&pApphelpInfoContext->ustrDetailsDatabase);
  723. RtlFreeUnicodeString(&pApphelpInfoContext->ustrChmFile);
  724. pApphelpInfoContext->bOfflineContent = bOfflineContent;
  725. pApphelpInfoContext->bUseHtmlHelp = bUseHtmlHelp;
  726. if (pszDetailsDatabase != NULL) {
  727. if (!RtlCreateUnicodeString(&pApphelpInfoContext->ustrDetailsDatabase, pszDetailsDatabase)) {
  728. DBGPRINT((sdlError, "SdbSetApphelpDebugParameters",
  729. "Failed to create unicode string from \"%S\"\n", pszDetailsDatabase));
  730. return FALSE;
  731. }
  732. }
  733. if (pszChmFile != NULL) {
  734. if (!RtlCreateUnicodeString(&pApphelpInfoContext->ustrChmFile, pszChmFile)) {
  735. DBGPRINT((sdlError, "SdbSetApphelpDebugParameters",
  736. "Failed to create unicode string from \"%S\"\n", pszChmFile));
  737. return FALSE;
  738. }
  739. }
  740. return TRUE;
  741. }
  742. DWORD
  743. SDBAPI
  744. SdbQueryApphelpInformation(
  745. HAPPHELPINFOCONTEXT hctx,
  746. APPHELPINFORMATIONCLASS InfoClass,
  747. LPVOID pBuffer, // may be NULL
  748. DWORD cbSize // may be 0 if pBuffer is NULL
  749. )
  750. {
  751. PAPPHELPINFOCONTEXT pApphelpInfoContext = (PAPPHELPINFOCONTEXT)hctx;
  752. LPCWSTR *ppwsz;
  753. TAG tag;
  754. TAGID tiParent;
  755. LPVOID pResult = NULL;
  756. DWORD cbResult = 0;
  757. PDB pdb = NULL;
  758. PDB pdbDetails = NULL;
  759. TAGID tiApphelpDetails = TAGID_NULL;
  760. FIND_INFO FindInfo;
  761. switch(InfoClass) {
  762. case ApphelpLinkURL:
  763. case ApphelpLinkText:
  764. case ApphelpTitle:
  765. case ApphelpDetails:
  766. case ApphelpContact:
  767. pdbDetails = pApphelpInfoContext->pdbDetails;
  768. if (pApphelpInfoContext->pdbDetails == NULL) {
  769. //
  770. // see which db we should open
  771. //
  772. if ((pApphelpInfoContext->ustrDetailsDatabase.Buffer != NULL) ||
  773. (pApphelpInfoContext->dwDatabaseType & SDB_DATABASE_MAIN)) {
  774. pdbDetails = SdbOpenApphelpDetailsDatabase(pApphelpInfoContext->ustrDetailsDatabase.Buffer);
  775. } else {
  776. // we have a case when the apphelp details should be in main db
  777. pApphelpInfoContext->dwContextFlags |= AHC_DBDETAILS_NOCLOSE;
  778. pdbDetails = pApphelpInfoContext->pdb;
  779. }
  780. if (pdbDetails == NULL) {
  781. return cbResult; // apphelp db is not available
  782. }
  783. pApphelpInfoContext->pdbDetails = pdbDetails;
  784. }
  785. tiApphelpDetails = pApphelpInfoContext->tiApphelpDetails;
  786. if (tiApphelpDetails == TAGID_NULL) {
  787. if (!SdbIsIndexAvailable(pdbDetails, TAG_APPHELP, TAG_HTMLHELPID)) {
  788. DBGPRINT((sdlError,
  789. "SdbQueryApphelpInformation",
  790. "HTMLHELPID index in details database is not available.\n"));
  791. return cbResult;
  792. }
  793. tiApphelpDetails = SdbFindFirstDWORDIndexedTag(pdbDetails,
  794. TAG_APPHELP,
  795. TAG_HTMLHELPID,
  796. pApphelpInfoContext->dwHtmlHelpID,
  797. &FindInfo);
  798. if (tiApphelpDetails == TAGID_NULL) {
  799. DBGPRINT((sdlError,
  800. "SdbQueryApphelpInformation",
  801. "Failed to find HTMLHELPID 0x%x in the details database.\n",
  802. pApphelpInfoContext->dwHtmlHelpID));
  803. return cbResult;
  804. }
  805. pApphelpInfoContext->tiApphelpDetails = tiApphelpDetails;
  806. }
  807. break;
  808. default:
  809. break;
  810. }
  811. switch(InfoClass) {
  812. case ApphelpExeTagID:
  813. pResult = &pApphelpInfoContext->tiExe;
  814. cbResult = sizeof(pApphelpInfoContext->tiExe);
  815. break;
  816. case ApphelpExeName:
  817. pdb = pApphelpInfoContext->pdb; // main db
  818. tiParent = pApphelpInfoContext->tiExe;
  819. ppwsz = &pApphelpInfoContext->pwszExeName;
  820. tag = TAG_NAME;
  821. cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
  822. break;
  823. case ApphelpAppName:
  824. pdb = pApphelpInfoContext->pdb; // main db
  825. tiParent = pApphelpInfoContext->tiExe;
  826. ppwsz = &pApphelpInfoContext->pwszAppName;
  827. tag = TAG_APP_NAME;
  828. cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
  829. break;
  830. case ApphelpVendorName:
  831. pdb = pApphelpInfoContext->pdb; // main db
  832. tiParent = pApphelpInfoContext->tiExe;
  833. ppwsz = &pApphelpInfoContext->pwszVendorName;
  834. tag = TAG_VENDOR;
  835. cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
  836. break;
  837. case ApphelpHtmlHelpID:
  838. pResult = &pApphelpInfoContext->dwHtmlHelpID;
  839. cbResult = sizeof(pApphelpInfoContext->dwHtmlHelpID);
  840. break;
  841. case ApphelpProblemSeverity:
  842. pResult = &pApphelpInfoContext->dwSeverity;
  843. cbResult = sizeof(pApphelpInfoContext->dwSeverity);
  844. break;
  845. case ApphelpFlags:
  846. pResult = &pApphelpInfoContext->dwFlags;
  847. cbResult = sizeof(pApphelpInfoContext->dwFlags);
  848. break;
  849. case ApphelpLinkURL:
  850. if (!SdbpReadApphelpLinkInformation(pApphelpInfoContext)) {
  851. break;
  852. }
  853. pResult = (LPWSTR)pApphelpInfoContext->pwszLinkURL;
  854. cbResult = SIZE_WSTRING(pResult);
  855. break;
  856. case ApphelpLinkText:
  857. if (!SdbpReadApphelpLinkInformation(pApphelpInfoContext)) {
  858. break;
  859. }
  860. pResult = (LPWSTR)pApphelpInfoContext->pwszLinkText;
  861. cbResult = SIZE_WSTRING(pResult);
  862. break;
  863. case ApphelpTitle:
  864. pdb = pdbDetails;
  865. tiParent = tiApphelpDetails;
  866. ppwsz = &pApphelpInfoContext->pwszTitle;
  867. tag = TAG_APPHELP_TITLE;
  868. cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
  869. break;
  870. case ApphelpDetails:
  871. pdb = pdbDetails;
  872. tiParent = tiApphelpDetails;
  873. ppwsz = &pApphelpInfoContext->pwszDetails;
  874. tag = TAG_APPHELP_DETAILS;
  875. cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
  876. break;
  877. case ApphelpContact:
  878. pdb = pdbDetails;
  879. tiParent = tiApphelpDetails;
  880. ppwsz = &pApphelpInfoContext->pwszContact;
  881. tag = TAG_APPHELP_CONTACT;
  882. cbResult = SdbpReadApphelpString(pdb, tiParent, tag, ppwsz, &pResult);
  883. break;
  884. case ApphelpHelpCenterURL:
  885. if (!SdbpCreateHelpCenterURL(hctx,
  886. pApphelpInfoContext->bOfflineContent,
  887. pApphelpInfoContext->bUseHtmlHelp,
  888. pApphelpInfoContext->ustrChmFile.Buffer)) {
  889. break;
  890. }
  891. pResult = pApphelpInfoContext->pwszHelpCtrURL;
  892. cbResult = SIZE_WSTRING(pResult);
  893. break;
  894. case ApphelpDatabaseGUID:
  895. pResult = &pApphelpInfoContext->guidDB;
  896. cbResult = sizeof(pApphelpInfoContext->guidDB);
  897. break;
  898. default:
  899. DBGPRINT((sdlError, "SdbQueryApphelpInformation",
  900. "Bad Apphelp Information class 0x%lx\n", InfoClass));
  901. return 0;
  902. break;
  903. }
  904. if (pBuffer == NULL || cbResult > cbSize) {
  905. return cbResult;
  906. }
  907. if (pResult != NULL && cbResult > 0) {
  908. RtlCopyMemory(pBuffer, pResult, cbResult);
  909. }
  910. return cbResult;
  911. }
  912. typedef HRESULT (STDAPICALLTYPE *PFNUrlUnescapeW)(
  913. LPWSTR pszUrl,
  914. LPWSTR pszUnescaped,
  915. LPDWORD pcchUnescaped,
  916. DWORD dwFlags);
  917. typedef HRESULT (STDAPICALLTYPE *PFNUrlEscapeW)(
  918. LPCWSTR pszURL,
  919. LPWSTR pszEscaped,
  920. LPDWORD pcchEscaped,
  921. DWORD dwFlags
  922. );
  923. //
  924. // if bUseHtmlHelp is set -- then bOfflineContent is also set to TRUE
  925. //
  926. BOOL
  927. SDBAPI
  928. SdbpCreateHelpCenterURL(
  929. IN HAPPHELPINFOCONTEXT hctx,
  930. IN BOOL bOfflineContent OPTIONAL, // pass FALSE
  931. IN BOOL bUseHtmlHelp OPTIONAL, // pass FALSE
  932. IN LPCWSTR pwszChmFile OPTIONAL // pass NULL
  933. )
  934. {
  935. WCHAR szAppHelpURL[2048];
  936. WCHAR szChmURL[1024];
  937. PAPPHELPINFOCONTEXT pApphelpInfo = (PAPPHELPINFOCONTEXT)hctx;
  938. HMODULE hModShlwapi = NULL;
  939. PFNUrlUnescapeW pfnUnescape;
  940. PFNUrlEscapeW pfnEscape;
  941. BOOL bSuccess = FALSE;
  942. int nChURL = 0; // counts used bytes
  943. int cch = 0;
  944. int nch;
  945. LPWSTR lpwszUnescaped = NULL;
  946. HRESULT hr;
  947. DWORD nChars;
  948. WCHAR szWindowsDir[MAX_PATH];
  949. BOOL bCustom;
  950. if (pApphelpInfo->pwszHelpCtrURL != NULL) {
  951. return TRUE;
  952. }
  953. if (bUseHtmlHelp) {
  954. bOfflineContent = TRUE;
  955. }
  956. // ping the database
  957. if (0 == SdbQueryApphelpInformation(hctx, ApphelpLinkURL, NULL, 0)) {
  958. return FALSE;
  959. }
  960. //
  961. // check and see whether it's custom apphelp or not
  962. //
  963. bCustom = !(pApphelpInfo->dwDatabaseType & SDB_DATABASE_MAIN);
  964. if (bCustom) {
  965. if (pApphelpInfo->pwszLinkURL != NULL) {
  966. nChURL = _snwprintf(szAppHelpURL, CHARCOUNT(szAppHelpURL),
  967. L"%ls", pApphelpInfo->pwszLinkURL);
  968. if (nChURL < 0) {
  969. DBGPRINT((sdlError, "SdbpCreateHelpCenterURL",
  970. "Custom apphelp URL %s is too long\n", pApphelpInfo->pwszLinkURL));
  971. goto out;
  972. }
  973. // now we are done
  974. goto createApphelpURL;
  975. } else {
  976. // custom apphelp will not fly without a link
  977. DBGPRINT((sdlError, "SdbpCreateHelpCenterURL", "Custom apphelp without a url link\n"));
  978. goto out;
  979. }
  980. }
  981. // unescape the URL
  982. hModShlwapi = LoadLibraryW(L"shlwapi.dll");
  983. if (hModShlwapi == NULL) {
  984. return FALSE;
  985. }
  986. pfnUnescape = (PFNUrlUnescapeW)GetProcAddress(hModShlwapi, "UrlUnescapeW");
  987. pfnEscape = (PFNUrlEscapeW) GetProcAddress(hModShlwapi, "UrlEscapeW");
  988. if (pfnUnescape == NULL || pfnEscape == NULL) {
  989. DBGPRINT((sdlError, "SdbpCreateHelpCenterURL", "Cannot get shlwapi functions\n"));
  990. goto out;
  991. }
  992. if (!bUseHtmlHelp) {
  993. nChURL = _snwprintf(szAppHelpURL,
  994. CHARCOUNT(szAppHelpURL),
  995. L"hcp://services/redirect?online=");
  996. }
  997. if (!bOfflineContent && pApphelpInfo->pwszLinkURL != NULL) {
  998. // unescape the url first using shell
  999. cch = wcslen(pApphelpInfo->pwszLinkURL) + 1;
  1000. STACK_ALLOC(lpwszUnescaped, cch * sizeof(WCHAR));
  1001. if (lpwszUnescaped == NULL) {
  1002. DBGPRINT((sdlError, "SdbpCreateHelpCenterURL",
  1003. "Error trying to allocate memory for \"%S\"\n", pApphelpInfo->pwszLinkURL));
  1004. goto out;
  1005. }
  1006. //
  1007. // Unescape round 1 - use the shell function (same as used to encode it for xml/database)
  1008. //
  1009. hr = pfnUnescape((LPTSTR)pApphelpInfo->pwszLinkURL, lpwszUnescaped, &cch, 0);
  1010. if (!SUCCEEDED(hr)) {
  1011. DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "UrlUnescapeW failed on \"%S\"\n", pApphelpInfo->pwszLinkURL));
  1012. goto out;
  1013. }
  1014. //
  1015. // round 2 - use our function borrowed from help center
  1016. //
  1017. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  1018. if (!SdbEscapeApphelpURL(szAppHelpURL + nChURL, &cch, lpwszUnescaped)) {
  1019. DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error escaping URL \"%S\"\n", lpwszUnescaped));
  1020. goto out;
  1021. }
  1022. nChURL += (int)cch;
  1023. }
  1024. //
  1025. // Retrieve the Windows directory.
  1026. //
  1027. nChars = GetWindowsDirectoryW(szWindowsDir, CHARCOUNT(szWindowsDir));
  1028. if (!nChars || nChars > CHARCOUNT(szWindowsDir)) {
  1029. DBGPRINT((sdlError, "SdbCreateHelpCenterURL",
  1030. "Error trying to retrieve Windows Directory %d.\n", GetLastError()));
  1031. goto out;
  1032. }
  1033. if (pwszChmFile != NULL) {
  1034. _snwprintf(szChmURL,
  1035. CHARCOUNT(szChmURL),
  1036. L"mk:@msitstore:%ls::/idh_w2_%d.htm",
  1037. pwszChmFile,
  1038. pApphelpInfo->dwHtmlHelpID);
  1039. } else { // standard chm file
  1040. //
  1041. // Attention: if we use hDlg here then, upon exit we will need to clean
  1042. // up the window.
  1043. //
  1044. _snwprintf(szChmURL,
  1045. CHARCOUNT(szChmURL),
  1046. L"mk:@msitstore:%ls\\help\\apps.chm::/idh_w2_%d.htm",
  1047. szWindowsDir,
  1048. pApphelpInfo->dwHtmlHelpID);
  1049. }
  1050. if (bOfflineContent) {
  1051. if (bUseHtmlHelp) {
  1052. cch = CHARCOUNT(szAppHelpURL);
  1053. hr = pfnEscape(szChmURL, szAppHelpURL, &cch, 0);
  1054. if (SUCCEEDED(hr)) {
  1055. nChURL += (INT)cch;
  1056. }
  1057. } else { // do not use html help
  1058. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  1059. if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
  1060. DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error escaping URL \"%S\"\n", szChmURL));
  1061. goto out;
  1062. }
  1063. nChURL += (INT)cch;
  1064. }
  1065. }
  1066. if (!bUseHtmlHelp) {
  1067. //
  1068. // now offline sequence
  1069. //
  1070. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  1071. nch = _snwprintf(szAppHelpURL + nChURL, cch, L"&offline=");
  1072. if (nch < 0) {
  1073. goto out;
  1074. }
  1075. nChURL += nch;
  1076. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  1077. if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
  1078. DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error escaping URL \"%S\"\n", szChmURL));
  1079. goto out;
  1080. }
  1081. nChURL += (int)cch;
  1082. }
  1083. *(szAppHelpURL + nChURL) = L'\0';
  1084. // we are done
  1085. // copy data now
  1086. createApphelpURL:
  1087. pApphelpInfo->pwszHelpCtrURL = (LPWSTR)SdbAlloc(nChURL * sizeof(WCHAR) + sizeof(UNICODE_NULL));
  1088. if (pApphelpInfo->pwszHelpCtrURL == NULL) {
  1089. DBGPRINT((sdlError, "SdbCreateHelpCenterURL", "Error allocating memory for the URL 0x%lx chars\n", nChURL));
  1090. goto out;
  1091. }
  1092. wcscpy(pApphelpInfo->pwszHelpCtrURL, szAppHelpURL);
  1093. bSuccess = TRUE;
  1094. out:
  1095. if (lpwszUnescaped != NULL) {
  1096. STACK_FREE(lpwszUnescaped);
  1097. }
  1098. if (hModShlwapi) {
  1099. FreeLibrary(hModShlwapi);
  1100. }
  1101. return bSuccess;
  1102. }
  1103. //
  1104. // returns TRUE if dialog was shown
  1105. // if there was an error, input parameter (pRunApp) will NOT
  1106. // be touched
  1107. BOOL
  1108. SdbShowApphelpDialog( // returns TRUE if success, whether we should run the app is in pRunApp
  1109. IN PAPPHELP_INFO pAHInfo, // the info necessary to find the apphelp data
  1110. IN PHANDLE phProcess, // [optional] returns the process handle of
  1111. // the process displaying the apphelp.
  1112. // When the process completes, the return value
  1113. // (from GetExitCodeProcess()) will be zero
  1114. // if the app should not run, or non-zero
  1115. // if it should run.
  1116. IN OUT BOOL* pRunApp
  1117. )
  1118. {
  1119. //
  1120. // basically just launch the apphelp.exe and wait for it to return
  1121. //
  1122. TCHAR szGuid[64];
  1123. TCHAR szCommandLine[MAX_PATH * 2 + 64];
  1124. LPTSTR pszCmdLine = szCommandLine;
  1125. STARTUPINFO StartupInfo;
  1126. PROCESS_INFORMATION ProcessInfo;
  1127. DWORD dwExit = 1; // by default and in case of failure, we allow to run the app
  1128. BOOL bReturn = FALSE;
  1129. BOOL bRunApp = TRUE; // by default we run the app ?
  1130. int nch; // chars in the buffer
  1131. nch = _stprintf(pszCmdLine, TEXT("ahui.exe"));
  1132. pszCmdLine += nch;
  1133. if (pAHInfo->tiExe != TAGID_NULL) {
  1134. // figure out what the default return value should be
  1135. if (!SdbGUIDToString(&pAHInfo->guidDB, szGuid)) {
  1136. DBGPRINT((sdlError, "SdbShowApphelpDialog",
  1137. "Failed to convert guid to string.\n"));
  1138. goto cleanup;
  1139. }
  1140. nch = _stprintf(pszCmdLine, L" %s 0x%lX", szGuid, pAHInfo->tiExe);
  1141. pszCmdLine += nch;
  1142. } else {
  1143. if (!pAHInfo->dwHtmlHelpID) {
  1144. DBGPRINT((sdlError, "SdbShowApphelpDialog",
  1145. "Neither HTMLHELPID nor tiExe provided\n"));
  1146. goto cleanup;
  1147. }
  1148. nch = _stprintf(pszCmdLine, TEXT(" /HTMLHELPID:0x%lx"), pAHInfo->dwHtmlHelpID);
  1149. pszCmdLine += nch;
  1150. nch = _stprintf(pszCmdLine, TEXT(" /SEVERITY:0x%lx"), pAHInfo->dwSeverity);
  1151. pszCmdLine += nch;
  1152. if (!SdbIsNullGUID(&pAHInfo->guidID)) {
  1153. if (SdbGUIDToString(&pAHInfo->guidID, szGuid)) {
  1154. nch = _stprintf(pszCmdLine, TEXT(" /GUID:%s"), szGuid);
  1155. pszCmdLine += nch;
  1156. }
  1157. }
  1158. if (pAHInfo->lpszAppName != NULL) {
  1159. nch = _stprintf(pszCmdLine, TEXT(" /APPNAME:\"%s\""), pAHInfo->lpszAppName);
  1160. pszCmdLine += nch;
  1161. }
  1162. }
  1163. if (pAHInfo->bPreserveChoice) {
  1164. nch = _stprintf(pszCmdLine, TEXT(" /PRESERVECHOICE"));
  1165. pszCmdLine += nch;
  1166. }
  1167. /*++
  1168. if (bOfflineContent) {
  1169. _tcscat(szCommand, TEXT(" /USELOCALCHM"));
  1170. }
  1171. if (bUseHTMLHelp) {
  1172. _tcscat(szCommand, TEXT(" /USEHTMLHELP"));
  1173. }
  1174. if (lpszChmFile) {
  1175. _tcscat(szCommand, TEXT(" /HTMLHELP:"));
  1176. _tcscat(szCommand, lpszChmFile);
  1177. }
  1178. if (lpszDetailsFile) {
  1179. _tcscat(szCommand, TEXT(" /DETAILS:"));
  1180. _tcscat(szCommand, lpszDetailsFile);
  1181. }
  1182. --*/
  1183. RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
  1184. RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
  1185. StartupInfo.cb = sizeof(StartupInfo);
  1186. if (!CreateProcessW(NULL,
  1187. szCommandLine,
  1188. NULL,
  1189. NULL,
  1190. FALSE,
  1191. 0,
  1192. NULL,
  1193. NULL,
  1194. &StartupInfo, &ProcessInfo)) {
  1195. DBGPRINT((sdlError, "SdbShowApphelpDialog",
  1196. "Failed to launch apphelp process.\n"));
  1197. goto cleanup;
  1198. }
  1199. //
  1200. // check to see if they want to monitor the process themselves
  1201. //
  1202. if (phProcess) {
  1203. bReturn = TRUE;
  1204. pRunApp = NULL; // we do this so that we don't touch the bRunApp
  1205. *phProcess = ProcessInfo.hProcess;
  1206. goto cleanup;
  1207. }
  1208. //
  1209. // otherwise, we'll do the waiting.
  1210. //
  1211. WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  1212. bReturn = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit);
  1213. if (bReturn) {
  1214. bRunApp = (0 != dwExit);
  1215. }
  1216. cleanup:
  1217. if (bReturn && pRunApp != NULL) {
  1218. *pRunApp = bRunApp;
  1219. }
  1220. //
  1221. // process handle is to be closed only when phProcess is NULL
  1222. //
  1223. if (phProcess == NULL && ProcessInfo.hProcess) {
  1224. CloseHandle(ProcessInfo.hProcess);
  1225. }
  1226. if (ProcessInfo.hThread) {
  1227. CloseHandle(ProcessInfo.hThread);
  1228. }
  1229. return bReturn;
  1230. }