Leaked source code of windows server 2003
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.

1782 lines
50 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. #ifndef _WIN64
  38. #include <wow64t.h>
  39. #endif
  40. #include "ahmsg.h"
  41. #include "strsafe.h"
  42. extern "C" VOID AllowForegroundActivation(VOID);
  43. #define APPHELP_DIALOG_FAILED ((DWORD)-1)
  44. //
  45. // TODO: add parameters to apphelp.exe's command line to work with these
  46. // variables.
  47. //
  48. BOOL g_bShowOnlyOfflineContent = FALSE;
  49. BOOL g_bUseHtmlHelp = FALSE;
  50. BOOL g_bMSI = FALSE;
  51. USHORT g_uExeImageType= DEFAULT_IMAGE;
  52. WCHAR g_wszApphelpPath[MAX_PATH];
  53. HFONT g_hFontBold = NULL;
  54. HINSTANCE g_hInstance;
  55. //
  56. // Global variables used while parsing args
  57. //
  58. DWORD g_dwHtmlHelpID;
  59. DWORD g_dwTagID;
  60. DWORD g_dwSeverity;
  61. LPCWSTR g_pAppName;
  62. LPCWSTR g_pszGuid;
  63. BOOL g_bPreserveChoice;
  64. WCHAR wszHtmlHelpID[] = L"HtmlHelpID";
  65. WCHAR wszAppName[] = L"AppName";
  66. WCHAR wszSeverity[] = L"Severity";
  67. WCHAR wszGUID[] = L"GUID";
  68. WCHAR wszTagID[] = L"TagID";
  69. WCHAR wszOfflineContent[] = L"OfflineContent";
  70. WCHAR wszPreserveChoice[] = L"PreserveChoice";
  71. WCHAR wszMSI[] = L"MSI";
  72. WCHAR wszPlatform[] = L"Platform";
  73. WCHAR wszX86[] = L"X86";
  74. WCHAR wszIA64[] = L"IA64";
  75. //
  76. // FORWARD DECLARATIONS OF FUNCTIONS
  77. DWORD
  78. ShowApphelpDialog(
  79. IN PAPPHELP_DATA pApphelpData
  80. );
  81. PSID
  82. GetCurrentUserSID(void)
  83. {
  84. HANDLE hProcessToken = INVALID_HANDLE_VALUE;
  85. BOOL bRet = FALSE;
  86. DWORD dwTokenLength = 0;
  87. DWORD dwReturnLength = 0;
  88. DWORD dwSIDLength = 0;
  89. PTOKEN_USER pTokenUser = NULL;
  90. BOOL bSuccess = FALSE;
  91. PSID pSID = NULL;
  92. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hProcessToken)) {
  93. goto out;
  94. }
  95. GetTokenInformation(hProcessToken, TokenUser, NULL, dwTokenLength, &dwReturnLength);
  96. if (dwReturnLength == 0) {
  97. goto out;
  98. }
  99. dwTokenLength = dwReturnLength;
  100. dwReturnLength = 0;
  101. pTokenUser = (PTOKEN_USER)malloc(dwTokenLength);
  102. if (!pTokenUser) {
  103. goto out;
  104. }
  105. if (!GetTokenInformation(hProcessToken, TokenUser, (LPVOID)pTokenUser, dwTokenLength, &dwReturnLength)) {
  106. goto out;
  107. }
  108. if (!IsValidSid(pTokenUser->User.Sid)) {
  109. goto out;
  110. }
  111. dwSIDLength = GetLengthSid(pTokenUser->User.Sid);
  112. pSID = malloc(dwSIDLength);
  113. if (!pSID) {
  114. goto out;
  115. }
  116. if (!CopySid(dwSIDLength, pSID, pTokenUser->User.Sid)) {
  117. goto out;
  118. }
  119. bSuccess = TRUE;
  120. out:
  121. if (pTokenUser) {
  122. free(pTokenUser);
  123. }
  124. if (hProcessToken != INVALID_HANDLE_VALUE) {
  125. CloseHandle(hProcessToken);
  126. }
  127. if (!bSuccess && pSID) {
  128. free(pSID);
  129. pSID = NULL;
  130. }
  131. return pSID;
  132. }
  133. void
  134. DeleteCurrentUserSID(PSID pSID)
  135. {
  136. if (pSID) {
  137. free(pSID);
  138. }
  139. }
  140. BOOL
  141. AppHelpLogEnabled(
  142. void
  143. )
  144. {
  145. HKEY hKey;
  146. LONG lResult;
  147. DWORD dwValue, dwSize = sizeof(dwValue);
  148. DWORD dwType;
  149. // First, check for a policy.
  150. lResult = RegOpenKeyExW (HKEY_LOCAL_MACHINE, POLICY_KEY_APPCOMPAT_W, 0,
  151. KEY_READ, &hKey);
  152. if (lResult == ERROR_SUCCESS) {
  153. dwValue = 0;
  154. lResult = RegQueryValueExW (hKey, POLICY_VALUE_APPHELP_LOG_W, 0, &dwType,
  155. (LPBYTE) &dwValue, &dwSize);
  156. RegCloseKey (hKey);
  157. }
  158. // Exit if a policy value was found.
  159. if (lResult == ERROR_SUCCESS && dwValue != 0) {
  160. return TRUE;
  161. }
  162. return FALSE;
  163. }
  164. VOID
  165. LogAppHelpEvent(
  166. APPHELP_DATA *pApphelpData
  167. )
  168. {
  169. if (!AppHelpLogEnabled()) {
  170. return;
  171. }
  172. LPCWSTR apszMessage[1];
  173. HANDLE hEventLog = RegisterEventSourceW(NULL, L"apphelp");
  174. if (!hEventLog) {
  175. DWORD dwErr = GetLastError();
  176. } else {
  177. PSID pSID = GetCurrentUserSID();
  178. apszMessage[0] = pApphelpData->szAppName;
  179. if (pApphelpData->dwSeverity == APPHELP_HARDBLOCK) {
  180. ReportEventW(hEventLog,
  181. EVENTLOG_INFORMATION_TYPE,
  182. 0,
  183. ID_APPHELP_BLOCK_TRIGGERED,
  184. pSID,
  185. 2,
  186. 0,
  187. apszMessage,
  188. NULL);
  189. } else {
  190. ReportEventW(hEventLog,
  191. EVENTLOG_INFORMATION_TYPE,
  192. 0,
  193. ID_APPHELP_TRIGGERED,
  194. pSID,
  195. 2,
  196. 0,
  197. apszMessage,
  198. NULL);
  199. }
  200. DeleteCurrentUserSID(pSID);
  201. DeregisterEventSource(hEventLog);
  202. }
  203. }
  204. DWORD
  205. ShowApphelp(
  206. IN PAPPHELP_DATA pApphelpData,
  207. IN LPCWSTR pwszDetailsDatabasePath,
  208. IN PDB pdbDetails
  209. )
  210. /*++
  211. Return: The return value can be one of the following based on what the user has
  212. selected:
  213. -1 - failed to show the info
  214. IDOK | 0x8000 - "no ui" checked, run the app
  215. IDCANCEL - do not run the app
  216. IDOK - run the app
  217. Desc: Open the details database, collect the details info and then show it.
  218. --*/
  219. {
  220. DWORD dwRet = APPHELP_DIALOG_FAILED;
  221. BOOL bCloseDetails = FALSE;
  222. if (pdbDetails == NULL) {
  223. //
  224. // Open the database containing the details info, if one wasn't passed in.
  225. //
  226. pdbDetails = SdbOpenApphelpDetailsDatabase(pwszDetailsDatabasePath);
  227. bCloseDetails = TRUE;
  228. if (pdbDetails == NULL) {
  229. DBGPRINT((sdlError, "ShowApphelp", "Failed to open the details database.\n"));
  230. goto Done;
  231. }
  232. }
  233. //
  234. // Read apphelp details data.
  235. //
  236. if (!SdbReadApphelpDetailsData(pdbDetails, pApphelpData)) {
  237. DBGPRINT((sdlError, "ShowApphelp", "Failed to read apphelp details.\n"));
  238. goto Done;
  239. }
  240. //
  241. // log an event, if necessary
  242. //
  243. LogAppHelpEvent(pApphelpData);
  244. //
  245. // Show the dialog box. The return values can be:
  246. // -1 - error
  247. // IDOK | 0x8000 - "no ui" checked, run the app
  248. // IDCANCEL - do not run the app
  249. // IDOK - run the app
  250. //
  251. dwRet = ShowApphelpDialog(pApphelpData);
  252. if (dwRet == APPHELP_DIALOG_FAILED) {
  253. DBGPRINT((sdlError, "ShowApphelp", "Failed to show the apphelp info.\n"));
  254. }
  255. Done:
  256. if (pdbDetails != NULL && bCloseDetails) {
  257. SdbCloseDatabase(pdbDetails);
  258. }
  259. return dwRet;
  260. }
  261. void
  262. FixEditControlScrollBar(
  263. IN HWND hDlg,
  264. IN int nCtrlId
  265. )
  266. /*++
  267. Return: void.
  268. Desc: This function tricks the edit control to not show the vertical scrollbar
  269. unless absolutely necessary.
  270. --*/
  271. {
  272. HFONT hFont = NULL;
  273. HFONT hFontOld = NULL;
  274. HDC hDC = NULL;
  275. TEXTMETRICW tm;
  276. RECT rc;
  277. int nVisibleLines = 0;
  278. int nLines;
  279. DWORD dwStyle;
  280. HWND hCtl;
  281. //
  282. // Get the edit control's rectangle.
  283. //
  284. SendDlgItemMessageW(hDlg, nCtrlId, EM_GETRECT, 0, (LPARAM)&rc);
  285. //
  286. // Retrieve the number of lines.
  287. //
  288. nLines = (int)SendDlgItemMessageW(hDlg, nCtrlId, EM_GETLINECOUNT, 0, 0);
  289. //
  290. // Calculate how many lines will fit.
  291. //
  292. hFont = (HFONT)SendDlgItemMessageW(hDlg, nCtrlId, WM_GETFONT, 0, 0);
  293. if (hFont != NULL) {
  294. hDC = CreateCompatibleDC(NULL);
  295. if (hDC != NULL) {
  296. hFontOld = (HFONT)SelectObject(hDC, hFont);
  297. //
  298. // Now get the metrics
  299. //
  300. if (GetTextMetricsW(hDC, &tm)) {
  301. nVisibleLines = (rc.bottom - rc.top) / tm.tmHeight;
  302. }
  303. SelectObject(hDC, hFontOld);
  304. DeleteDC(hDC);
  305. }
  306. }
  307. if (nVisibleLines && nVisibleLines >= nLines) {
  308. hCtl = GetDlgItem(hDlg, nCtrlId);
  309. dwStyle = (DWORD)GetWindowLongPtrW(hCtl, GWL_STYLE);
  310. SetWindowLongPtrW(hCtl, GWL_STYLE, (LONG)(dwStyle & ~WS_VSCROLL));
  311. SetWindowPos(hCtl,
  312. NULL,
  313. 0,
  314. 0,
  315. 0,
  316. 0,
  317. SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
  318. }
  319. }
  320. BOOL
  321. ShowApphelpHtmlHelp(
  322. HWND hDlg,
  323. PAPPHELP_DATA pApphelpData
  324. )
  325. /*++
  326. Return: TRUE on success, FALSE otherwise.
  327. Desc: Shows html help using hhctrl.ocx
  328. --*/
  329. {
  330. WCHAR szAppHelpURL[2048];
  331. WCHAR szWindowsDir[MAX_PATH];
  332. WCHAR szChmFile[MAX_PATH];
  333. WCHAR szChmURL[1024];
  334. HINSTANCE hInst = NULL;
  335. UINT nChars;
  336. int nChURL, nch;
  337. HRESULT hr;
  338. DWORD cch;
  339. LPWSTR lpwszUnescaped = NULL;
  340. BOOL bSuccess = FALSE;
  341. BOOL bCustom = FALSE;
  342. LCID lcid;
  343. size_t nLen;
  344. BOOL bFound = FALSE;
  345. bCustom = !(pApphelpData->dwData & SDB_DATABASE_MAIN);
  346. // apphelp is not in the main database, then it's custom apphelp
  347. nChars = GetSystemWindowsDirectoryW(szWindowsDir,
  348. CHARCOUNT(szWindowsDir));
  349. if (!nChars || nChars > CHARCOUNT(szWindowsDir)) {
  350. DBGPRINT((sdlError, "ShowApphelpHtmlHelp",
  351. "Error trying to retrieve Windows Directory %d.\n", GetLastError()));
  352. goto errHandle;
  353. }
  354. lcid = GetUserDefaultUILanguage();
  355. if (lcid != MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)) {
  356. StringCchPrintfExW(szChmFile,
  357. CHARCOUNT(szChmFile),
  358. NULL,
  359. &nLen,
  360. 0,
  361. L"%s\\Help\\MUI\\%04x\\apps.chm",
  362. szWindowsDir,
  363. lcid);
  364. if (nLen > 0) {
  365. bFound = RtlDoesFileExists_U(szChmFile);
  366. }
  367. }
  368. if (!bFound) {
  369. StringCchPrintfW(szChmFile, CHARCOUNT(szChmFile), L"%s\\Help\\apps.chm", szWindowsDir);
  370. }
  371. if (bCustom) {
  372. //
  373. // this is a custom DB, and therefore the URL in it should be taken
  374. // as-is, without using the MS redirector
  375. //
  376. StringCchCopyW(szAppHelpURL, CHARCOUNT(szAppHelpURL), pApphelpData->szURL);
  377. } else {
  378. StringCchPrintfW(szAppHelpURL,
  379. CHARCOUNT(szAppHelpURL),
  380. L"hcp://services/redirect?online=");
  381. nChURL = wcslen(szAppHelpURL);
  382. //
  383. // When we are compiling retail we check for the offline content as well.
  384. //
  385. if (!g_bShowOnlyOfflineContent) {
  386. //
  387. // First thing, unescape url
  388. //
  389. if (pApphelpData->szURL != NULL) {
  390. //
  391. // Unescape the url first, using shell.
  392. //
  393. cch = wcslen(pApphelpData->szURL) + 1;
  394. lpwszUnescaped = (LPWSTR)malloc(cch * sizeof(WCHAR));
  395. if (lpwszUnescaped == NULL) {
  396. DBGPRINT((sdlError,
  397. "ShowApphelpHtmlHelp",
  398. "Error trying to allocate memory for \"%S\"\n",
  399. pApphelpData->szURL));
  400. goto errHandle;
  401. }
  402. //
  403. // Unescape round 1 - use the shell function (same as used to encode
  404. // it for xml/database).
  405. //
  406. hr = UrlUnescapeW(pApphelpData->szURL, lpwszUnescaped, &cch, 0);
  407. if (!SUCCEEDED(hr)) {
  408. DBGPRINT((sdlError,
  409. "ShowApphelpHtmlHelp",
  410. "UrlUnescapeW failed on \"%S\"\n",
  411. pApphelpData->szURL));
  412. goto errHandle;
  413. }
  414. //
  415. // Unescape round 2 - use our function borrowed from help center
  416. //
  417. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  418. if (!SdbEscapeApphelpURL(szAppHelpURL + nChURL, &cch, lpwszUnescaped)) {
  419. DBGPRINT((sdlError,
  420. "ShowApphelpHtmlHelp",
  421. "Error escaping URL \"%S\"\n",
  422. lpwszUnescaped));
  423. goto errHandle;
  424. }
  425. nChURL += (int)cch;
  426. }
  427. }
  428. //
  429. // At this point szAppHelpURL contains redirected URL for online usage
  430. // for custom db szAppHelpURL contains full URL
  431. //
  432. // If Apphelp file is provided -- use it
  433. //
  434. if (*g_wszApphelpPath) {
  435. StringCchPrintfW(szChmURL,
  436. CHARCOUNT(szChmURL),
  437. L"mk:@msitstore:%ls::/idh_w2_%d.htm",
  438. g_wszApphelpPath,
  439. pApphelpData->dwHTMLHelpID);
  440. } else {
  441. StringCchPrintfW(szChmURL,
  442. CHARCOUNT(szChmURL),
  443. L"mk:@msitstore:%ls::/idh_w2_%d.htm",
  444. szChmFile,
  445. pApphelpData->dwHTMLHelpID);
  446. }
  447. //
  448. // at this point szChmURL contains a URL pointing to the offline help file
  449. // we put it into the szAppHelpURL for both online and offline case
  450. //
  451. if (g_bShowOnlyOfflineContent) {
  452. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  453. if (g_bUseHtmlHelp) {
  454. hr = UrlEscapeW(szChmURL, szAppHelpURL + nChURL, &cch, 0);
  455. if (SUCCEEDED(hr)) {
  456. nChURL += (INT)cch;
  457. }
  458. } else {
  459. if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
  460. DBGPRINT((sdlError, "ShowApphelpHtmlHelp", "Error escaping URL \"%S\"\n", szChmURL));
  461. goto errHandle;
  462. }
  463. nChURL += (int)cch;
  464. }
  465. }
  466. //
  467. // now offline sequence
  468. //
  469. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  470. StringCchPrintfW(szAppHelpURL + nChURL, cch, L"&offline=");
  471. nch = wcslen(szAppHelpURL + nChURL);
  472. nChURL += nch;
  473. cch = (DWORD)(CHARCOUNT(szAppHelpURL) - nChURL);
  474. if (!SdbEscapeApphelpURL(szAppHelpURL+nChURL, &cch, szChmURL)) {
  475. DBGPRINT((sdlError, "ShowApphelpHtmlHelp", "Error escaping URL \"%S\"\n", szChmURL));
  476. goto errHandle;
  477. }
  478. nChURL += (int)cch;
  479. *(szAppHelpURL + nChURL) = L'\0';
  480. }
  481. //
  482. // WARNING: On Whistler execution of the following line will cause
  483. // an AV (it works properly on Win2k) when it's executed twice
  484. // from the same process. We should be able to just call
  485. // shell with szAppHelpURL but we can't.
  486. // So for now, use hh.exe as the stub.
  487. //
  488. // right before we do ShellExecute -- set current directory to windows dir
  489. //
  490. if (g_bUseHtmlHelp && !bCustom) {
  491. WCHAR szHHPath[MAX_PATH];
  492. DBGPRINT((sdlInfo, "ShowApphelpHtmlHelp", "Opening Apphelp URL \"%S\"\n", szChmURL));
  493. StringCchCopyW(szHHPath, ARRAYSIZE(szHHPath), szWindowsDir);
  494. StringCchCatW(szHHPath, ARRAYSIZE(szHHPath), L"\\hh.exe");
  495. hInst = ShellExecuteW(hDlg, L"open", szHHPath, szChmURL, NULL, SW_SHOWNORMAL);
  496. } else if (!bCustom) {
  497. WCHAR szHSCPath[MAX_PATH];
  498. WCHAR* pszParameters;
  499. size_t cchUrl = ARRAYSIZE(szAppHelpURL);
  500. static WCHAR szUrlPrefix[] = L"-url ";
  501. StringCchCopyW(szHSCPath, ARRAYSIZE(szHSCPath), szWindowsDir);
  502. StringCchCatW (szHSCPath, ARRAYSIZE(szHSCPath), L"\\pchealth\\helpctr\\binaries\\helpctr.exe");
  503. // format for parameters is " -url <our url>"
  504. StringCchLengthW(szAppHelpURL, ARRAYSIZE(szAppHelpURL), &cchUrl);
  505. cchUrl += CHARCOUNT(szUrlPrefix) + 1;
  506. pszParameters = new WCHAR[cchUrl];
  507. if (pszParameters == NULL) {
  508. bSuccess = FALSE;
  509. goto errHandle;
  510. }
  511. StringCchCopyW(pszParameters, cchUrl, szUrlPrefix);
  512. StringCchCatW (pszParameters, cchUrl, szAppHelpURL);
  513. DBGPRINT((sdlInfo,
  514. "ShowApphelpHtmlHelp",
  515. "Opening APPHELP URL \"%S\"\n",
  516. szAppHelpURL));
  517. hInst = ShellExecuteW(hDlg, L"open", szHSCPath, pszParameters, NULL, SW_SHOWNORMAL);
  518. delete[] pszParameters;
  519. } else {
  520. DBGPRINT((sdlInfo,
  521. "ShowApphelpHtmlHelp",
  522. "Opening Custom APPHELP URL \"%S\"\n",
  523. szAppHelpURL));
  524. hInst = ShellExecuteW(hDlg, L"open", szAppHelpURL, NULL, NULL, SW_SHOWNORMAL);
  525. }
  526. if (HandleToUlong(hInst) <= 32) {
  527. DBGPRINT((sdlError,
  528. "ShowApphelpHtmlHelp",
  529. "Error 0x%p trying to show help topic \"%ls\"\n",
  530. hInst,
  531. szAppHelpURL));
  532. }
  533. //
  534. // If we unload html help now we'll get weird and unpredictable behavior!
  535. // So don't do it :-(
  536. //
  537. bSuccess = (HandleToUlong(hInst) > 32);
  538. errHandle:
  539. if (lpwszUnescaped != NULL) {
  540. free(lpwszUnescaped);
  541. }
  542. return bSuccess;
  543. }
  544. INT_PTR CALLBACK
  545. AppCompatDlgProc(
  546. HWND hDlg,
  547. UINT uMsg,
  548. WPARAM wParam,
  549. LPARAM lParam
  550. )
  551. /*++
  552. Return: void.
  553. Desc: This is the dialog proc for the apphelp dialog.
  554. --*/
  555. {
  556. BOOL bReturn = TRUE;
  557. PAPPHELP_DATA pApphelpData;
  558. pApphelpData = (PAPPHELP_DATA)GetWindowLongPtrW(hDlg, DWLP_USER);
  559. switch (uMsg) {
  560. case WM_INITDIALOG:
  561. {
  562. WCHAR wszMessage[2048];
  563. DWORD dwResActionString;
  564. HFONT hFont;
  565. LOGFONTW LogFont;
  566. WCHAR* pwszAppTitle;
  567. INT nChars;
  568. DWORD dwDefID = IDD_DETAILS;
  569. DWORD dwDefBtn; // old default button id
  570. HICON hIcon;
  571. LPWSTR IconID = MAKEINTRESOURCEW(IDI_WARNING);
  572. pApphelpData = (PAPPHELP_DATA)lParam;
  573. SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)pApphelpData);
  574. //
  575. // Show the app icon.
  576. //
  577. hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDD_ICON_TRASH));
  578. SetClassLongPtr(hDlg, GCLP_HICON, (LONG_PTR)hIcon);
  579. mouse_event(MOUSEEVENTF_WHEEL, 0, 0, 0, 0);
  580. SetForegroundWindow(hDlg);
  581. pwszAppTitle = pApphelpData->szAppTitle;
  582. if (pwszAppTitle == NULL) {
  583. pwszAppTitle = pApphelpData->szAppName;
  584. }
  585. if (pwszAppTitle != NULL) {
  586. SetDlgItemTextW(hDlg, IDD_APPNAME, pwszAppTitle);
  587. //
  588. // Make sure that we only utilize the first line of that text
  589. // for the window title.
  590. //
  591. SetWindowTextW(hDlg, pwszAppTitle);
  592. }
  593. hFont = (HFONT)SendDlgItemMessageW(hDlg,
  594. IDD_APPNAME,
  595. WM_GETFONT,
  596. 0, 0);
  597. if (hFont && GetObjectW(hFont, sizeof(LogFont), (LPVOID)&LogFont)) {
  598. LogFont.lfWeight = FW_BOLD;
  599. hFont = CreateFontIndirectW(&LogFont);
  600. if (hFont != NULL) {
  601. g_hFontBold = hFont;
  602. SendDlgItemMessageW(hDlg,
  603. IDD_APPNAME,
  604. WM_SETFONT,
  605. (WPARAM)hFont, TRUE);
  606. }
  607. }
  608. //
  609. // By default, we have both RUN AND CANCEL
  610. //
  611. dwResActionString = IDS_APPCOMPAT_RUNCANCEL;
  612. switch (pApphelpData->dwSeverity) {
  613. case APPHELP_HARDBLOCK:
  614. //
  615. // Disable run button and "don't show this again" box
  616. // Reset the "defpushbutton" style from this one...
  617. //
  618. EnableWindow(GetDlgItem(hDlg, IDD_STATE), FALSE);
  619. EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
  620. dwResActionString = IDS_APPCOMPAT_CANCEL;
  621. dwDefID = IDD_DETAILS; // set for hardblock case since RUN is not avail
  622. IconID = MAKEINTRESOURCEW(IDI_ERROR);
  623. break;
  624. case APPHELP_MINORPROBLEM:
  625. break;
  626. case APPHELP_NOBLOCK:
  627. break;
  628. case APPHELP_REINSTALL:
  629. break;
  630. }
  631. //
  632. // if we have no URL, or the URL begins with "null" gray out the "details" button
  633. //
  634. if (!pApphelpData->szURL || !pApphelpData->szURL[0] ||
  635. _wcsnicmp(pApphelpData->szURL, L"null", 4) == 0) {
  636. EnableWindow(GetDlgItem(hDlg, IDD_DETAILS), FALSE);
  637. }
  638. hIcon = LoadIconW(NULL, IconID);
  639. if (hIcon != NULL) {
  640. SendDlgItemMessageW(hDlg, IDD_ICON, STM_SETICON, (WPARAM)hIcon, 0);
  641. }
  642. //
  643. // Set the default push button
  644. // Reset the current default push button to a regular button.
  645. //
  646. // Update the default push button's control ID.
  647. //
  648. dwDefBtn = (DWORD)SendMessageW(hDlg, DM_GETDEFID, 0, 0);
  649. if (HIWORD(dwDefBtn) == DC_HASDEFID) {
  650. dwDefBtn = LOWORD(dwDefBtn);
  651. SendDlgItemMessageW(hDlg, dwDefBtn, BM_SETSTYLE, BS_PUSHBUTTON, TRUE);
  652. }
  653. SendDlgItemMessageW(hDlg, dwDefID, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
  654. SendMessageW(hDlg, DM_SETDEFID, (WPARAM)dwDefID, 0);
  655. //
  656. // now set the focus
  657. // be careful and do not mess with other focus-related messages, else use PostMessage here
  658. //
  659. SendMessageW(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, dwDefID), TRUE);
  660. //
  661. // If dwHTMLHelpID is not present disable "Details" button
  662. //
  663. if (!pApphelpData->dwHTMLHelpID) {
  664. EnableWindow(GetDlgItem(hDlg, IDD_DETAILS), FALSE);
  665. }
  666. wszMessage[0] = L'\0';
  667. LoadStringW(g_hInstance,
  668. dwResActionString,
  669. wszMessage,
  670. sizeof(wszMessage) / sizeof(WCHAR));
  671. SetDlgItemTextW(hDlg, IDD_LINE_2, wszMessage);
  672. SetDlgItemTextW(hDlg,
  673. IDD_APPHELP_DETAILS,
  674. pApphelpData->szDetails ? pApphelpData->szDetails : L"");
  675. FixEditControlScrollBar(hDlg, IDD_APPHELP_DETAILS);
  676. //
  677. // Return false so that the default focus-setting would not apply.
  678. //
  679. bReturn = FALSE;
  680. break;
  681. }
  682. case WM_DESTROY:
  683. //
  684. // perform cleanup - remove the font we've had created
  685. //
  686. if (g_hFontBold != NULL) {
  687. DeleteObject(g_hFontBold);
  688. g_hFontBold = NULL;
  689. }
  690. AllowForegroundActivation();
  691. PostQuitMessage(0); // we just bailed out
  692. break;
  693. case WM_COMMAND:
  694. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  695. case IDOK:
  696. //
  697. // Check the NO UI checkbox
  698. //
  699. EndDialog(hDlg, (INT_PTR)(IsDlgButtonChecked(hDlg, IDD_STATE) ? (IDOK | 0x8000) : IDOK));
  700. break;
  701. case IDCANCEL:
  702. EndDialog(hDlg, (INT_PTR)(IsDlgButtonChecked(hDlg, IDD_STATE) && g_bPreserveChoice ? (IDCANCEL | 0x8000) : IDCANCEL));
  703. break;
  704. case IDD_DETAILS:
  705. //
  706. // Launch details.
  707. //
  708. ShowApphelpHtmlHelp(hDlg, pApphelpData);
  709. break;
  710. default:
  711. bReturn = FALSE;
  712. break;
  713. }
  714. break;
  715. default:
  716. bReturn = FALSE;
  717. break;
  718. }
  719. return bReturn;
  720. }
  721. typedef NTSTATUS (NTAPI *PFNUSERTESTTOKENFORINTERACTIVE)(HANDLE Token, PLUID pluidCaller);
  722. PFNUSERTESTTOKENFORINTERACTIVE UserTestTokenForInteractive = NULL;
  723. BOOL
  724. CheckUserToken(
  725. )
  726. /*++
  727. returns TRUE if the apphelp should be shown
  728. FALSE if we should not present apphelp UI
  729. --*/
  730. {
  731. NTSTATUS Status;
  732. HANDLE hToken = NULL;
  733. LUID LuidUser;
  734. HMODULE hWinsrv = NULL;
  735. BOOL bShowUI = FALSE;
  736. UINT nChars;
  737. WCHAR szSystemDir[MAX_PATH];
  738. WCHAR szWinsrvDll[MAX_PATH];
  739. static
  740. WCHAR szWinsrvDllName[] = L"winsrv.dll";
  741. nChars = GetSystemDirectoryW(szSystemDir, CHARCOUNT(szSystemDir));
  742. if (nChars == 0 || nChars > CHARCOUNT(szSystemDir) - 2 - CHARCOUNT(szWinsrvDllName)) {
  743. DBGPRINT((sdlError, "CheckUserToken",
  744. "Error trying to get systemdirectory %d.\n", GetLastError()));
  745. *szSystemDir = L'\0';
  746. } else {
  747. szSystemDir[nChars] = L'\\';
  748. szSystemDir[nChars + 1] = L'\0';
  749. }
  750. StringCchCopyW(szWinsrvDll, ARRAYSIZE(szWinsrvDll), szSystemDir);
  751. StringCchCatW (szWinsrvDll, ARRAYSIZE(szWinsrvDll), szWinsrvDllName);
  752. hWinsrv = LoadLibraryW(szWinsrvDll);
  753. if (hWinsrv == NULL) {
  754. goto ErrHandle;
  755. }
  756. UserTestTokenForInteractive = (PFNUSERTESTTOKENFORINTERACTIVE)GetProcAddress(hWinsrv,
  757. "_UserTestTokenForInteractive");
  758. if (UserTestTokenForInteractive == NULL) {
  759. goto ErrHandle;
  760. }
  761. Status = NtOpenProcessToken(NtCurrentProcess(),
  762. TOKEN_QUERY,
  763. &hToken);
  764. if (NT_SUCCESS(Status)) {
  765. Status = UserTestTokenForInteractive(hToken, &LuidUser);
  766. NtClose(hToken);
  767. if (NT_SUCCESS(Status)) {
  768. bShowUI = TRUE;
  769. goto ErrHandle;
  770. }
  771. }
  772. Status = NtOpenThreadToken(NtCurrentThread(),
  773. TOKEN_QUERY,
  774. TRUE,
  775. &hToken);
  776. if (NT_SUCCESS(Status)) {
  777. Status = UserTestTokenForInteractive(hToken, &LuidUser);
  778. NtClose(hToken);
  779. if (NT_SUCCESS(Status)) {
  780. bShowUI = TRUE;
  781. goto ErrHandle;
  782. }
  783. }
  784. ErrHandle:
  785. if (hWinsrv) {
  786. FreeLibrary(hWinsrv);
  787. }
  788. return bShowUI;
  789. }
  790. BOOL
  791. CheckWindowStation(
  792. )
  793. /*++
  794. returns TRUE if the apphelp should be shown
  795. FALSE if we should not bother with apphelp UI
  796. --*/
  797. {
  798. HWINSTA hWindowStation;
  799. BOOL bShowUI = FALSE;
  800. DWORD dwLength = 0;
  801. DWORD dwBufferSize = 0;
  802. DWORD dwError;
  803. BOOL bSuccess;
  804. LPWSTR pwszWindowStation = NULL;
  805. hWindowStation = GetProcessWindowStation();
  806. if (hWindowStation == NULL) {
  807. DBGPRINT((sdlError,
  808. "ApphelpCheckWindowStation",
  809. "GetProcessWindowStation failed error 0x%lx\n", GetLastError()));
  810. goto ErrHandle; // the app is not a Windows NT/Windows 2000 app??? try to show UI
  811. }
  812. // get the information please
  813. bSuccess = GetUserObjectInformationW(hWindowStation, UOI_NAME, NULL, 0, &dwBufferSize);
  814. if (!bSuccess) {
  815. dwError = GetLastError();
  816. if (dwError != ERROR_INSUFFICIENT_BUFFER) {
  817. DBGPRINT((sdlError,
  818. "ApphelpCheckWindowStation",
  819. "GetUserObjectInformation failed error 0x%lx\n", dwError));
  820. goto ErrHandle;
  821. }
  822. pwszWindowStation = (LPWSTR)malloc(dwBufferSize);
  823. if (pwszWindowStation == NULL) {
  824. DBGPRINT((sdlError,
  825. "ApphelpCheckWindowStation",
  826. "Failed to allocate 0x%lx bytes for Window Station name\n", dwBufferSize));
  827. goto ErrHandle;
  828. }
  829. // ok, call again
  830. bSuccess = GetUserObjectInformationW(hWindowStation,
  831. UOI_NAME,
  832. pwszWindowStation,
  833. dwBufferSize,
  834. &dwLength);
  835. if (!bSuccess) {
  836. DBGPRINT((sdlError,
  837. "ApphelpCheckWindowStation",
  838. "GetUserObjectInformation failed error 0x%lx, buffer size 0x%lx returned 0x%lx\n",
  839. GetLastError(), dwBufferSize, dwLength));
  840. goto ErrHandle;
  841. }
  842. // now we have window station name, compare it to winsta0
  843. //
  844. bShowUI = (_wcsicmp(pwszWindowStation, L"Winsta0") == 0);
  845. if (!bShowUI) {
  846. DBGPRINT((sdlInfo,
  847. "ApphelpCheckWindowStation",
  848. "Apphelp UI will not be shown, running this process on window station \"%s\"\n",
  849. pwszWindowStation));
  850. }
  851. }
  852. //
  853. // presumably we will try and check the token for interactive access
  854. //
  855. ErrHandle:
  856. // should we do a close handle ???
  857. //
  858. if (hWindowStation != NULL) {
  859. CloseWindowStation(hWindowStation);
  860. }
  861. if (pwszWindowStation != NULL) {
  862. free(pwszWindowStation);
  863. }
  864. return bShowUI;
  865. }
  866. DWORD
  867. ShowApphelpDialog(
  868. IN PAPPHELP_DATA pApphelpData
  869. )
  870. /*++
  871. Return: (IDOK | IDCANCEL) | [0x8000]
  872. IDOK | 0x8000 - the user has chosen to run the app and
  873. checked "don't show me this anymore"
  874. IDOK - the user has chosen to run the app, dialog will be shown again
  875. IDCANCEL - the user has chosen not to run the app
  876. -1 - we have failed to import APIs necessary to show dlg box
  877. Desc: Shows the dialog box with apphelp info.
  878. --*/
  879. {
  880. BOOL bSuccess;
  881. INT_PTR retVal = 0;
  882. retVal = DialogBoxParamW(g_hInstance,
  883. MAKEINTRESOURCEW(DLG_APPCOMPAT),
  884. NULL,
  885. AppCompatDlgProc,
  886. (LPARAM)pApphelpData); // parameter happens to be a pointer of type PAPPHELP_DATA
  887. return (DWORD)retVal;
  888. }
  889. VOID
  890. ParseCommandLineArgs(
  891. int argc,
  892. WCHAR* argv[]
  893. )
  894. {
  895. WCHAR ch;
  896. WCHAR* pArg;
  897. WCHAR* pEnd;
  898. while (--argc) {
  899. pArg = argv[argc];
  900. if (*pArg == L'/' || *pArg == '-') {
  901. ch = *++pArg;
  902. switch(towupper(ch)) {
  903. case L'H':
  904. if (!_wcsnicmp(pArg, wszHtmlHelpID, CHARCOUNT(wszHtmlHelpID)-1)) {
  905. pArg = wcschr(pArg, L':');
  906. if (pArg) {
  907. ++pArg; // skip over :
  908. g_dwHtmlHelpID = (DWORD)wcstoul(pArg, &pEnd, 0);
  909. }
  910. }
  911. break;
  912. case L'A':
  913. if (!_wcsnicmp(pArg, wszAppName, CHARCOUNT(wszAppName)-1)) {
  914. pArg = wcschr(pArg, L':');
  915. if (pArg) {
  916. ++pArg;
  917. g_pAppName = pArg; // this is app name, remove the quotes
  918. }
  919. }
  920. break;
  921. case L'S':
  922. if (!_wcsnicmp(pArg, wszSeverity, CHARCOUNT(wszSeverity)-1)) {
  923. pArg = wcschr(pArg, L':');
  924. if (pArg) {
  925. ++pArg;
  926. g_dwSeverity = (DWORD)wcstoul(pArg, &pEnd, 0);
  927. }
  928. }
  929. break;
  930. case L'T':
  931. if (!_wcsnicmp(pArg, wszTagID, CHARCOUNT(wszTagID)-1)) {
  932. pArg = wcschr(pArg, L':');
  933. if (pArg) {
  934. ++pArg;
  935. g_dwTagID = (DWORD)wcstoul(pArg, &pEnd, 0);
  936. }
  937. }
  938. break;
  939. case L'G':
  940. if (!_wcsnicmp(pArg, wszGUID, CHARCOUNT(wszGUID)-1)) {
  941. if ((pArg = wcschr(pArg, L':')) != NULL) {
  942. ++pArg;
  943. g_pszGuid = pArg;
  944. }
  945. }
  946. break;
  947. case L'O':
  948. if (!_wcsnicmp(pArg, wszOfflineContent, CHARCOUNT(wszOfflineContent)-1)) {
  949. g_bShowOnlyOfflineContent = TRUE;
  950. }
  951. break;
  952. case L'P':
  953. if (!_wcsnicmp(pArg, wszPreserveChoice, CHARCOUNT(wszPreserveChoice)-1)) {
  954. g_bPreserveChoice = TRUE;
  955. } else if (!_wcsnicmp(pArg, wszPlatform, CHARCOUNT(wszPlatform) - 1)) {
  956. if ((pArg = wcschr(pArg, L':')) != NULL) {
  957. ++pArg;
  958. //
  959. // identify each of the supported platforms
  960. //
  961. if (!_wcsnicmp(pArg, wszX86, CHARCOUNT(wszX86) - 1)) {
  962. g_uExeImageType = IMAGE_FILE_MACHINE_I386;
  963. } else if (!_wcsnicmp(pArg, wszIA64, CHARCOUNT(wszIA64) - 1)) {
  964. g_uExeImageType = IMAGE_FILE_MACHINE_IA64;
  965. }
  966. }
  967. }
  968. break;
  969. case L'M':
  970. if (!_wcsnicmp(pArg, wszMSI, CHARCOUNT(wszMSI))) {
  971. g_bMSI = TRUE;
  972. }
  973. break;
  974. default:
  975. // unrecognized switch
  976. DBGPRINT((sdlError, "ParseCommandLineArgs",
  977. "Unrecognized parameter %s\n", pArg));
  978. break;
  979. }
  980. } else {
  981. // not a switch
  982. if (*pArg == L'{') {
  983. g_pszGuid = pArg;
  984. } else {
  985. g_dwTagID = (DWORD)wcstoul(pArg, &pEnd, 0);
  986. }
  987. }
  988. }
  989. }
  990. INT_PTR CALLBACK
  991. FeedbackDlgProc(
  992. HWND hDlg,
  993. UINT uMsg,
  994. WPARAM wParam,
  995. LPARAM lParam
  996. )
  997. /*++
  998. Return: void.
  999. Desc: This is the dialog proc for the apphelp dialog.
  1000. --*/
  1001. {
  1002. BOOL bReturn = TRUE;
  1003. LPWSTR lpszExeName;
  1004. lpszExeName = (LPWSTR)GetWindowLongPtrW(hDlg, DWLP_USER);
  1005. switch (uMsg) {
  1006. case WM_INITDIALOG:
  1007. {
  1008. HICON hIcon;
  1009. lpszExeName = (LPWSTR)lParam;
  1010. SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)lpszExeName);
  1011. //
  1012. // Show the app icon.
  1013. //
  1014. hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDD_ICON_TRASH));
  1015. SetClassLongPtr(hDlg, GCLP_HICON, (LONG_PTR)hIcon);
  1016. SendDlgItemMessage(hDlg, IDC_WORKED, BM_SETCHECK, BST_CHECKED, 0);
  1017. }
  1018. case WM_COMMAND:
  1019. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  1020. case IDOK:
  1021. EndDialog(hDlg, IDOK);
  1022. break;
  1023. case IDCANCEL:
  1024. EndDialog(hDlg, IDCANCEL);
  1025. break;
  1026. default:
  1027. bReturn = FALSE;
  1028. break;
  1029. }
  1030. break;
  1031. default:
  1032. bReturn = FALSE;
  1033. break;
  1034. }
  1035. return bReturn;
  1036. }
  1037. void
  1038. ShowFeedbackDialog(
  1039. LPWSTR lpszAppName
  1040. )
  1041. {
  1042. DialogBoxParamW(g_hInstance,
  1043. MAKEINTRESOURCEW(DLG_FEEDBACK),
  1044. NULL,
  1045. FeedbackDlgProc,
  1046. (LPARAM)lpszAppName);
  1047. }
  1048. BOOL
  1049. CheckWOW64Redirection(
  1050. int* pReturnCode
  1051. )
  1052. /*++
  1053. Return: TRUE if redirected
  1054. FALSE if no redirection is needed
  1055. Desc: Checks whether we're running on wow64, if so - redirects to 64-bit ahui.exe
  1056. --*/
  1057. {
  1058. LPWSTR pszCommandLine;
  1059. UINT nChars;
  1060. WCHAR szWindowsDir[MAX_PATH];
  1061. WCHAR szAhuiExePath[MAX_PATH];
  1062. STARTUPINFOW StartupInfo = { 0 };
  1063. PROCESS_INFORMATION ProcessInfo = { 0 };
  1064. static
  1065. WCHAR szPlatformX86[] = L"/Platform:X86";
  1066. WCHAR* pszAhuiCmdLine = NULL;
  1067. DWORD dwExit = 0;
  1068. BOOL bWow64 = FALSE;
  1069. BOOL bReturn = FALSE;
  1070. BOOL bRedirect = FALSE;
  1071. WCHAR** argv = NULL;
  1072. int nArg, argc;
  1073. size_t cchCmd;
  1074. size_t cchLeft;
  1075. WCHAR* pchCur;
  1076. #ifndef _WIN64
  1077. if (IsWow64Process(GetCurrentProcess(), &bWow64)) {
  1078. if (!bWow64) {
  1079. return FALSE;
  1080. }
  1081. }
  1082. // if a check for wow64 failes, we do not redirect and will try to check
  1083. // for interactive token. That test will fail and we won't show any UI,
  1084. // which is better than the alternative (getting a service stuck on a
  1085. // dialog which is invisible
  1086. #endif // _WIN64
  1087. //
  1088. // we are running in the context of a wow64 process, redirect to native 64-bit ahui.exe
  1089. //
  1090. nChars = GetSystemWindowsDirectoryW(szWindowsDir,
  1091. CHARCOUNT(szWindowsDir));
  1092. if (!nChars || nChars > CHARCOUNT(szWindowsDir) - 1) {
  1093. DBGPRINT((sdlError, "CheckWow64Redirection",
  1094. "Error trying to retrieve Windows Directory %d.\n", GetLastError()));
  1095. goto out;
  1096. }
  1097. StringCchPrintfW(szAhuiExePath, CHARCOUNT(szAhuiExePath), L"%s\\system32\\ahui.exe", szWindowsDir);
  1098. #ifndef _WIN64
  1099. Wow64DisableFilesystemRedirector(szAhuiExePath);
  1100. bRedirect = TRUE;
  1101. #endif
  1102. pszCommandLine = GetCommandLineW();
  1103. if (pszCommandLine == NULL) {
  1104. goto out;
  1105. }
  1106. argv = CommandLineToArgvW(pszCommandLine, &argc);
  1107. if (argv != NULL) {
  1108. for (cchCmd = 0, nArg = 1; nArg < argc; ++nArg) {
  1109. cchCmd += wcslen(argv[nArg]) + 2 + 1; // 2 for " " and 1 for space
  1110. }
  1111. }
  1112. // now allocate space for command line, ahui exe path and additional parameter (/Platform:x86)
  1113. nChars = wcslen(szAhuiExePath) + cchCmd + CHARCOUNT(szPlatformX86) + 3;
  1114. pszAhuiCmdLine = new WCHAR[nChars];
  1115. if (pszAhuiCmdLine == NULL) {
  1116. goto out;
  1117. }
  1118. pchCur = pszAhuiCmdLine;
  1119. cchLeft = nChars;
  1120. StringCchPrintfExW(pszAhuiCmdLine, nChars, &pchCur, &cchLeft, 0, L"%s ", szAhuiExePath);
  1121. if (argv != NULL) {
  1122. for (nArg = 1; nArg < argc; ++nArg) {
  1123. //
  1124. // check for platform parameter, if it exists -- skip it
  1125. //
  1126. if ((*argv[nArg] == L'/' || *argv[nArg] == L'-') &&
  1127. _wcsnicmp(argv[nArg] + 1, wszPlatform, CHARCOUNT(wszPlatform) - 1) == 0) {
  1128. continue;
  1129. }
  1130. StringCchPrintfExW(pchCur, cchLeft, &pchCur, &cchLeft, 0, L" \"%s\"", argv[nArg]);
  1131. }
  1132. }
  1133. StringCchPrintfExW(pchCur, cchLeft, &pchCur, &cchLeft, 0, L" %s", szPlatformX86);
  1134. StartupInfo.cb = sizeof(StartupInfo);
  1135. if (!CreateProcessW(szAhuiExePath,
  1136. pszAhuiCmdLine,
  1137. NULL,
  1138. NULL,
  1139. FALSE,
  1140. 0,
  1141. NULL,
  1142. NULL,
  1143. &StartupInfo, &ProcessInfo)) {
  1144. DBGPRINT((sdlError, "CheckWow64Redirection",
  1145. "Failed to launch apphelp process %d.\n", GetLastError()));
  1146. goto out;
  1147. }
  1148. WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  1149. bReturn = GetExitCodeProcess(ProcessInfo.hProcess, &dwExit);
  1150. if (bReturn) {
  1151. *pReturnCode = (int)dwExit;
  1152. bReturn = TRUE;
  1153. }
  1154. out:
  1155. if (pszAhuiCmdLine) {
  1156. delete[] pszAhuiCmdLine;
  1157. }
  1158. if (argv) {
  1159. HGLOBAL hMem = GlobalHandle(argv);
  1160. if (hMem != NULL) {
  1161. GlobalFree(hMem);
  1162. }
  1163. }
  1164. #ifndef _WIN64
  1165. if (bRedirect) {
  1166. Wow64EnableFilesystemRedirector();
  1167. }
  1168. #endif
  1169. return bReturn;
  1170. }
  1171. extern "C" int APIENTRY
  1172. wWinMain(
  1173. HINSTANCE hInstance,
  1174. HINSTANCE hPrevInstance,
  1175. LPWSTR lpCmdLine,
  1176. int nCmdShow
  1177. )
  1178. /*++
  1179. Return: 1 if the app for which the apphelp is shown should run, 0 otherwise.
  1180. Desc: The command line looks like this:
  1181. apphelp.exe GUID tagID [USELOCALCHM USEHTMLHELP APPHELPPATH]
  1182. Ex:
  1183. apphelp.exe {243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E
  1184. apphelp.exe {243B08D7-8CF7-4072-AF64-FD5DF4085E26} 0x0000009E 1 1 c:\temp
  1185. --*/
  1186. {
  1187. int nReturn = 1; // we always default to running, if something goes wrong
  1188. LPWSTR szCommandLine;
  1189. LPWSTR* argv;
  1190. int argc;
  1191. UNICODE_STRING ustrGuid;
  1192. GUID guidDB = { 0 };
  1193. GUID guidExeID = { 0 };
  1194. TAGID tiExe = TAGID_NULL;
  1195. TAGID tiExeID = TAGID_NULL;
  1196. TAGREF trExe = TAGREF_NULL;
  1197. WCHAR wszDBPath[MAX_PATH];
  1198. DWORD dwType = 0;
  1199. APPHELP_DATA ApphelpData;
  1200. WCHAR szDBPath[MAX_PATH];
  1201. HSDB hSDB = NULL;
  1202. PDB pdb = NULL;
  1203. DWORD dwFlags = 0;
  1204. BOOL bAppHelp = FALSE;
  1205. BOOL bRunApp = FALSE;
  1206. g_hInstance = hInstance;
  1207. #ifndef _WIN64
  1208. if (CheckWOW64Redirection(&nReturn)) {
  1209. goto out;
  1210. }
  1211. #endif // _WIN64
  1212. InitCommonControls();
  1213. ZeroMemory(&ApphelpData, sizeof(ApphelpData));
  1214. //
  1215. // Note that this memory isn't freed because it will automatically
  1216. // be freed on exit anyway, and there are a lot of exit cases from
  1217. // this application.
  1218. //
  1219. szCommandLine = GetCommandLineW();
  1220. argv = CommandLineToArgvW(szCommandLine, &argc);
  1221. ParseCommandLineArgs(argc, argv);
  1222. if (argc > 1) {
  1223. if (_wcsicmp(L"feedback", argv[1]) == 0) {
  1224. ShowFeedbackDialog(argc > 2 ? argv[2] : NULL);
  1225. }
  1226. }
  1227. if (g_pszGuid == NULL) {
  1228. DBGPRINT((sdlError, "AHUI!wWinMain",
  1229. "GUID not provided\n"));
  1230. goto out;
  1231. }
  1232. if (!(g_dwTagID ^ g_dwHtmlHelpID)) {
  1233. DBGPRINT((sdlError, "AHUI!wWinMain",
  1234. "Only TagID or HtmlHelpID should be provided\n"));
  1235. goto out;
  1236. }
  1237. RtlInitUnicodeString(&ustrGuid, g_pszGuid);
  1238. if (g_dwHtmlHelpID) {
  1239. //
  1240. // provided here: guid, severity and html help id along with app name
  1241. //
  1242. if (!NT_SUCCESS(RtlGUIDFromString(&ustrGuid, &guidExeID))) {
  1243. DBGPRINT((sdlError,
  1244. "Ahui!wWinMain",
  1245. "Error getting GUID from string %s\n", g_pszGuid));
  1246. goto out;
  1247. }
  1248. ApphelpData.dwSeverity = g_dwSeverity;
  1249. ApphelpData.dwHTMLHelpID = g_dwHtmlHelpID;
  1250. ApphelpData.szAppName = (LPWSTR)g_pAppName;
  1251. bAppHelp = TRUE;
  1252. dwType = SDB_DATABASE_MAIN_SHIM;
  1253. goto ProceedWithApphelp;
  1254. }
  1255. // non-htmlid case, guid is a database guid
  1256. // also dwTagID is specified
  1257. if (RtlGUIDFromString(&ustrGuid, &guidDB) != STATUS_SUCCESS) {
  1258. DBGPRINT((sdlError,
  1259. "AppHelp.exe!wWinMain",
  1260. "Error trying to convert GUID string %S.\n",
  1261. g_pszGuid));
  1262. goto out;
  1263. }
  1264. tiExe = (TAGID)g_dwTagID;
  1265. if (tiExe == TAGID_NULL) {
  1266. DBGPRINT((sdlError,
  1267. "AppHelp.exe!wWinMain",
  1268. "Error getting TAGID from param %S\n",
  1269. argv[2]));
  1270. goto out;
  1271. }
  1272. hSDB = SdbInitDatabaseEx(0, NULL, g_uExeImageType);
  1273. if (!hSDB) {
  1274. DBGPRINT((sdlError,
  1275. "AppHelp.exe!wWinMain",
  1276. "Error initing database context.\n"));
  1277. goto out;
  1278. }
  1279. if (g_bMSI) {
  1280. SdbSetImageType(hSDB, IMAGE_FILE_MSI);
  1281. }
  1282. pdb = SdbGetPDBFromGUID(hSDB, &guidDB);
  1283. if (!pdb) {
  1284. DWORD dwLen;
  1285. //
  1286. // It's not one of the main DBs, try it as a local.
  1287. //
  1288. dwLen = SdbResolveDatabase(hSDB, &guidDB, &dwType, szDBPath, MAX_PATH);
  1289. if (!dwLen || dwLen > MAX_PATH) {
  1290. DBGPRINT((sdlError,
  1291. "AppHelp.exe!wWinMain",
  1292. "Error resolving database from GUID\n"));
  1293. goto out;
  1294. }
  1295. //
  1296. // We have many "main" databases -- we should limit the check
  1297. //
  1298. if (dwType != SDB_DATABASE_MAIN_SHIM && dwType != SDB_DATABASE_MAIN_TEST) {
  1299. SdbOpenLocalDatabase(hSDB, szDBPath);
  1300. }
  1301. pdb = SdbGetPDBFromGUID(hSDB, &guidDB);
  1302. if (!pdb) {
  1303. DBGPRINT((sdlError,
  1304. "AppHelp.exe!wWinMain",
  1305. "Error getting pdb from GUID.\n"));
  1306. goto out;
  1307. }
  1308. } else {
  1309. dwType |= SDB_DATABASE_MAIN; // we will use details from the main db
  1310. }
  1311. if (!SdbTagIDToTagRef(hSDB, pdb, tiExe, &trExe)) {
  1312. DBGPRINT((sdlError,
  1313. "AppHelp.exe!wWinMain",
  1314. "Error converting TAGID to TAGREF.\n"));
  1315. goto out;
  1316. }
  1317. tiExeID = SdbFindFirstTag(pdb, tiExe, TAG_EXE_ID);
  1318. if (tiExeID == TAGID_NULL) {
  1319. DBGPRINT((sdlError,
  1320. "AppHelp.exe!wWinMain",
  1321. "Error trying to find TAG_EXE_ID.\n"));
  1322. goto out;
  1323. }
  1324. if (!SdbReadBinaryTag(pdb,
  1325. tiExeID,
  1326. (PBYTE)&guidExeID,
  1327. sizeof(GUID))) {
  1328. DBGPRINT((sdlError,
  1329. "AppHelp.exe!wWinMain",
  1330. "Error trying to read TAG_EXE_ID.\n"));
  1331. goto out;
  1332. }
  1333. bAppHelp = SdbReadApphelpData(hSDB, trExe, &ApphelpData);
  1334. ProceedWithApphelp:
  1335. if (SdbIsNullGUID(&guidExeID) || !SdbGetEntryFlags(&guidExeID, &dwFlags)) {
  1336. dwFlags = 0;
  1337. }
  1338. if (bAppHelp) {
  1339. //
  1340. // Check whether the disable bit is set.
  1341. //
  1342. if (!(dwFlags & SHIMREG_DISABLE_APPHELP)) {
  1343. BOOL bNoUI;
  1344. //
  1345. // See whether the user has checked "Don't show this anymore" box before.
  1346. //
  1347. bNoUI = ((dwFlags & SHIMREG_APPHELP_NOUI) != 0);
  1348. if (!bNoUI) {
  1349. bNoUI = !CheckWindowStation();
  1350. }
  1351. if (!bNoUI) {
  1352. bNoUI = !CheckUserToken();
  1353. }
  1354. if (bNoUI) {
  1355. DBGPRINT((sdlInfo,
  1356. "bCheckRunBadapp",
  1357. "NoUI flag is set, apphelp UI disabled for this app.\n"));
  1358. }
  1359. //
  1360. // depending on severity of the problem...
  1361. //
  1362. switch (ApphelpData.dwSeverity) {
  1363. case APPHELP_MINORPROBLEM:
  1364. case APPHELP_HARDBLOCK:
  1365. case APPHELP_NOBLOCK:
  1366. case APPHELP_REINSTALL:
  1367. if (bNoUI) {
  1368. bRunApp = (ApphelpData.dwSeverity != APPHELP_HARDBLOCK) && !(dwFlags & SHIMREG_APPHELP_CANCEL);
  1369. } else {
  1370. DWORD dwRet;
  1371. //
  1372. // Show the UI. This function returns -1 in case of error or one
  1373. // of the following values on success:
  1374. // IDOK | 0x8000 - "no ui" checked, run app
  1375. // IDCANCEL - do not run app
  1376. // IDOK - run app
  1377. ApphelpData.dwData = dwType; // we use custom data for database type
  1378. dwRet = ShowApphelp(&ApphelpData, NULL, (dwType & SDB_DATABASE_MAIN) ? NULL : SdbGetLocalPDB(hSDB));
  1379. if (dwRet != APPHELP_DIALOG_FAILED) {
  1380. //
  1381. // The UI was shown. See whether the user has
  1382. // checked the "no ui" box.
  1383. //
  1384. if (dwRet & 0x8000) {
  1385. //
  1386. // "no ui" box was checked. Save the appropriate bits
  1387. // in the registry.
  1388. //
  1389. dwFlags |= SHIMREG_APPHELP_NOUI;
  1390. if ((dwRet & 0x0FFF) != IDOK) {
  1391. dwFlags |= SHIMREG_APPHELP_CANCEL; // we will not be hitting this path unless g_bPreserveChoice is enabled
  1392. }
  1393. if (!SdbIsNullGUID(&guidExeID)) {
  1394. SdbSetEntryFlags(&guidExeID, dwFlags);
  1395. }
  1396. }
  1397. //
  1398. // Check whether the user has chosen to run the app.
  1399. //
  1400. bRunApp = ((dwRet & 0x0FFF) == IDOK) && (ApphelpData.dwSeverity != APPHELP_HARDBLOCK);
  1401. } else {
  1402. //
  1403. // The UI was not shown (some error prevented that).
  1404. // If the app is not "Hardblock" run it anyway.
  1405. //
  1406. bRunApp = (APPHELP_HARDBLOCK != ApphelpData.dwSeverity);
  1407. }
  1408. }
  1409. break;
  1410. }
  1411. }
  1412. }
  1413. if (!bRunApp) {
  1414. nReturn = 0;
  1415. }
  1416. out:
  1417. return nReturn;
  1418. }