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.

1480 lines
41 KiB

  1. /*--
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. ahui.cpp
  5. Abstract:
  6. Shows an apphelp message, and returns 0 if the program shouldn't run, and non-
  7. zero if the program should run
  8. Accepts a command line with a GUID and a TAGID, in the following format:
  9. {243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E
  10. Author:
  11. dmunsil 04/03/2001
  12. Revision History:
  13. Notes:
  14. --*/
  15. #define _UNICODE
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <stddef.h>
  21. #include <memory.h>
  22. #include <malloc.h>
  23. #include <nt.h>
  24. #include <ntrtl.h>
  25. #include <nturtl.h>
  26. #include <windows.h>
  27. #include <windowsx.h>
  28. #include <htmlhelp.h>
  29. extern "C" {
  30. #include "shimdb.h"
  31. }
  32. #include "ids.h"
  33. #include "shlobj.h"
  34. #include "shlobjp.h"
  35. #include "shellapi.h"
  36. #include "shlwapi.h"
  37. //
  38. // same is in shell/published and also the same as used in shimdbc
  39. //
  40. #define APPTYPE_TYPE_MASK 0x000000FF
  41. #define APPTYPE_INC_NOBLOCK 0x00000001
  42. #define APPTYPE_INC_HARDBLOCK 0x00000002
  43. #define APPTYPE_MINORPROBLEM 0x00000003
  44. #define APPTYPE_REINSTALL 0x00000004
  45. #define APPTYPE_VERSIONSUB 0x00000005
  46. #define APPTYPE_SHIM 0x00000006
  47. #define APPTYPE_NONE 0x00000000
  48. extern "C" VOID AllowForegroundActivation(VOID);
  49. enum ShimAppHelpSeverityType
  50. {
  51. APPHELP_MINORPROBLEM = APPTYPE_MINORPROBLEM,
  52. APPHELP_HARDBLOCK = APPTYPE_INC_HARDBLOCK,
  53. APPHELP_NOBLOCK = APPTYPE_INC_NOBLOCK,
  54. APPHELP_VERSIONSUB = APPTYPE_VERSIONSUB,
  55. APPHELP_SHIM = APPTYPE_SHIM,
  56. APPHELP_REINSTALL = APPTYPE_REINSTALL,
  57. APPHELP_NONE = APPTYPE_NONE
  58. };
  59. #define APPHELP_DIALOG_FAILED ((DWORD)-1)
  60. //
  61. // TODO: add parameters to apphelp.exe's command line to work with these
  62. // variables.
  63. //
  64. BOOL g_bShowOnlyOfflineContent = FALSE;
  65. BOOL g_bUseHtmlHelp = FALSE;
  66. WCHAR g_wszApphelpPath[MAX_PATH];
  67. HFONT g_hFontBold = NULL;
  68. HINSTANCE g_hInstance;
  69. //
  70. // Global variables used while parsing args
  71. //
  72. DWORD g_dwHtmlHelpID;
  73. DWORD g_dwTagID;
  74. DWORD g_dwSeverity;
  75. LPCWSTR g_pAppName;
  76. LPCWSTR g_pszGuid;
  77. BOOL g_bPreserveChoice;
  78. WCHAR wszHtmlHelpID[] = L"HtmlHelpID";
  79. WCHAR wszAppName[] = L"AppName";
  80. WCHAR wszSeverity[] = L"Severity";
  81. WCHAR wszGUID[] = L"GUID";
  82. WCHAR wszTagID[] = L"TagID";
  83. WCHAR wszOfflineContent[] = L"OfflineContent";
  84. WCHAR wszPreserveChoice[] = L"PreserveChoice";
  85. //
  86. // FORWARD DECLARATIONS OF FUNCTIONS
  87. DWORD
  88. ShowApphelpDialog(
  89. IN PAPPHELP_DATA pApphelpData
  90. );
  91. DWORD
  92. ShowApphelp(
  93. IN PAPPHELP_DATA pApphelpData,
  94. IN LPCWSTR pwszDetailsDatabasePath,
  95. IN PDB pdbDetails
  96. )
  97. /*++
  98. Return: The return value can be one of the following based on what the user has
  99. selected:
  100. -1 - failed to show the info
  101. IDOK | 0x8000 - "no ui" checked, run the app
  102. IDCANCEL - do not run the app
  103. IDOK - run the app
  104. Desc: Open the details database, collect the details info and then show it.
  105. --*/
  106. {
  107. DWORD dwRet = APPHELP_DIALOG_FAILED;
  108. BOOL bCloseDetails = FALSE;
  109. if (pdbDetails == NULL) {
  110. //
  111. // Open the database containing the details info, if one wasn't passed in.
  112. //
  113. if (pApphelpData->bSPEntry) {
  114. pdbDetails = SdbOpenApphelpDetailsDatabaseSP();
  115. } else {
  116. pdbDetails = SdbOpenApphelpDetailsDatabase(pwszDetailsDatabasePath);
  117. }
  118. bCloseDetails = TRUE;
  119. if (pdbDetails == NULL) {
  120. DBGPRINT((sdlError, "ShowApphelp", "Failed to open the details database.\n"));
  121. goto Done;
  122. }
  123. }
  124. //
  125. // Read apphelp details data.
  126. //
  127. if (!SdbReadApphelpDetailsData(pdbDetails, pApphelpData)) {
  128. DBGPRINT((sdlError, "ShowApphelp", "Failed to read apphelp details.\n"));
  129. goto Done;
  130. }
  131. //
  132. // Show the dialog box. The return values can be:
  133. // -1 - error
  134. // IDOK | 0x8000 - "no ui" checked, run the app
  135. // IDCANCEL - do not run the app
  136. // IDOK - run the app
  137. //
  138. dwRet = ShowApphelpDialog(pApphelpData);
  139. if (dwRet == APPHELP_DIALOG_FAILED) {
  140. DBGPRINT((sdlError, "ShowApphelp", "Failed to show the apphelp info.\n"));
  141. }
  142. Done:
  143. if (pdbDetails != NULL && bCloseDetails) {
  144. SdbCloseDatabase(pdbDetails);
  145. }
  146. return dwRet;
  147. }
  148. void
  149. FixEditControlScrollBar(
  150. IN HWND hDlg,
  151. IN int nCtrlId
  152. )
  153. /*++
  154. Return: void.
  155. Desc: This function tricks the edit control to not show the vertical scrollbar
  156. unless absolutely necessary.
  157. --*/
  158. {
  159. HFONT hFont = NULL;
  160. HFONT hFontOld = NULL;
  161. HDC hDC = NULL;
  162. TEXTMETRICW tm;
  163. RECT rc;
  164. int nVisibleLines = 0;
  165. int nLines;
  166. DWORD dwStyle;
  167. HWND hCtl;
  168. //
  169. // Get the edit control's rectangle.
  170. //
  171. SendDlgItemMessageW(hDlg, nCtrlId, EM_GETRECT, 0, (LPARAM)&rc);
  172. //
  173. // Retrieve the number of lines.
  174. //
  175. nLines = (int)SendDlgItemMessageW(hDlg, nCtrlId, EM_GETLINECOUNT, 0, 0);
  176. //
  177. // Calculate how many lines will fit.
  178. //
  179. hFont = (HFONT)SendDlgItemMessageW(hDlg, nCtrlId, WM_GETFONT, 0, 0);
  180. if (hFont != NULL) {
  181. hDC = CreateCompatibleDC(NULL);
  182. if (hDC != NULL) {
  183. hFontOld = (HFONT)SelectObject(hDC, hFont);
  184. //
  185. // Now get the metrics
  186. //
  187. if (GetTextMetricsW(hDC, &tm)) {
  188. nVisibleLines = (rc.bottom - rc.top) / tm.tmHeight;
  189. }
  190. SelectObject(hDC, hFontOld);
  191. DeleteDC(hDC);
  192. }
  193. }
  194. if (nVisibleLines && nVisibleLines >= nLines) {
  195. hCtl = GetDlgItem(hDlg, nCtrlId);
  196. dwStyle = (DWORD)GetWindowLongPtrW(hCtl, GWL_STYLE);
  197. SetWindowLongPtrW(hCtl, GWL_STYLE, (LONG)(dwStyle & ~WS_VSCROLL));
  198. SetWindowPos(hCtl,
  199. NULL,
  200. 0,
  201. 0,
  202. 0,
  203. 0,
  204. SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
  205. }
  206. }
  207. BOOL
  208. ShowApphelpHtmlHelp(
  209. HWND hDlg,
  210. PAPPHELP_DATA pApphelpData
  211. )
  212. /*++
  213. Return: TRUE on success, FALSE otherwise.
  214. Desc: Shows html help using hhctrl.ocx
  215. --*/
  216. {
  217. WCHAR szAppHelpURL[2048];
  218. WCHAR szWindowsDir[MAX_PATH];
  219. WCHAR szChmURL[1024];
  220. WCHAR szChmFile[MAX_PATH];
  221. HINSTANCE hInst = NULL;
  222. UINT nChars;
  223. int nChURL, nch;
  224. HRESULT hr;
  225. DWORD cch;
  226. LPWSTR lpwszUnescaped = NULL;
  227. BOOL bSuccess = FALSE;
  228. BOOL bCustom = FALSE;
  229. LCID lcid;
  230. size_t nLen;
  231. BOOL bFound = FALSE;
  232. bCustom = !(pApphelpData->dwData & SDB_DATABASE_MAIN);
  233. // apphelp is not in the main database, then it's custom apphelp
  234. nChars = GetSystemWindowsDirectoryW(szWindowsDir,
  235. CHARCOUNT(szWindowsDir));
  236. if (!nChars || nChars > CHARCOUNT(szWindowsDir)) {
  237. DBGPRINT((sdlError, "ShowApphelpHtmlHelp",
  238. "Error trying to retrieve Windows Directory %d.\n", GetLastError()));
  239. goto errHandle;
  240. }
  241. if (bCustom) {
  242. //
  243. // this is a custom DB, and therefore the URL in it should be taken
  244. // as-is, without using the MS redirector
  245. //
  246. wcscpy(szAppHelpURL, pApphelpData->szURL);
  247. } else {
  248. WCHAR szLcid[16] = L"";
  249. lcid = GetUserDefaultUILanguage();
  250. if (pApphelpData->bSPEntry) {
  251. _snwprintf(szChmFile, CHARCOUNT(szChmFile), L"%s\\Help\\apps_sp.chm", szWindowsDir);
  252. _snwprintf(szLcid, CHARCOUNT(szLcid), L"_%x.htm", lcid);
  253. } else {
  254. wcscpy(szLcid, L".htm");
  255. if (lcid != MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)) {
  256. nLen = _snwprintf(szChmFile,
  257. CHARCOUNT(szChmFile),
  258. L"%s\\Help\\MUI\\%04x\\apps.chm",
  259. szWindowsDir,
  260. lcid);
  261. if (nLen > 0) {
  262. bFound = RtlDoesFileExists_U(szChmFile);
  263. }
  264. }
  265. if (!bFound) {
  266. _snwprintf(szChmFile, CHARCOUNT(szChmFile), L"%s\\Help\\apps.chm", szWindowsDir);
  267. }
  268. }
  269. nChURL = _snwprintf(szAppHelpURL,
  270. CHARCOUNT(szAppHelpURL),
  271. L"hcp://services/redirect?online=");
  272. //
  273. // When we are compiling retail we check for the offline content as well.
  274. //
  275. if (!g_bShowOnlyOfflineContent) {
  276. //
  277. // First thing, unescape url
  278. //
  279. if (pApphelpData->szURL != NULL) {
  280. //
  281. // Unescape the url first, using shell.
  282. //
  283. cch = wcslen(pApphelpData->szURL) + 1;
  284. lpwszUnescaped = (LPWSTR)malloc(cch * sizeof(WCHAR));
  285. if (lpwszUnescaped == NULL) {
  286. DBGPRINT((sdlError,
  287. "ShowApphelpHtmlHelp",
  288. "Error trying to allocate memory for \"%S\"\n",
  289. pApphelpData->szURL));
  290. goto errHandle;
  291. }
  292. //
  293. // Unescape round 1 - use the shell function (same as used to encode
  294. // it for xml/database).
  295. //
  296. hr = UrlUnescapeW(pApphelpData->szURL, lpwszUnescaped, &cch, 0);
  297. if (!SUCCEEDED(hr)) {
  298. DBGPRINT((sdlError,
  299. "ShowApphelpHtmlHelp",
  300. "UrlUnescapeW failed on \"%S\"\n",
  301. pApphelpData->szURL));
  302. goto errHandle;
  303. }
  304. //
  305. // Unescape round 2 - use our function borrowed from help center
  306. //
  307. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  308. if (!SdbEscapeApphelpURL(szAppHelpURL + nChURL, &cch, lpwszUnescaped)) {
  309. DBGPRINT((sdlError,
  310. "ShowApphelpHtmlHelp",
  311. "Error escaping URL \"%S\"\n",
  312. lpwszUnescaped));
  313. goto errHandle;
  314. }
  315. nChURL += (int)cch;
  316. }
  317. }
  318. //
  319. // At this point szAppHelpURL contains redirected URL for online usage
  320. // for custom db szAppHelpURL contains full URL
  321. //
  322. // If Apphelp file is provided -- use it
  323. //
  324. if (*g_wszApphelpPath) {
  325. _snwprintf(szChmURL,
  326. CHARCOUNT(szChmURL),
  327. L"mk:@msitstore:%ls::/idh_w2_%d%s",
  328. g_wszApphelpPath,
  329. pApphelpData->dwHTMLHelpID,
  330. szLcid);
  331. } else {
  332. _snwprintf(szChmURL,
  333. CHARCOUNT(szChmURL),
  334. L"mk:@msitstore:%ls::/idh_w2_%d%s",
  335. szChmFile,
  336. pApphelpData->dwHTMLHelpID,
  337. szLcid);
  338. }
  339. //
  340. // at this point szChmURL contains a URL pointing to the offline help file
  341. // we put it into the szAppHelpURL for both online and offline case
  342. //
  343. if (g_bShowOnlyOfflineContent) {
  344. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  345. if (g_bUseHtmlHelp) {
  346. hr = UrlEscapeW(szChmURL, szAppHelpURL + nChURL, &cch, 0);
  347. if (SUCCEEDED(hr)) {
  348. nChURL += (INT)cch;
  349. }
  350. } else {
  351. if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
  352. DBGPRINT((sdlError, "ShowApphelpHtmlHelp", "Error escaping URL \"%S\"\n", szChmURL));
  353. goto errHandle;
  354. }
  355. nChURL += (int)cch;
  356. }
  357. }
  358. //
  359. // now offline sequence
  360. //
  361. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  362. nch = _snwprintf(szAppHelpURL + nChURL, cch, L"&offline=");
  363. nChURL += nch;
  364. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  365. if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
  366. DBGPRINT((sdlError, "ShowApphelpHtmlHelp", "Error escaping URL \"%S\"\n", szChmURL));
  367. goto errHandle;
  368. }
  369. nChURL += (int)cch;
  370. *(szAppHelpURL + nChURL) = L'\0';
  371. }
  372. //
  373. // WARNING: On Whistler execution of the following line will cause
  374. // an AV (it works properly on Win2k) when it's executed twice
  375. // from the same process. We should be able to just call
  376. // shell with szAppHelpURL but we can't.
  377. // So for now, use hh.exe as the stub.
  378. //
  379. // right before we do ShellExecute -- set current directory to windows dir
  380. //
  381. SetCurrentDirectoryW(szWindowsDir);
  382. if (g_bUseHtmlHelp && !bCustom) {
  383. DBGPRINT((sdlInfo, "ShowApphelpHtmlHelp", "Opening Apphelp URL \"%S\"\n", szChmURL));
  384. hInst = ShellExecuteW(hDlg, L"open", L"hh.exe", szChmURL, NULL, SW_SHOWNORMAL);
  385. } else if (!bCustom) {
  386. WCHAR szHSCPath[MAX_PATH];
  387. WCHAR* pszParameters;
  388. size_t cchUrl = ARRAYSIZE(szAppHelpURL);
  389. static WCHAR szUrlPrefix[] = L"-url ";
  390. nch = _snwprintf(szHSCPath, CHARCOUNT(szHSCPath),
  391. L"%s\\pchealth\\helpctr\\binaries\\helpctr.exe",
  392. szWindowsDir);
  393. if (nch < 0) {
  394. DBGPRINT((sdlError, "ShowApphelpHtmlHelp", "Windows path to helpctr too long %S\n", szWindowsDir));
  395. goto errHandle;
  396. }
  397. cchUrl = CHARCOUNT(szUrlPrefix) + wcslen(szAppHelpURL) + 1;
  398. pszParameters = new WCHAR[cchUrl];
  399. if (pszParameters == NULL) {
  400. bSuccess = FALSE;
  401. goto errHandle;
  402. }
  403. wcscpy(pszParameters, szUrlPrefix);
  404. wcscat(pszParameters, szAppHelpURL);
  405. DBGPRINT((sdlInfo,
  406. "ShowApphelpHtmlHelp",
  407. "Opening APPHELP URL \"%S\"\n",
  408. szAppHelpURL));
  409. hInst = ShellExecuteW(hDlg, L"open", szHSCPath, pszParameters, NULL, SW_SHOWNORMAL);
  410. delete[] pszParameters;
  411. } else {
  412. DBGPRINT((sdlInfo,
  413. "ShowApphelpHtmlHelp",
  414. "Opening Custom APPHELP URL \"%S\"\n",
  415. szAppHelpURL));
  416. hInst = ShellExecuteW(hDlg, L"open", szAppHelpURL, NULL, NULL, SW_SHOWNORMAL);
  417. }
  418. if (HandleToUlong(hInst) <= 32) {
  419. DBGPRINT((sdlError,
  420. "ShowApphelpHtmlHelp",
  421. "Error 0x%p trying to show help topic \"%ls\"\n",
  422. hInst,
  423. szAppHelpURL));
  424. }
  425. //
  426. // If we unload html help now we'll get weird and unpredictable behavior!
  427. // So don't do it :-(
  428. //
  429. bSuccess = (HandleToUlong(hInst) > 32);
  430. errHandle:
  431. if (lpwszUnescaped != NULL) {
  432. free(lpwszUnescaped);
  433. }
  434. return bSuccess;
  435. }
  436. INT_PTR
  437. AppCompatDlgProc(
  438. HWND hDlg,
  439. UINT uMsg,
  440. WPARAM wParam,
  441. LPARAM lParam
  442. )
  443. /*++
  444. Return: void.
  445. Desc: This is the dialog proc for the apphelp dialog.
  446. --*/
  447. {
  448. BOOL bReturn = TRUE;
  449. PAPPHELP_DATA pApphelpData;
  450. pApphelpData = (PAPPHELP_DATA)GetWindowLongPtrW(hDlg, DWLP_USER);
  451. switch (uMsg) {
  452. case WM_INITDIALOG:
  453. {
  454. WCHAR wszMessage[2048];
  455. DWORD dwResActionString;
  456. HFONT hFont;
  457. LOGFONTW LogFont;
  458. WCHAR* pwszAppTitle;
  459. INT nChars;
  460. DWORD dwDefID = IDD_DETAILS;
  461. DWORD dwDefBtn; // old default button id
  462. HICON hIcon;
  463. LPWSTR IconID = MAKEINTRESOURCEW(IDI_WARNING);
  464. pApphelpData = (PAPPHELP_DATA)lParam;
  465. SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)pApphelpData);
  466. //
  467. // Show the app icon.
  468. //
  469. hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDD_ICON_TRASH));
  470. SetClassLongPtr(hDlg, GCLP_HICON, (LONG_PTR)hIcon);
  471. mouse_event(MOUSEEVENTF_WHEEL, 0, 0, 0, 0);
  472. SetForegroundWindow(hDlg);
  473. pwszAppTitle = pApphelpData->szAppTitle;
  474. if (pwszAppTitle == NULL) {
  475. pwszAppTitle = pApphelpData->szAppName;
  476. }
  477. if (pwszAppTitle != NULL) {
  478. SetDlgItemTextW(hDlg, IDD_APPNAME, pwszAppTitle);
  479. //
  480. // Make sure that we only utilize the first line of that text
  481. // for the window title.
  482. //
  483. SetWindowTextW(hDlg, pwszAppTitle);
  484. }
  485. hFont = (HFONT)SendDlgItemMessageW(hDlg,
  486. IDD_APPNAME,
  487. WM_GETFONT,
  488. 0, 0);
  489. if (hFont && GetObjectW(hFont, sizeof(LogFont), (LPVOID)&LogFont)) {
  490. LogFont.lfWeight = FW_BOLD;
  491. hFont = CreateFontIndirectW(&LogFont);
  492. if (hFont != NULL) {
  493. g_hFontBold = hFont;
  494. SendDlgItemMessageW(hDlg,
  495. IDD_APPNAME,
  496. WM_SETFONT,
  497. (WPARAM)hFont, TRUE);
  498. }
  499. }
  500. //
  501. // By default, we have both RUN AND CANCEL
  502. //
  503. dwResActionString = IDS_APPCOMPAT_RUNCANCEL;
  504. switch (pApphelpData->dwSeverity) {
  505. case APPHELP_HARDBLOCK:
  506. //
  507. // Disable run button and "don't show this again" box
  508. // Reset the "defpushbutton" style from this one...
  509. //
  510. EnableWindow(GetDlgItem(hDlg, IDD_STATE), FALSE);
  511. EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
  512. dwResActionString = IDS_APPCOMPAT_CANCEL;
  513. dwDefID = IDD_DETAILS; // set for hardblock case since RUN is not avail
  514. IconID = MAKEINTRESOURCEW(IDI_ERROR);
  515. break;
  516. case APPHELP_MINORPROBLEM:
  517. break;
  518. case APPHELP_NOBLOCK:
  519. break;
  520. case APPHELP_REINSTALL:
  521. break;
  522. }
  523. //
  524. // if we have no URL, or the URL begins with "null" gray out the "details" button
  525. //
  526. if (!pApphelpData->szURL || !pApphelpData->szURL[0] ||
  527. _wcsnicmp(pApphelpData->szURL, L"null", 4) == 0) {
  528. EnableWindow(GetDlgItem(hDlg, IDD_DETAILS), FALSE);
  529. }
  530. hIcon = LoadIconW(NULL, IconID);
  531. if (hIcon != NULL) {
  532. SendDlgItemMessageW(hDlg, IDD_ICON, STM_SETICON, (WPARAM)hIcon, 0);
  533. }
  534. //
  535. // Set the default push button
  536. // Reset the current default push button to a regular button.
  537. //
  538. // Update the default push button's control ID.
  539. //
  540. dwDefBtn = (DWORD)SendMessageW(hDlg, DM_GETDEFID, 0, 0);
  541. if (HIWORD(dwDefBtn) == DC_HASDEFID) {
  542. dwDefBtn = LOWORD(dwDefBtn);
  543. SendDlgItemMessageW(hDlg, dwDefBtn, BM_SETSTYLE, BS_PUSHBUTTON, TRUE);
  544. }
  545. SendDlgItemMessageW(hDlg, dwDefID, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
  546. SendMessageW(hDlg, DM_SETDEFID, (WPARAM)dwDefID, 0);
  547. //
  548. // now set the focus
  549. // be careful and do not mess with other focus-related messages, else use PostMessage here
  550. //
  551. SendMessageW(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, dwDefID), TRUE);
  552. //
  553. // If dwHTMLHelpID is not present disable "Details" button
  554. //
  555. if (!pApphelpData->dwHTMLHelpID) {
  556. EnableWindow(GetDlgItem(hDlg, IDD_DETAILS), FALSE);
  557. }
  558. wszMessage[0] = L'\0';
  559. LoadStringW(g_hInstance,
  560. dwResActionString,
  561. wszMessage,
  562. sizeof(wszMessage) / sizeof(WCHAR));
  563. SetDlgItemTextW(hDlg, IDD_LINE_2, wszMessage);
  564. SetDlgItemTextW(hDlg,
  565. IDD_APPHELP_DETAILS,
  566. pApphelpData->szDetails ? pApphelpData->szDetails : L"");
  567. FixEditControlScrollBar(hDlg, IDD_APPHELP_DETAILS);
  568. //
  569. // Return false so that the default focus-setting would not apply.
  570. //
  571. bReturn = FALSE;
  572. break;
  573. }
  574. case WM_DESTROY:
  575. //
  576. // perform cleanup - remove the font we've had created
  577. //
  578. if (g_hFontBold != NULL) {
  579. DeleteObject(g_hFontBold);
  580. g_hFontBold = NULL;
  581. }
  582. AllowForegroundActivation();
  583. PostQuitMessage(0); // we just bailed out
  584. break;
  585. case WM_COMMAND:
  586. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  587. case IDOK:
  588. //
  589. // Check the NO UI checkbox
  590. //
  591. EndDialog(hDlg, (INT_PTR)(IsDlgButtonChecked(hDlg, IDD_STATE) ? (IDOK | 0x8000) : IDOK));
  592. break;
  593. case IDCANCEL:
  594. EndDialog(hDlg, (INT_PTR)(IsDlgButtonChecked(hDlg, IDD_STATE) && g_bPreserveChoice ? (IDCANCEL | 0x8000) : IDCANCEL));
  595. break;
  596. case IDD_DETAILS:
  597. //
  598. // Launch details.
  599. //
  600. ShowApphelpHtmlHelp(hDlg, pApphelpData);
  601. break;
  602. default:
  603. bReturn = FALSE;
  604. break;
  605. }
  606. break;
  607. default:
  608. bReturn = FALSE;
  609. break;
  610. }
  611. return bReturn;
  612. }
  613. typedef NTSTATUS (NTAPI *PFNUSERTESTTOKENFORINTERACTIVE)(HANDLE Token, PLUID pluidCaller);
  614. PFNUSERTESTTOKENFORINTERACTIVE UserTestTokenForInteractive = NULL;
  615. BOOL
  616. CheckUserToken(
  617. )
  618. /*++
  619. returns TRUE if the apphelp should be shown
  620. FALSE if we should not present apphelp UI
  621. --*/
  622. {
  623. NTSTATUS Status;
  624. HANDLE hToken = NULL;
  625. LUID LuidUser;
  626. HMODULE hWinsrv = NULL;
  627. BOOL bShowUI = FALSE;
  628. UINT nChars;
  629. static
  630. WCHAR szWinsrvDllName[] = L"winsrv.dll";
  631. WCHAR szSystemDir[MAX_PATH];
  632. WCHAR szWinsrvDll[MAX_PATH];
  633. BOOL bWow64 = FALSE;
  634. if (IsWow64Process(GetCurrentProcess(), &bWow64)) {
  635. if (bWow64) {
  636. return TRUE;
  637. }
  638. }
  639. nChars = GetSystemDirectoryW(szSystemDir,
  640. CHARCOUNT(szSystemDir));
  641. if (nChars == 0 || nChars > CHARCOUNT(szSystemDir) - 1 - CHARCOUNT(szWinsrvDllName)) {
  642. DBGPRINT((sdlError, "CheckUserToken",
  643. "Error trying to retrieve windows system dir %d\n", GetLastError()));
  644. *szSystemDir = L'\0';
  645. nChars = 0;
  646. } else {
  647. //
  648. // safe, we have counted the winsrv.dll (which includes 0-terminator),
  649. // 1 = '\\' character
  650. // and szSystemDir
  651. //
  652. szSystemDir[nChars++] = L'\\';
  653. szSystemDir[nChars] = L'\0';
  654. }
  655. //
  656. // extra check just to be safe, nChars includes now '\\'
  657. //
  658. if (nChars + CHARCOUNT(szWinsrvDllName) > CHARCOUNT(szWinsrvDll)) {
  659. goto ErrHandle;
  660. }
  661. wcscpy(szWinsrvDll, szSystemDir);
  662. wcscat(szWinsrvDll, szWinsrvDllName);
  663. hWinsrv = LoadLibraryW(szWinsrvDll);
  664. if (hWinsrv == NULL) {
  665. goto ErrHandle;
  666. }
  667. UserTestTokenForInteractive = (PFNUSERTESTTOKENFORINTERACTIVE)GetProcAddress(hWinsrv,
  668. "_UserTestTokenForInteractive");
  669. if (UserTestTokenForInteractive == NULL) {
  670. goto ErrHandle;
  671. }
  672. Status = NtOpenProcessToken(NtCurrentProcess(),
  673. TOKEN_QUERY,
  674. &hToken);
  675. if (NT_SUCCESS(Status)) {
  676. Status = UserTestTokenForInteractive(hToken, &LuidUser);
  677. NtClose(hToken);
  678. if (NT_SUCCESS(Status)) {
  679. bShowUI = TRUE;
  680. goto ErrHandle;
  681. }
  682. }
  683. Status = NtOpenThreadToken(NtCurrentThread(),
  684. TOKEN_QUERY,
  685. TRUE,
  686. &hToken);
  687. if (NT_SUCCESS(Status)) {
  688. Status = UserTestTokenForInteractive(hToken, &LuidUser);
  689. NtClose(hToken);
  690. if (NT_SUCCESS(Status)) {
  691. bShowUI = TRUE;
  692. goto ErrHandle;
  693. }
  694. }
  695. ErrHandle:
  696. if (hWinsrv) {
  697. FreeLibrary(hWinsrv);
  698. }
  699. return bShowUI;
  700. }
  701. BOOL
  702. CheckWindowStation(
  703. )
  704. /*++
  705. returns TRUE if the apphelp should be shown
  706. FALSE if we should not bother with apphelp UI
  707. --*/
  708. {
  709. HWINSTA hWindowStation;
  710. BOOL bShowUI = FALSE;
  711. DWORD dwLength = 0;
  712. DWORD dwBufferSize = 0;
  713. DWORD dwError;
  714. BOOL bSuccess;
  715. LPWSTR pwszWindowStation = NULL;
  716. hWindowStation = GetProcessWindowStation();
  717. if (hWindowStation == NULL) {
  718. DBGPRINT((sdlError,
  719. "ApphelpCheckWindowStation",
  720. "GetProcessWindowStation failed error 0x%lx\n", GetLastError()));
  721. goto ErrHandle; // the app is not a Windows NT/Windows 2000 app??? try to show UI
  722. }
  723. // get the information please
  724. bSuccess = GetUserObjectInformationW(hWindowStation, UOI_NAME, NULL, 0, &dwBufferSize);
  725. if (!bSuccess) {
  726. dwError = GetLastError();
  727. if (dwError != ERROR_INSUFFICIENT_BUFFER) {
  728. DBGPRINT((sdlError,
  729. "ApphelpCheckWindowStation",
  730. "GetUserObjectInformation failed error 0x%lx\n", dwError));
  731. goto ErrHandle;
  732. }
  733. pwszWindowStation = (LPWSTR)malloc(dwBufferSize);
  734. if (pwszWindowStation == NULL) {
  735. DBGPRINT((sdlError,
  736. "ApphelpCheckWindowStation",
  737. "Failed to allocate 0x%lx bytes for Window Station name\n", dwBufferSize));
  738. goto ErrHandle;
  739. }
  740. // ok, call again
  741. bSuccess = GetUserObjectInformationW(hWindowStation,
  742. UOI_NAME,
  743. pwszWindowStation,
  744. dwBufferSize,
  745. &dwLength);
  746. if (!bSuccess) {
  747. DBGPRINT((sdlError,
  748. "ApphelpCheckWindowStation",
  749. "GetUserObjectInformation failed error 0x%lx, buffer size 0x%lx returned 0x%lx\n",
  750. GetLastError(), dwBufferSize, dwLength));
  751. goto ErrHandle;
  752. }
  753. // now we have window station name, compare it to winsta0
  754. //
  755. bShowUI = (_wcsicmp(pwszWindowStation, L"Winsta0") == 0);
  756. if (!bShowUI) {
  757. DBGPRINT((sdlInfo,
  758. "ApphelpCheckWindowStation",
  759. "Apphelp UI will not be shown, running this process on window station \"%s\"\n",
  760. pwszWindowStation));
  761. }
  762. }
  763. ErrHandle:
  764. // should we do a close handle ???
  765. //
  766. if (hWindowStation != NULL) {
  767. CloseWindowStation(hWindowStation);
  768. }
  769. if (pwszWindowStation != NULL) {
  770. free(pwszWindowStation);
  771. }
  772. return bShowUI;
  773. }
  774. DWORD
  775. ShowApphelpDialog(
  776. IN PAPPHELP_DATA pApphelpData
  777. )
  778. /*++
  779. Return: (IDOK | IDCANCEL) | [0x8000]
  780. IDOK | 0x8000 - the user has chosen to run the app and
  781. checked "don't show me this anymore"
  782. IDOK - the user has chosen to run the app, dialog will be shown again
  783. IDCANCEL - the user has chosen not to run the app
  784. -1 - we have failed to import APIs necessary to show dlg box
  785. Desc: Shows the dialog box with apphelp info.
  786. --*/
  787. {
  788. BOOL bSuccess;
  789. INT_PTR retVal = 0;
  790. retVal = DialogBoxParamW(g_hInstance,
  791. MAKEINTRESOURCEW(DLG_APPCOMPAT),
  792. NULL,
  793. (DLGPROC)AppCompatDlgProc,
  794. (LPARAM)pApphelpData); // parameter happens to be a pointer of type PAPPHELP_DATA
  795. return (DWORD)retVal;
  796. }
  797. VOID
  798. ParseCommandLineArgs(
  799. int argc,
  800. WCHAR* argv[]
  801. )
  802. {
  803. WCHAR ch;
  804. WCHAR* pArg;
  805. WCHAR* pEnd;
  806. while (--argc) {
  807. pArg = argv[argc];
  808. if (*pArg == L'/' || *pArg == '-') {
  809. ch = *++pArg;
  810. switch(towupper(ch)) {
  811. case L'H':
  812. if (!_wcsnicmp(pArg, wszHtmlHelpID, CHARCOUNT(wszHtmlHelpID)-1)) {
  813. pArg = wcschr(pArg, L':');
  814. if (pArg) {
  815. ++pArg; // skip over :
  816. g_dwHtmlHelpID = (DWORD)wcstoul(pArg, &pEnd, 0);
  817. }
  818. }
  819. break;
  820. case L'A':
  821. if (!_wcsnicmp(pArg, wszAppName, CHARCOUNT(wszAppName)-1)) {
  822. pArg = wcschr(pArg, L':');
  823. if (pArg) {
  824. ++pArg;
  825. g_pAppName = pArg; // this is app name, remove the quotes
  826. }
  827. }
  828. break;
  829. case L'S':
  830. if (!_wcsnicmp(pArg, wszSeverity, CHARCOUNT(wszSeverity)-1)) {
  831. pArg = wcschr(pArg, L':');
  832. if (pArg) {
  833. ++pArg;
  834. g_dwSeverity = (DWORD)wcstoul(pArg, &pEnd, 0);
  835. }
  836. }
  837. break;
  838. case L'T':
  839. if (!_wcsnicmp(pArg, wszTagID, CHARCOUNT(wszTagID)-1)) {
  840. pArg = wcschr(pArg, L':');
  841. if (pArg) {
  842. ++pArg;
  843. g_dwTagID = (DWORD)wcstoul(pArg, &pEnd, 0);
  844. }
  845. }
  846. break;
  847. case L'G':
  848. if (!_wcsnicmp(pArg, wszGUID, CHARCOUNT(wszGUID)-1)) {
  849. if ((pArg = wcschr(pArg, L':')) != NULL) {
  850. ++pArg;
  851. g_pszGuid = pArg;
  852. }
  853. }
  854. break;
  855. case L'O':
  856. if (!_wcsnicmp(pArg, wszOfflineContent, CHARCOUNT(wszOfflineContent)-1)) {
  857. g_bShowOnlyOfflineContent = TRUE;
  858. }
  859. break;
  860. case L'P':
  861. if (!_wcsnicmp(pArg, wszPreserveChoice, CHARCOUNT(wszPreserveChoice)-1)) {
  862. g_bPreserveChoice = TRUE;
  863. }
  864. break;
  865. default:
  866. // unrecognized switch
  867. DBGPRINT((sdlError, "ParseCommandLineArgs",
  868. "Unrecognized parameter %s\n", pArg));
  869. break;
  870. }
  871. } else {
  872. // not a switch
  873. if (*pArg == L'{') {
  874. g_pszGuid = pArg;
  875. } else {
  876. g_dwTagID = (DWORD)wcstoul(pArg, &pEnd, 0);
  877. }
  878. }
  879. }
  880. }
  881. INT_PTR
  882. FeedbackDlgProc(
  883. HWND hDlg,
  884. UINT uMsg,
  885. WPARAM wParam,
  886. LPARAM lParam
  887. )
  888. /*++
  889. Return: void.
  890. Desc: This is the dialog proc for the apphelp dialog.
  891. --*/
  892. {
  893. BOOL bReturn = TRUE;
  894. LPWSTR lpszExeName;
  895. lpszExeName = (LPWSTR)GetWindowLongPtrW(hDlg, DWLP_USER);
  896. switch (uMsg) {
  897. case WM_INITDIALOG:
  898. {
  899. HICON hIcon;
  900. lpszExeName = (LPWSTR)lParam;
  901. SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)lpszExeName);
  902. //
  903. // Show the app icon.
  904. //
  905. hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDD_ICON_TRASH));
  906. SetClassLongPtr(hDlg, GCLP_HICON, (LONG_PTR)hIcon);
  907. SendDlgItemMessage(hDlg, IDC_WORKED, BM_SETCHECK, BST_CHECKED, 0);
  908. }
  909. case WM_COMMAND:
  910. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  911. case IDOK:
  912. EndDialog(hDlg, IDOK);
  913. break;
  914. case IDCANCEL:
  915. EndDialog(hDlg, IDCANCEL);
  916. break;
  917. default:
  918. bReturn = FALSE;
  919. break;
  920. }
  921. break;
  922. default:
  923. bReturn = FALSE;
  924. break;
  925. }
  926. return bReturn;
  927. }
  928. void
  929. ShowFeedbackDialog(
  930. LPWSTR lpszAppName
  931. )
  932. {
  933. DialogBoxParamW(g_hInstance,
  934. MAKEINTRESOURCEW(DLG_FEEDBACK),
  935. NULL,
  936. (DLGPROC)FeedbackDlgProc,
  937. (LPARAM)lpszAppName);
  938. }
  939. extern "C" int APIENTRY
  940. wWinMain(
  941. HINSTANCE hInstance,
  942. HINSTANCE hPrevInstance,
  943. LPWSTR lpCmdLine,
  944. int nCmdShow
  945. )
  946. /*++
  947. Return: 1 if the app for which the apphelp is shown should run, 0 otherwise.
  948. Desc: The command line looks like this:
  949. apphelp.exe GUID tagID [USELOCALCHM USEHTMLHELP APPHELPPATH]
  950. Ex:
  951. apphelp.exe {243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E
  952. apphelp.exe {243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E 1 1 c:\temp
  953. --*/
  954. {
  955. int nReturn = 1; // we always default to running, if something goes wrong
  956. LPWSTR szCommandLine;
  957. LPWSTR* argv;
  958. int argc;
  959. UNICODE_STRING ustrGuid;
  960. GUID guidDB = { 0 };
  961. GUID guidExeID = { 0 };
  962. TAGID tiExe = TAGID_NULL;
  963. TAGID tiExeID = TAGID_NULL;
  964. TAGREF trExe = TAGREF_NULL;
  965. WCHAR wszDBPath[MAX_PATH];
  966. DWORD dwType = 0;
  967. APPHELP_DATA ApphelpData;
  968. WCHAR szDBPath[MAX_PATH];
  969. HSDB hSDB = NULL;
  970. PDB pdb = NULL;
  971. DWORD dwFlags = 0;
  972. BOOL bAppHelp = FALSE;
  973. BOOL bRunApp = FALSE;
  974. g_hInstance = hInstance;
  975. InitCommonControls();
  976. ZeroMemory(&ApphelpData, sizeof(ApphelpData));
  977. //
  978. // Note that this memory isn't freed because it will automatically
  979. // be freed on exit anyway, and there are a lot of exit cases from
  980. // this application.
  981. //
  982. szCommandLine = GetCommandLineW();
  983. argv = CommandLineToArgvW(szCommandLine, &argc);
  984. ParseCommandLineArgs(argc, argv);
  985. if (argc > 1) {
  986. if (_wcsicmp(L"feedback", argv[1]) == 0) {
  987. ShowFeedbackDialog(argc > 2 ? argv[2] : NULL);
  988. }
  989. }
  990. if (g_pszGuid == NULL) {
  991. DBGPRINT((sdlError, "AHUI!wWinMain",
  992. "GUID not provided\n"));
  993. goto out;
  994. }
  995. if (!(g_dwTagID ^ g_dwHtmlHelpID)) {
  996. DBGPRINT((sdlError, "AHUI!wWinMain",
  997. "Only TagID or HtmlHelpID should be provided\n"));
  998. goto out;
  999. }
  1000. RtlInitUnicodeString(&ustrGuid, g_pszGuid);
  1001. if (g_dwHtmlHelpID) {
  1002. //
  1003. // provided here: guid, severity and html help id along with app name
  1004. //
  1005. if (!NT_SUCCESS(RtlGUIDFromString(&ustrGuid, &guidExeID))) {
  1006. DBGPRINT((sdlError,
  1007. "Ahui!wWinMain",
  1008. "Error getting GUID from string %s\n", g_pszGuid));
  1009. goto out;
  1010. }
  1011. ApphelpData.dwSeverity = g_dwSeverity;
  1012. ApphelpData.dwHTMLHelpID = g_dwHtmlHelpID;
  1013. ApphelpData.szAppName = (LPWSTR)g_pAppName;
  1014. bAppHelp = TRUE;
  1015. dwType = SDB_DATABASE_MAIN_SHIM;
  1016. goto ProceedWithApphelp;
  1017. }
  1018. // non-htmlid case, guid is a database guid
  1019. // also dwTagID is specified
  1020. if (RtlGUIDFromString(&ustrGuid, &guidDB) != STATUS_SUCCESS) {
  1021. DBGPRINT((sdlError,
  1022. "AppHelp.exe!wWinMain",
  1023. "Error trying to convert GUID string %S.\n",
  1024. g_pszGuid));
  1025. goto out;
  1026. }
  1027. tiExe = (TAGID)g_dwTagID;
  1028. if (tiExe == TAGID_NULL) {
  1029. DBGPRINT((sdlError,
  1030. "AppHelp.exe!wWinMain",
  1031. "Error getting TAGID from param %S\n",
  1032. argv[2]));
  1033. goto out;
  1034. }
  1035. hSDB = SdbInitDatabase(0, NULL);
  1036. if (!hSDB) {
  1037. DBGPRINT((sdlError,
  1038. "AppHelp.exe!wWinMain",
  1039. "Error initing database context.\n"));
  1040. goto out;
  1041. }
  1042. pdb = SdbGetPDBFromGUID(hSDB, &guidDB);
  1043. if (!pdb) {
  1044. DWORD dwLen;
  1045. //
  1046. // It's not one of the main DBs, try it as a local.
  1047. //
  1048. dwLen = SdbResolveDatabase(&guidDB, &dwType, szDBPath, MAX_PATH);
  1049. if (!dwLen || dwLen > MAX_PATH) {
  1050. DBGPRINT((sdlError,
  1051. "AppHelp.exe!wWinMain",
  1052. "Error resolving database from GUID\n"));
  1053. goto out;
  1054. }
  1055. //
  1056. // We have many "main" databases -- we should limit the check
  1057. //
  1058. if (dwType != SDB_DATABASE_MAIN_SHIM && dwType != SDB_DATABASE_MAIN_TEST) {
  1059. SdbOpenLocalDatabase(hSDB, szDBPath);
  1060. }
  1061. pdb = SdbGetPDBFromGUID(hSDB, &guidDB);
  1062. if (!pdb) {
  1063. DBGPRINT((sdlError,
  1064. "AppHelp.exe!wWinMain",
  1065. "Error getting pdb from GUID.\n"));
  1066. goto out;
  1067. }
  1068. } else {
  1069. dwType |= SDB_DATABASE_MAIN; // we will use details from the main db
  1070. }
  1071. if (!SdbTagIDToTagRef(hSDB, pdb, tiExe, &trExe)) {
  1072. DBGPRINT((sdlError,
  1073. "AppHelp.exe!wWinMain",
  1074. "Error converting TAGID to TAGREF.\n"));
  1075. goto out;
  1076. }
  1077. tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);
  1078. if (tiExeID == TAGID_NULL) {
  1079. DBGPRINT((sdlError,
  1080. "AppHelp.exe!wWinMain",
  1081. "Error trying to find TAG_EXE_ID.\n"));
  1082. goto out;
  1083. }
  1084. if (!SdbReadBinaryTag(pdb,
  1085. tiExeID,
  1086. (PBYTE)&guidExeID,
  1087. sizeof(GUID))) {
  1088. DBGPRINT((sdlError,
  1089. "AppHelp.exe!wWinMain",
  1090. "Error trying to read TAG_EXE_ID.\n"));
  1091. goto out;
  1092. }
  1093. bAppHelp = SdbReadApphelpData(hSDB, trExe, &ApphelpData);
  1094. ProceedWithApphelp:
  1095. if (SdbIsNullGUID(&guidExeID) || !SdbGetEntryFlags(&guidExeID, &dwFlags)) {
  1096. dwFlags = 0;
  1097. }
  1098. if (bAppHelp) {
  1099. //
  1100. // Check whether the disable bit is set.
  1101. //
  1102. if (!(dwFlags & SHIMREG_DISABLE_APPHELP)) {
  1103. BOOL bNoUI;
  1104. //
  1105. // See whether the user has checked "Don't show this anymore" box before.
  1106. //
  1107. bNoUI = ((dwFlags & SHIMREG_APPHELP_NOUI) != 0);
  1108. if (!bNoUI) {
  1109. //
  1110. // checkwindowstation returns true when UI should be shown
  1111. //
  1112. bNoUI = !CheckWindowStation();
  1113. }
  1114. if (!bNoUI) {
  1115. bNoUI = !CheckUserToken();
  1116. }
  1117. if (bNoUI) {
  1118. DBGPRINT((sdlInfo,
  1119. "bCheckRunBadapp",
  1120. "NoUI flag is set, apphelp UI disabled for this app.\n"));
  1121. }
  1122. //
  1123. // depending on severity of the problem...
  1124. //
  1125. switch (ApphelpData.dwSeverity) {
  1126. case APPHELP_MINORPROBLEM:
  1127. case APPHELP_HARDBLOCK:
  1128. case APPHELP_NOBLOCK:
  1129. case APPHELP_REINSTALL:
  1130. if (bNoUI) {
  1131. bRunApp = (ApphelpData.dwSeverity != APPHELP_HARDBLOCK) && !(dwFlags & SHIMREG_APPHELP_CANCEL);
  1132. } else {
  1133. DWORD dwRet;
  1134. //
  1135. // Show the UI. This function returns -1 in case of error or one
  1136. // of the following values on success:
  1137. // IDOK | 0x8000 - "no ui" checked, run app
  1138. // IDCANCEL - do not run app
  1139. // IDOK - run app
  1140. ApphelpData.dwData = dwType; // we use custom data for database type
  1141. dwRet = ShowApphelp(&ApphelpData,
  1142. NULL,
  1143. (dwType & SDB_DATABASE_MAIN) ? NULL : SdbGetLocalPDB(hSDB));
  1144. if (dwRet != APPHELP_DIALOG_FAILED) {
  1145. //
  1146. // The UI was shown. See whether the user has
  1147. // checked the "no ui" box.
  1148. //
  1149. if (dwRet & 0x8000) {
  1150. //
  1151. // "no ui" box was checked. Save the appropriate bits
  1152. // in the registry.
  1153. //
  1154. dwFlags |= SHIMREG_APPHELP_NOUI;
  1155. if ((dwRet & 0x0FFF) != IDOK) {
  1156. dwFlags |= SHIMREG_APPHELP_CANCEL; // we will not be hitting this path unless g_bPreserveChoice is enabled
  1157. }
  1158. if (!SdbIsNullGUID(&guidExeID)) {
  1159. SdbSetEntryFlags(&guidExeID, dwFlags);
  1160. }
  1161. }
  1162. //
  1163. // Check whether the user has chosen to run the app.
  1164. //
  1165. bRunApp = ((dwRet & 0x0FFF) == IDOK);
  1166. } else {
  1167. //
  1168. // The UI was not shown (some error prevented that).
  1169. // If the app is not "Hardblock" run it anyway.
  1170. //
  1171. bRunApp = (APPHELP_HARDBLOCK != ApphelpData.dwSeverity);
  1172. }
  1173. }
  1174. break;
  1175. }
  1176. }
  1177. }
  1178. if (!bRunApp) {
  1179. nReturn = 0;
  1180. }
  1181. out:
  1182. return nReturn;
  1183. }