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.

2805 lines
99 KiB

  1. //-------------------------------------------------------------------------------------------------
  2. // <copyright file="WixStandardBootstrapperApplication.cpp" company="Outercurve Foundation">
  3. // Copyright (c) 2004, Outercurve Foundation.
  4. // This software is released under Microsoft Reciprocal License (MS-RL).
  5. // The license and further copyright text can be found in the file
  6. // LICENSE.TXT at the root directory of the distribution.
  7. // </copyright>
  8. //-------------------------------------------------------------------------------------------------
  9. #include "pch.h"
  10. static const LPCWSTR PYBA_WINDOW_CLASS = L"PythonBA";
  11. static const LPCWSTR PYBA_VARIABLE_LAUNCH_TARGET_PATH = L"LaunchTarget";
  12. static const LPCWSTR PYBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID = L"LaunchTargetElevatedId";
  13. static const LPCWSTR PYBA_VARIABLE_LAUNCH_ARGUMENTS = L"LaunchArguments";
  14. static const LPCWSTR PYBA_VARIABLE_LAUNCH_HIDDEN = L"LaunchHidden";
  15. static const DWORD PYBA_ACQUIRE_PERCENTAGE = 30;
  16. static const LPCWSTR PYBA_VARIABLE_BUNDLE_FILE_VERSION = L"WixBundleFileVersion";
  17. enum PYBA_STATE {
  18. PYBA_STATE_INITIALIZING,
  19. PYBA_STATE_INITIALIZED,
  20. PYBA_STATE_HELP,
  21. PYBA_STATE_DETECTING,
  22. PYBA_STATE_DETECTED,
  23. PYBA_STATE_PLANNING,
  24. PYBA_STATE_PLANNED,
  25. PYBA_STATE_APPLYING,
  26. PYBA_STATE_CACHING,
  27. PYBA_STATE_CACHED,
  28. PYBA_STATE_EXECUTING,
  29. PYBA_STATE_EXECUTED,
  30. PYBA_STATE_APPLIED,
  31. PYBA_STATE_FAILED,
  32. };
  33. static const int WM_PYBA_SHOW_HELP = WM_APP + 100;
  34. static const int WM_PYBA_DETECT_PACKAGES = WM_APP + 101;
  35. static const int WM_PYBA_PLAN_PACKAGES = WM_APP + 102;
  36. static const int WM_PYBA_APPLY_PACKAGES = WM_APP + 103;
  37. static const int WM_PYBA_CHANGE_STATE = WM_APP + 104;
  38. static const int WM_PYBA_SHOW_FAILURE = WM_APP + 105;
  39. // This enum must be kept in the same order as the PAGE_NAMES array.
  40. enum PAGE {
  41. PAGE_LOADING,
  42. PAGE_HELP,
  43. PAGE_INSTALL,
  44. PAGE_SIMPLE_INSTALL,
  45. PAGE_CUSTOM1,
  46. PAGE_CUSTOM2,
  47. PAGE_MODIFY,
  48. PAGE_PROGRESS,
  49. PAGE_PROGRESS_PASSIVE,
  50. PAGE_SUCCESS,
  51. PAGE_FAILURE,
  52. COUNT_PAGE,
  53. };
  54. // This array must be kept in the same order as the PAGE enum.
  55. static LPCWSTR PAGE_NAMES[] = {
  56. L"Loading",
  57. L"Help",
  58. L"Install",
  59. L"SimpleInstall",
  60. L"Custom1",
  61. L"Custom2",
  62. L"Modify",
  63. L"Progress",
  64. L"ProgressPassive",
  65. L"Success",
  66. L"Failure",
  67. };
  68. enum CONTROL_ID {
  69. // Non-paged controls
  70. ID_CLOSE_BUTTON = THEME_FIRST_ASSIGN_CONTROL_ID,
  71. ID_MINIMIZE_BUTTON,
  72. // Welcome page
  73. ID_INSTALL_ALL_USERS_BUTTON,
  74. ID_INSTALL_JUST_FOR_ME_BUTTON,
  75. ID_INSTALL_CUSTOM_BUTTON,
  76. ID_INSTALL_SIMPLE_BUTTON,
  77. ID_INSTALL_CANCEL_BUTTON,
  78. // Customize Page
  79. ID_TARGETDIR_EDITBOX,
  80. ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX,
  81. ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX,
  82. ID_CUSTOM_COMPILE_ALL_CHECKBOX,
  83. ID_CUSTOM_BROWSE_BUTTON,
  84. ID_CUSTOM_BROWSE_BUTTON_LABEL,
  85. ID_CUSTOM_INSTALL_BUTTON,
  86. ID_CUSTOM_NEXT_BUTTON,
  87. ID_CUSTOM1_BACK_BUTTON,
  88. ID_CUSTOM2_BACK_BUTTON,
  89. ID_CUSTOM1_CANCEL_BUTTON,
  90. ID_CUSTOM2_CANCEL_BUTTON,
  91. // Modify page
  92. ID_MODIFY_BUTTON,
  93. ID_REPAIR_BUTTON,
  94. ID_UNINSTALL_BUTTON,
  95. ID_MODIFY_CANCEL_BUTTON,
  96. // Progress page
  97. ID_CACHE_PROGRESS_PACKAGE_TEXT,
  98. ID_CACHE_PROGRESS_BAR,
  99. ID_CACHE_PROGRESS_TEXT,
  100. ID_EXECUTE_PROGRESS_PACKAGE_TEXT,
  101. ID_EXECUTE_PROGRESS_BAR,
  102. ID_EXECUTE_PROGRESS_TEXT,
  103. ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT,
  104. ID_OVERALL_PROGRESS_PACKAGE_TEXT,
  105. ID_OVERALL_PROGRESS_BAR,
  106. ID_OVERALL_CALCULATED_PROGRESS_BAR,
  107. ID_OVERALL_PROGRESS_TEXT,
  108. ID_PROGRESS_CANCEL_BUTTON,
  109. // Success page
  110. ID_LAUNCH_BUTTON,
  111. ID_SUCCESS_TEXT,
  112. ID_SUCCESS_RESTART_TEXT,
  113. ID_SUCCESS_RESTART_BUTTON,
  114. ID_SUCCESS_CANCEL_BUTTON,
  115. // Failure page
  116. ID_FAILURE_LOGFILE_LINK,
  117. ID_FAILURE_MESSAGE_TEXT,
  118. ID_FAILURE_RESTART_TEXT,
  119. ID_FAILURE_RESTART_BUTTON,
  120. ID_FAILURE_CANCEL_BUTTON
  121. };
  122. static THEME_ASSIGN_CONTROL_ID CONTROL_ID_NAMES[] = {
  123. { ID_CLOSE_BUTTON, L"CloseButton" },
  124. { ID_MINIMIZE_BUTTON, L"MinimizeButton" },
  125. { ID_INSTALL_ALL_USERS_BUTTON, L"InstallAllUsersButton" },
  126. { ID_INSTALL_JUST_FOR_ME_BUTTON, L"InstallJustForMeButton" },
  127. { ID_INSTALL_CUSTOM_BUTTON, L"InstallCustomButton" },
  128. { ID_INSTALL_SIMPLE_BUTTON, L"InstallSimpleButton" },
  129. { ID_INSTALL_CANCEL_BUTTON, L"InstallCancelButton" },
  130. { ID_TARGETDIR_EDITBOX, L"TargetDir" },
  131. { ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, L"AssociateFiles" },
  132. { ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX, L"InstallAllUsers" },
  133. { ID_CUSTOM_COMPILE_ALL_CHECKBOX, L"CompileAll" },
  134. { ID_CUSTOM_BROWSE_BUTTON, L"CustomBrowseButton" },
  135. { ID_CUSTOM_BROWSE_BUTTON_LABEL, L"CustomBrowseButtonLabel" },
  136. { ID_CUSTOM_INSTALL_BUTTON, L"CustomInstallButton" },
  137. { ID_CUSTOM_NEXT_BUTTON, L"CustomNextButton" },
  138. { ID_CUSTOM1_BACK_BUTTON, L"Custom1BackButton" },
  139. { ID_CUSTOM2_BACK_BUTTON, L"Custom2BackButton" },
  140. { ID_CUSTOM1_CANCEL_BUTTON, L"Custom1CancelButton" },
  141. { ID_CUSTOM2_CANCEL_BUTTON, L"Custom2CancelButton" },
  142. { ID_MODIFY_BUTTON, L"ModifyButton" },
  143. { ID_REPAIR_BUTTON, L"RepairButton" },
  144. { ID_UNINSTALL_BUTTON, L"UninstallButton" },
  145. { ID_MODIFY_CANCEL_BUTTON, L"ModifyCancelButton" },
  146. { ID_CACHE_PROGRESS_PACKAGE_TEXT, L"CacheProgressPackageText" },
  147. { ID_CACHE_PROGRESS_BAR, L"CacheProgressbar" },
  148. { ID_CACHE_PROGRESS_TEXT, L"CacheProgressText" },
  149. { ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L"ExecuteProgressPackageText" },
  150. { ID_EXECUTE_PROGRESS_BAR, L"ExecuteProgressbar" },
  151. { ID_EXECUTE_PROGRESS_TEXT, L"ExecuteProgressText" },
  152. { ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"ExecuteProgressActionDataText" },
  153. { ID_OVERALL_PROGRESS_PACKAGE_TEXT, L"OverallProgressPackageText" },
  154. { ID_OVERALL_PROGRESS_BAR, L"OverallProgressbar" },
  155. { ID_OVERALL_CALCULATED_PROGRESS_BAR, L"OverallCalculatedProgressbar" },
  156. { ID_OVERALL_PROGRESS_TEXT, L"OverallProgressText" },
  157. { ID_PROGRESS_CANCEL_BUTTON, L"ProgressCancelButton" },
  158. { ID_LAUNCH_BUTTON, L"LaunchButton" },
  159. { ID_SUCCESS_TEXT, L"SuccessText" },
  160. { ID_SUCCESS_RESTART_TEXT, L"SuccessRestartText" },
  161. { ID_SUCCESS_RESTART_BUTTON, L"SuccessRestartButton" },
  162. { ID_SUCCESS_CANCEL_BUTTON, L"SuccessCancelButton" },
  163. { ID_FAILURE_LOGFILE_LINK, L"FailureLogFileLink" },
  164. { ID_FAILURE_MESSAGE_TEXT, L"FailureMessageText" },
  165. { ID_FAILURE_RESTART_TEXT, L"FailureRestartText" },
  166. { ID_FAILURE_RESTART_BUTTON, L"FailureRestartButton" },
  167. { ID_FAILURE_CANCEL_BUTTON, L"FailureCancelButton" },
  168. };
  169. class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {
  170. void ShowPage(DWORD newPageId) {
  171. // Process each control for special handling in the new page.
  172. ProcessPageControls(ThemeGetPage(_theme, newPageId));
  173. // Enable disable controls per-page.
  174. if (_pageIds[PAGE_INSTALL] == newPageId || _pageIds[PAGE_SIMPLE_INSTALL] == newPageId) {
  175. InstallPage_Show();
  176. } else if (_pageIds[PAGE_CUSTOM1] == newPageId) {
  177. Custom1Page_Show();
  178. } else if (_pageIds[PAGE_CUSTOM2] == newPageId) {
  179. Custom2Page_Show();
  180. } else if (_pageIds[PAGE_MODIFY] == newPageId) {
  181. ModifyPage_Show();
  182. } else if (_pageIds[PAGE_SUCCESS] == newPageId) {
  183. SuccessPage_Show();
  184. } else if (_pageIds[PAGE_FAILURE] == newPageId) {
  185. FailurePage_Show();
  186. }
  187. // Prevent repainting while switching page to avoid ugly flickering
  188. _suppressPaint = TRUE;
  189. ThemeShowPage(_theme, newPageId, SW_SHOW);
  190. ThemeShowPage(_theme, _visiblePageId, SW_HIDE);
  191. _suppressPaint = FALSE;
  192. InvalidateRect(_theme->hwndParent, nullptr, TRUE);
  193. _visiblePageId = newPageId;
  194. // On the install page set the focus to the install button or
  195. // the next enabled control if install is disabled
  196. if (_pageIds[PAGE_INSTALL] == newPageId) {
  197. ThemeSetFocus(_theme, ID_INSTALL_ALL_USERS_BUTTON);
  198. } else if (_pageIds[PAGE_SIMPLE_INSTALL] == newPageId) {
  199. ThemeSetFocus(_theme, ID_INSTALL_SIMPLE_BUTTON);
  200. }
  201. }
  202. //
  203. // Handles control clicks
  204. //
  205. void OnCommand(CONTROL_ID id) {
  206. LPWSTR defaultDir = nullptr;
  207. LPWSTR targetDir = nullptr;
  208. LONGLONG elevated, crtInstalled;
  209. BOOL checked;
  210. WCHAR wzPath[MAX_PATH] = { };
  211. BROWSEINFOW browseInfo = { };
  212. PIDLIST_ABSOLUTE pidl = nullptr;
  213. HRESULT hr = S_OK;
  214. switch(id) {
  215. case ID_CLOSE_BUTTON:
  216. OnClickCloseButton();
  217. break;
  218. // Install commands
  219. case ID_INSTALL_SIMPLE_BUTTON:
  220. hr = BalGetStringVariable(L"TargetDir", &targetDir);
  221. if (FAILED(hr) || !targetDir || !*targetDir) {
  222. LONGLONG installAll;
  223. if (SUCCEEDED(BalGetNumericVariable(L"InstallAllUsers", &installAll)) && installAll) {
  224. hr = BalGetStringVariable(L"DefaultAllUsersTargetDir", &defaultDir);
  225. BalExitOnFailure(hr, "Failed to get the default all users install directory");
  226. } else {
  227. hr = BalGetStringVariable(L"DefaultJustForMeTargetDir", &defaultDir);
  228. BalExitOnFailure(hr, "Failed to get the default per-user install directory");
  229. }
  230. if (!defaultDir || !*defaultDir) {
  231. BalLogError(E_INVALIDARG, "Default install directory is blank");
  232. }
  233. hr = BalFormatString(defaultDir, &targetDir);
  234. BalExitOnFailure1(hr, "Failed to format '%ls'", defaultDir);
  235. hr = _engine->SetVariableString(L"TargetDir", targetDir);
  236. ReleaseStr(targetDir);
  237. BalExitOnFailure(hr, "Failed to set install target directory");
  238. } else {
  239. ReleaseStr(targetDir);
  240. }
  241. OnPlan(BOOTSTRAPPER_ACTION_INSTALL);
  242. break;
  243. case ID_INSTALL_ALL_USERS_BUTTON:
  244. SavePageSettings();
  245. hr = _engine->SetVariableNumeric(L"InstallAllUsers", 1);
  246. ExitOnFailure(hr, L"Failed to set install scope");
  247. hr = _engine->SetVariableNumeric(L"CompileAll", 1);
  248. ExitOnFailure(hr, L"Failed to set compile all setting");
  249. hr = BalGetStringVariable(L"DefaultAllUsersTargetDir", &defaultDir);
  250. BalExitOnFailure(hr, "Failed to get the default all users install directory");
  251. if (!defaultDir || !*defaultDir) {
  252. BalLogError(E_INVALIDARG, "Default install directory is blank");
  253. }
  254. hr = BalFormatString(defaultDir, &targetDir);
  255. BalExitOnFailure1(hr, "Failed to format '%ls'", defaultDir);
  256. hr = _engine->SetVariableString(L"TargetDir", targetDir);
  257. ReleaseStr(targetDir);
  258. BalExitOnFailure(hr, "Failed to set install target directory");
  259. OnPlan(BOOTSTRAPPER_ACTION_INSTALL);
  260. break;
  261. case ID_INSTALL_JUST_FOR_ME_BUTTON:
  262. SavePageSettings();
  263. if (!QueryElevateForCrtInstall()) {
  264. break;
  265. }
  266. hr = _engine->SetVariableNumeric(L"InstallAllUsers", 0);
  267. ExitOnFailure(hr, L"Failed to set install scope");
  268. hr = _engine->SetVariableNumeric(L"CompileAll", 0);
  269. ExitOnFailure(hr, L"Failed to unset CompileAll");
  270. hr = BalGetStringVariable(L"DefaultJustForMeTargetDir", &defaultDir);
  271. BalExitOnFailure(hr, "Failed to get the default per-user install directory");
  272. if (!defaultDir || !*defaultDir) {
  273. BalLogError(E_INVALIDARG, "Default install directory is blank");
  274. }
  275. hr = BalFormatString(defaultDir, &targetDir);
  276. BalExitOnFailure1(hr, "Failed to format '%ls'", defaultDir);
  277. hr = _engine->SetVariableString(L"TargetDir", targetDir);
  278. ReleaseStr(targetDir);
  279. BalExitOnFailure(hr, "Failed to set install target directory");
  280. OnPlan(BOOTSTRAPPER_ACTION_INSTALL);
  281. break;
  282. case ID_CUSTOM1_BACK_BUTTON:
  283. SavePageSettings();
  284. if (_modifying) {
  285. GoToPage(PAGE_MODIFY);
  286. } else {
  287. GoToPage(PAGE_INSTALL);
  288. }
  289. break;
  290. case ID_INSTALL_CUSTOM_BUTTON: __fallthrough;
  291. case ID_CUSTOM2_BACK_BUTTON:
  292. SavePageSettings();
  293. GoToPage(PAGE_CUSTOM1);
  294. break;
  295. case ID_CUSTOM_NEXT_BUTTON:
  296. SavePageSettings();
  297. GoToPage(PAGE_CUSTOM2);
  298. break;
  299. case ID_CUSTOM_INSTALL_BUTTON:
  300. SavePageSettings();
  301. hr = BalGetStringVariable(L"TargetDir", &targetDir);
  302. if (SUCCEEDED(hr)) {
  303. // TODO: Check whether directory exists and contains another installation
  304. ReleaseStr(targetDir);
  305. }
  306. checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX);
  307. if (!checked && !QueryElevateForCrtInstall()) {
  308. break;
  309. }
  310. OnPlan(_command.action);
  311. break;
  312. case ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX:
  313. hr = BalGetNumericVariable(L"WixBundleElevated", &elevated);
  314. checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX);
  315. ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, checked && (FAILED(hr) || !elevated));
  316. ThemeControlEnable(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, !checked);
  317. if (checked) {
  318. _engine->SetVariableNumeric(L"CompileAll", 1);
  319. ThemeSendControlMessage(_theme, ID_CUSTOM_COMPILE_ALL_CHECKBOX, BM_SETCHECK, BST_CHECKED, 0);
  320. }
  321. ThemeGetTextControl(_theme, ID_TARGETDIR_EDITBOX, &targetDir);
  322. if (targetDir) {
  323. // Check the current value against the default to see
  324. // if we should switch it automatically.
  325. hr = BalGetStringVariable(
  326. checked ? L"DefaultJustForMeTargetDir" : L"DefaultAllUsersTargetDir",
  327. &defaultDir
  328. );
  329. if (SUCCEEDED(hr) && defaultDir) {
  330. LPWSTR formatted = nullptr;
  331. if (defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) {
  332. if (wcscmp(formatted, targetDir) == 0) {
  333. ReleaseStr(defaultDir);
  334. defaultDir = nullptr;
  335. ReleaseStr(formatted);
  336. formatted = nullptr;
  337. hr = BalGetStringVariable(
  338. checked ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",
  339. &defaultDir
  340. );
  341. if (SUCCEEDED(hr) && defaultDir && defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) {
  342. ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, formatted);
  343. ReleaseStr(formatted);
  344. }
  345. } else {
  346. ReleaseStr(formatted);
  347. }
  348. }
  349. ReleaseStr(defaultDir);
  350. }
  351. }
  352. break;
  353. case ID_CUSTOM_BROWSE_BUTTON:
  354. browseInfo.hwndOwner = _hWnd;
  355. browseInfo.pszDisplayName = wzPath;
  356. browseInfo.lpszTitle = _theme->sczCaption;
  357. browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
  358. pidl = ::SHBrowseForFolderW(&browseInfo);
  359. if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) {
  360. ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, wzPath);
  361. }
  362. if (pidl) {
  363. ::CoTaskMemFree(pidl);
  364. }
  365. break;
  366. // Modify commands
  367. case ID_MODIFY_BUTTON:
  368. // Some variables cannot be modified
  369. _engine->SetVariableString(L"InstallAllUsersState", L"disable");
  370. _engine->SetVariableString(L"TargetDirState", L"disable");
  371. _engine->SetVariableString(L"CustomBrowseButtonState", L"disable");
  372. _modifying = TRUE;
  373. GoToPage(PAGE_CUSTOM1);
  374. break;
  375. case ID_REPAIR_BUTTON:
  376. OnPlan(BOOTSTRAPPER_ACTION_REPAIR);
  377. break;
  378. case ID_UNINSTALL_BUTTON:
  379. OnPlan(BOOTSTRAPPER_ACTION_UNINSTALL);
  380. break;
  381. }
  382. LExit:
  383. return;
  384. }
  385. void InstallPage_Show() {
  386. // Ensure the All Users install button has a UAC shield
  387. LONGLONG elevated, installAll;
  388. if (FAILED(BalGetNumericVariable(L"WixBundleElevated", &elevated))) {
  389. elevated = 0;
  390. }
  391. ThemeControlElevates(_theme, ID_INSTALL_ALL_USERS_BUTTON, !elevated);
  392. if (SUCCEEDED(BalGetNumericVariable(L"InstallAllUsers", &installAll)) && installAll) {
  393. ThemeControlElevates(_theme, ID_INSTALL_SIMPLE_BUTTON, !elevated);
  394. }
  395. }
  396. void Custom1Page_Show() {
  397. }
  398. void Custom2Page_Show() {
  399. HRESULT hr;
  400. LONGLONG installAll, elevated, includeLauncher;
  401. if (FAILED(BalGetNumericVariable(L"WixBundleElevated", &elevated))) {
  402. elevated = 0;
  403. }
  404. if (SUCCEEDED(BalGetNumericVariable(L"InstallAllUsers", &installAll))) {
  405. ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, installAll && !elevated);
  406. ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_HIDE);
  407. } else {
  408. installAll = 0;
  409. ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_SHOW);
  410. }
  411. if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher)) && includeLauncher) {
  412. ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, TRUE);
  413. } else {
  414. ThemeSendControlMessage(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, BM_SETCHECK, BST_UNCHECKED, 0);
  415. ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, FALSE);
  416. }
  417. LPWSTR targetDir = nullptr;
  418. hr = BalGetStringVariable(L"TargetDir", &targetDir);
  419. if (SUCCEEDED(hr) && targetDir && targetDir[0]) {
  420. ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir);
  421. StrFree(targetDir);
  422. } else if (SUCCEEDED(hr)) {
  423. StrFree(targetDir);
  424. targetDir = nullptr;
  425. LPWSTR defaultTargetDir = nullptr;
  426. hr = BalGetStringVariable(L"DefaultCustomTargetDir", &defaultTargetDir);
  427. if (SUCCEEDED(hr) && defaultTargetDir && !defaultTargetDir[0]) {
  428. StrFree(defaultTargetDir);
  429. defaultTargetDir = nullptr;
  430. hr = BalGetStringVariable(
  431. installAll ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",
  432. &defaultTargetDir
  433. );
  434. }
  435. if (SUCCEEDED(hr) && defaultTargetDir) {
  436. if (defaultTargetDir[0] && SUCCEEDED(BalFormatString(defaultTargetDir, &targetDir))) {
  437. ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir);
  438. StrFree(targetDir);
  439. }
  440. StrFree(defaultTargetDir);
  441. }
  442. }
  443. }
  444. void ModifyPage_Show() {
  445. ThemeControlEnable(_theme, ID_REPAIR_BUTTON, !_suppressRepair);
  446. }
  447. void SuccessPage_Show() {
  448. // on the "Success" page, check if the restart or launch button should be enabled.
  449. BOOL showRestartButton = FALSE;
  450. BOOL launchTargetExists = FALSE;
  451. LOC_STRING *successText = nullptr;
  452. HRESULT hr = S_OK;
  453. if (_restartRequired) {
  454. if (BOOTSTRAPPER_RESTART_PROMPT == _command.restart) {
  455. showRestartButton = TRUE;
  456. }
  457. } else if (ThemeControlExists(_theme, ID_LAUNCH_BUTTON)) {
  458. launchTargetExists = BalStringVariableExists(PYBA_VARIABLE_LAUNCH_TARGET_PATH);
  459. }
  460. switch (_plannedAction) {
  461. case BOOTSTRAPPER_ACTION_INSTALL:
  462. hr = LocGetString(_wixLoc, L"#(loc.SuccessInstallMessage)", &successText);
  463. break;
  464. case BOOTSTRAPPER_ACTION_MODIFY:
  465. hr = LocGetString(_wixLoc, L"#(loc.SuccessModifyMessage)", &successText);
  466. break;
  467. case BOOTSTRAPPER_ACTION_REPAIR:
  468. hr = LocGetString(_wixLoc, L"#(loc.SuccessRepairMessage)", &successText);
  469. break;
  470. case BOOTSTRAPPER_ACTION_UNINSTALL:
  471. hr = LocGetString(_wixLoc, L"#(loc.SuccessRemoveMessage)", &successText);
  472. break;
  473. }
  474. if (successText) {
  475. LPWSTR formattedString = nullptr;
  476. BalFormatString(successText->wzText, &formattedString);
  477. if (formattedString) {
  478. ThemeSetTextControl(_theme, ID_SUCCESS_TEXT, formattedString);
  479. StrFree(formattedString);
  480. }
  481. }
  482. ThemeControlEnable(_theme, ID_LAUNCH_BUTTON, launchTargetExists && BOOTSTRAPPER_ACTION_UNINSTALL < _plannedAction);
  483. ThemeControlEnable(_theme, ID_SUCCESS_RESTART_TEXT, showRestartButton);
  484. ThemeControlEnable(_theme, ID_SUCCESS_RESTART_BUTTON, showRestartButton);
  485. }
  486. void FailurePage_Show() {
  487. // on the "Failure" page, show error message and check if the restart button should be enabled.
  488. // if there is a log file variable then we'll assume the log file exists.
  489. BOOL showLogLink = (_bundle.sczLogVariable && *_bundle.sczLogVariable);
  490. BOOL showErrorMessage = FALSE;
  491. BOOL showRestartButton = FALSE;
  492. if (FAILED(_hrFinal)) {
  493. LPWSTR unformattedText = nullptr;
  494. LPWSTR text = nullptr;
  495. // If we know the failure message, use that.
  496. if (_failedMessage && *_failedMessage) {
  497. StrAllocString(&unformattedText, _failedMessage, 0);
  498. } else {
  499. // try to get the error message from the error code.
  500. StrAllocFromError(&unformattedText, _hrFinal, nullptr);
  501. if (!unformattedText || !*unformattedText) {
  502. StrAllocFromError(&unformattedText, E_FAIL, nullptr);
  503. }
  504. }
  505. if (E_WIXSTDBA_CONDITION_FAILED == _hrFinal) {
  506. if (unformattedText) {
  507. StrAllocString(&text, unformattedText, 0);
  508. }
  509. } else {
  510. StrAllocFormatted(&text, L"0x%08x - %ls", _hrFinal, unformattedText);
  511. }
  512. if (text) {
  513. ThemeSetTextControl(_theme, ID_FAILURE_MESSAGE_TEXT, text);
  514. showErrorMessage = TRUE;
  515. }
  516. ReleaseStr(text);
  517. ReleaseStr(unformattedText);
  518. }
  519. if (_restartRequired && BOOTSTRAPPER_RESTART_PROMPT == _command.restart) {
  520. showRestartButton = TRUE;
  521. }
  522. ThemeControlEnable(_theme, ID_FAILURE_LOGFILE_LINK, showLogLink);
  523. ThemeControlEnable(_theme, ID_FAILURE_MESSAGE_TEXT, showErrorMessage);
  524. ThemeControlEnable(_theme, ID_FAILURE_RESTART_TEXT, showRestartButton);
  525. ThemeControlEnable(_theme, ID_FAILURE_RESTART_BUTTON, showRestartButton);
  526. }
  527. public: // IBootstrapperApplication
  528. virtual STDMETHODIMP OnStartup() {
  529. HRESULT hr = S_OK;
  530. DWORD dwUIThreadId = 0;
  531. // create UI thread
  532. _hUiThread = ::CreateThread(nullptr, 0, UiThreadProc, this, 0, &dwUIThreadId);
  533. if (!_hUiThread) {
  534. ExitWithLastError(hr, "Failed to create UI thread.");
  535. }
  536. LExit:
  537. return hr;
  538. }
  539. virtual STDMETHODIMP_(int) OnShutdown() {
  540. int nResult = IDNOACTION;
  541. // wait for UI thread to terminate
  542. if (_hUiThread) {
  543. ::WaitForSingleObject(_hUiThread, INFINITE);
  544. ReleaseHandle(_hUiThread);
  545. }
  546. // If a restart was required.
  547. if (_restartRequired && _allowRestart) {
  548. nResult = IDRESTART;
  549. }
  550. return nResult;
  551. }
  552. virtual STDMETHODIMP_(int) OnDetectRelatedBundle(
  553. __in LPCWSTR wzBundleId,
  554. __in BOOTSTRAPPER_RELATION_TYPE relationType,
  555. __in LPCWSTR /*wzBundleTag*/,
  556. __in BOOL fPerMachine,
  557. __in DWORD64 /*dw64Version*/,
  558. __in BOOTSTRAPPER_RELATED_OPERATION operation
  559. ) {
  560. BalInfoAddRelatedBundleAsPackage(&_bundle.packages, wzBundleId, relationType, fPerMachine);
  561. // Remember when our bundle would cause a downgrade.
  562. if (BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) {
  563. _downgrading = TRUE;
  564. }
  565. return CheckCanceled() ? IDCANCEL : IDOK;
  566. }
  567. virtual STDMETHODIMP_(void) OnDetectPackageComplete(
  568. __in LPCWSTR wzPackageId,
  569. __in HRESULT /*hrStatus*/,
  570. __in BOOTSTRAPPER_PACKAGE_STATE state
  571. ) { }
  572. virtual STDMETHODIMP_(void) OnDetectComplete(__in HRESULT hrStatus) {
  573. if (SUCCEEDED(hrStatus) && _baFunction) {
  574. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect complete BA function");
  575. _baFunction->OnDetectComplete();
  576. }
  577. if (SUCCEEDED(hrStatus)) {
  578. hrStatus = EvaluateConditions();
  579. }
  580. SetState(PYBA_STATE_DETECTED, hrStatus);
  581. // If we're not interacting with the user or we're doing a layout or we're just after a force restart
  582. // then automatically start planning.
  583. if (BOOTSTRAPPER_DISPLAY_FULL > _command.display ||
  584. BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||
  585. BOOTSTRAPPER_ACTION_UNINSTALL == _command.action ||
  586. BOOTSTRAPPER_RESUME_TYPE_REBOOT == _command.resumeType) {
  587. if (SUCCEEDED(hrStatus)) {
  588. ::PostMessageW(_hWnd, WM_PYBA_PLAN_PACKAGES, 0, _command.action);
  589. }
  590. }
  591. }
  592. virtual STDMETHODIMP_(int) OnPlanRelatedBundle(
  593. __in_z LPCWSTR /*wzBundleId*/,
  594. __inout_z BOOTSTRAPPER_REQUEST_STATE* pRequestedState
  595. ) {
  596. return CheckCanceled() ? IDCANCEL : IDOK;
  597. }
  598. virtual STDMETHODIMP_(int) OnPlanPackageBegin(
  599. __in_z LPCWSTR wzPackageId,
  600. __inout BOOTSTRAPPER_REQUEST_STATE *pRequestState
  601. ) {
  602. HRESULT hr = S_OK;
  603. BAL_INFO_PACKAGE* pPackage = nullptr;
  604. if (_nextPackageAfterRestart) {
  605. // After restart we need to finish the dependency registration for our package so allow the package
  606. // to go present.
  607. if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, _nextPackageAfterRestart, -1)) {
  608. // Do not allow a repair because that could put us in a perpetual restart loop.
  609. if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == *pRequestState) {
  610. *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
  611. }
  612. ReleaseNullStr(_nextPackageAfterRestart); // no more skipping now.
  613. } else {
  614. // not the matching package, so skip it.
  615. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Skipping package: %ls, after restart because it was applied before the restart.", wzPackageId);
  616. *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
  617. }
  618. } else if ((_plannedAction == BOOTSTRAPPER_ACTION_INSTALL || _plannedAction == BOOTSTRAPPER_ACTION_MODIFY) &&
  619. SUCCEEDED(BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage))) {
  620. BOOL f = FALSE;
  621. if (SUCCEEDED(_engine->EvaluateCondition(pPackage->sczInstallCondition, &f)) && f) {
  622. *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
  623. }
  624. }
  625. return CheckCanceled() ? IDCANCEL : IDOK;
  626. }
  627. virtual STDMETHODIMP_(int) OnPlanMsiFeature(
  628. __in_z LPCWSTR wzPackageId,
  629. __in_z LPCWSTR wzFeatureId,
  630. __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
  631. ) {
  632. LONGLONG install;
  633. if (wcscmp(wzFeatureId, L"AssociateFiles") == 0 || wcscmp(wzFeatureId, L"Shortcuts") == 0) {
  634. if (SUCCEEDED(_engine->GetVariableNumeric(wzFeatureId, &install)) && install) {
  635. *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
  636. } else {
  637. *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
  638. }
  639. } else {
  640. *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
  641. }
  642. return CheckCanceled() ? IDCANCEL : IDNOACTION;
  643. }
  644. virtual STDMETHODIMP_(void) OnPlanComplete(__in HRESULT hrStatus) {
  645. if (SUCCEEDED(hrStatus) && _baFunction) {
  646. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan complete BA function");
  647. _baFunction->OnPlanComplete();
  648. }
  649. SetState(PYBA_STATE_PLANNED, hrStatus);
  650. if (SUCCEEDED(hrStatus)) {
  651. ::PostMessageW(_hWnd, WM_PYBA_APPLY_PACKAGES, 0, 0);
  652. }
  653. _startedExecution = FALSE;
  654. _calculatedCacheProgress = 0;
  655. _calculatedExecuteProgress = 0;
  656. }
  657. virtual STDMETHODIMP_(int) OnCachePackageBegin(
  658. __in_z LPCWSTR wzPackageId,
  659. __in DWORD cCachePayloads,
  660. __in DWORD64 dw64PackageCacheSize
  661. ) {
  662. if (wzPackageId && *wzPackageId) {
  663. BAL_INFO_PACKAGE* pPackage = nullptr;
  664. HRESULT hr = BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage);
  665. LPCWSTR wz = (SUCCEEDED(hr) && pPackage->sczDisplayName) ? pPackage->sczDisplayName : wzPackageId;
  666. ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, wz);
  667. // If something started executing, leave it in the overall progress text.
  668. if (!_startedExecution) {
  669. ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz);
  670. }
  671. }
  672. return __super::OnCachePackageBegin(wzPackageId, cCachePayloads, dw64PackageCacheSize);
  673. }
  674. virtual STDMETHODIMP_(int) OnCacheAcquireProgress(
  675. __in_z LPCWSTR wzPackageOrContainerId,
  676. __in_z_opt LPCWSTR wzPayloadId,
  677. __in DWORD64 dw64Progress,
  678. __in DWORD64 dw64Total,
  679. __in DWORD dwOverallPercentage
  680. ) {
  681. WCHAR wzProgress[5] = { };
  682. #ifdef DEBUG
  683. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnCacheAcquireProgress() - container/package: %ls, payload: %ls, progress: %I64u, total: %I64u, overall progress: %u%%", wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage);
  684. #endif
  685. ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallPercentage);
  686. ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_TEXT, wzProgress);
  687. ThemeSetProgressControl(_theme, ID_CACHE_PROGRESS_BAR, dwOverallPercentage);
  688. _calculatedCacheProgress = dwOverallPercentage * PYBA_ACQUIRE_PERCENTAGE / 100;
  689. ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress);
  690. SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress);
  691. return __super::OnCacheAcquireProgress(wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage);
  692. }
  693. virtual STDMETHODIMP_(int) OnCacheAcquireComplete(
  694. __in_z LPCWSTR wzPackageOrContainerId,
  695. __in_z_opt LPCWSTR wzPayloadId,
  696. __in HRESULT hrStatus,
  697. __in int nRecommendation
  698. ) {
  699. SetProgressState(hrStatus);
  700. return __super::OnCacheAcquireComplete(wzPackageOrContainerId, wzPayloadId, hrStatus, nRecommendation);
  701. }
  702. virtual STDMETHODIMP_(int) OnCacheVerifyComplete(
  703. __in_z LPCWSTR wzPackageId,
  704. __in_z LPCWSTR wzPayloadId,
  705. __in HRESULT hrStatus,
  706. __in int nRecommendation
  707. ) {
  708. SetProgressState(hrStatus);
  709. return __super::OnCacheVerifyComplete(wzPackageId, wzPayloadId, hrStatus, nRecommendation);
  710. }
  711. virtual STDMETHODIMP_(void) OnCacheComplete(__in HRESULT /*hrStatus*/) {
  712. ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, L"");
  713. SetState(PYBA_STATE_CACHED, S_OK); // we always return success here and let OnApplyComplete() deal with the error.
  714. }
  715. virtual STDMETHODIMP_(int) OnError(
  716. __in BOOTSTRAPPER_ERROR_TYPE errorType,
  717. __in LPCWSTR wzPackageId,
  718. __in DWORD dwCode,
  719. __in_z LPCWSTR wzError,
  720. __in DWORD dwUIHint,
  721. __in DWORD /*cData*/,
  722. __in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/,
  723. __in int nRecommendation
  724. ) {
  725. int nResult = nRecommendation;
  726. LPWSTR sczError = nullptr;
  727. if (BOOTSTRAPPER_DISPLAY_EMBEDDED == _command.display) {
  728. HRESULT hr = _engine->SendEmbeddedError(dwCode, wzError, dwUIHint, &nResult);
  729. if (FAILED(hr)) {
  730. nResult = IDERROR;
  731. }
  732. } else if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {
  733. // If this is an authentication failure, let the engine try to handle it for us.
  734. if (BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER == errorType || BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY == errorType) {
  735. nResult = IDTRYAGAIN;
  736. } else // show a generic error message box.
  737. {
  738. BalRetryErrorOccurred(wzPackageId, dwCode);
  739. if (!_showingInternalUIThisPackage) {
  740. // If no error message was provided, use the error code to try and get an error message.
  741. if (!wzError || !*wzError || BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER != errorType) {
  742. HRESULT hr = StrAllocFromError(&sczError, dwCode, nullptr);
  743. if (FAILED(hr) || !sczError || !*sczError) {
  744. StrAllocFormatted(&sczError, L"0x%x", dwCode);
  745. }
  746. }
  747. nResult = ::MessageBoxW(_hWnd, sczError ? sczError : wzError, _theme->sczCaption, dwUIHint);
  748. }
  749. }
  750. SetProgressState(HRESULT_FROM_WIN32(dwCode));
  751. } else {
  752. // just take note of the error code and let things continue.
  753. BalRetryErrorOccurred(wzPackageId, dwCode);
  754. }
  755. ReleaseStr(sczError);
  756. return nResult;
  757. }
  758. virtual STDMETHODIMP_(int) OnExecuteMsiMessage(
  759. __in_z LPCWSTR wzPackageId,
  760. __in INSTALLMESSAGE mt,
  761. __in UINT uiFlags,
  762. __in_z LPCWSTR wzMessage,
  763. __in DWORD cData,
  764. __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
  765. __in int nRecommendation
  766. ) {
  767. #ifdef DEBUG
  768. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteMsiMessage() - package: %ls, message: %ls", wzPackageId, wzMessage);
  769. #endif
  770. if (BOOTSTRAPPER_DISPLAY_FULL == _command.display && (INSTALLMESSAGE_WARNING == mt || INSTALLMESSAGE_USER == mt)) {
  771. int nResult = ::MessageBoxW(_hWnd, wzMessage, _theme->sczCaption, uiFlags);
  772. return nResult;
  773. }
  774. if (INSTALLMESSAGE_ACTIONSTART == mt) {
  775. ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, wzMessage);
  776. }
  777. return __super::OnExecuteMsiMessage(wzPackageId, mt, uiFlags, wzMessage, cData, rgwzData, nRecommendation);
  778. }
  779. virtual STDMETHODIMP_(int) OnProgress(__in DWORD dwProgressPercentage, __in DWORD dwOverallProgressPercentage) {
  780. WCHAR wzProgress[5] = { };
  781. #ifdef DEBUG
  782. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnProgress() - progress: %u%%, overall progress: %u%%", dwProgressPercentage, dwOverallProgressPercentage);
  783. #endif
  784. ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage);
  785. ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_TEXT, wzProgress);
  786. ThemeSetProgressControl(_theme, ID_OVERALL_PROGRESS_BAR, dwOverallProgressPercentage);
  787. SetTaskbarButtonProgress(dwOverallProgressPercentage);
  788. return __super::OnProgress(dwProgressPercentage, dwOverallProgressPercentage);
  789. }
  790. virtual STDMETHODIMP_(int) OnExecutePackageBegin(__in_z LPCWSTR wzPackageId, __in BOOL fExecute) {
  791. LPWSTR sczFormattedString = nullptr;
  792. _startedExecution = TRUE;
  793. if (wzPackageId && *wzPackageId) {
  794. BAL_INFO_PACKAGE* pPackage = nullptr;
  795. BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage);
  796. LPCWSTR wz = wzPackageId;
  797. if (pPackage) {
  798. LOC_STRING* pLocString = nullptr;
  799. switch (pPackage->type) {
  800. case BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON:
  801. LocGetString(_wixLoc, L"#(loc.ExecuteAddonRelatedBundleMessage)", &pLocString);
  802. break;
  803. case BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH:
  804. LocGetString(_wixLoc, L"#(loc.ExecutePatchRelatedBundleMessage)", &pLocString);
  805. break;
  806. case BAL_INFO_PACKAGE_TYPE_BUNDLE_UPGRADE:
  807. LocGetString(_wixLoc, L"#(loc.ExecuteUpgradeRelatedBundleMessage)", &pLocString);
  808. break;
  809. }
  810. if (pLocString) {
  811. // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe
  812. // so don't go down the rabbit hole of making sure that this is securely freed.
  813. BalFormatString(pLocString->wzText, &sczFormattedString);
  814. }
  815. wz = sczFormattedString ? sczFormattedString : pPackage->sczDisplayName ? pPackage->sczDisplayName : wzPackageId;
  816. }
  817. _showingInternalUIThisPackage = pPackage && pPackage->fDisplayInternalUI;
  818. ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, wz);
  819. ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz);
  820. } else {
  821. _showingInternalUIThisPackage = FALSE;
  822. }
  823. ReleaseStr(sczFormattedString);
  824. return __super::OnExecutePackageBegin(wzPackageId, fExecute);
  825. }
  826. virtual int __stdcall OnExecuteProgress(
  827. __in_z LPCWSTR wzPackageId,
  828. __in DWORD dwProgressPercentage,
  829. __in DWORD dwOverallProgressPercentage
  830. ) {
  831. WCHAR wzProgress[8] = { };
  832. #ifdef DEBUG
  833. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteProgress() - package: %ls, progress: %u%%, overall progress: %u%%", wzPackageId, dwProgressPercentage, dwOverallProgressPercentage);
  834. #endif
  835. ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage);
  836. ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_TEXT, wzProgress);
  837. ThemeSetProgressControl(_theme, ID_EXECUTE_PROGRESS_BAR, dwOverallProgressPercentage);
  838. _calculatedExecuteProgress = dwOverallProgressPercentage * (100 - PYBA_ACQUIRE_PERCENTAGE) / 100;
  839. ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress);
  840. SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress);
  841. return __super::OnExecuteProgress(wzPackageId, dwProgressPercentage, dwOverallProgressPercentage);
  842. }
  843. virtual STDMETHODIMP_(int) OnExecutePackageComplete(
  844. __in_z LPCWSTR wzPackageId,
  845. __in HRESULT hrExitCode,
  846. __in BOOTSTRAPPER_APPLY_RESTART restart,
  847. __in int nRecommendation
  848. ) {
  849. SetProgressState(hrExitCode);
  850. if (_wcsnicmp(wzPackageId, L"path_", 5) == 0 && SUCCEEDED(hrExitCode)) {
  851. SendMessageTimeoutW(
  852. HWND_BROADCAST,
  853. WM_SETTINGCHANGE,
  854. 0,
  855. reinterpret_cast<LPARAM>(L"Environment"),
  856. SMTO_ABORTIFHUNG,
  857. 1000,
  858. nullptr
  859. );
  860. }
  861. int nResult = __super::OnExecutePackageComplete(wzPackageId, hrExitCode, restart, nRecommendation);
  862. return nResult;
  863. }
  864. virtual STDMETHODIMP_(void) OnExecuteComplete(__in HRESULT hrStatus) {
  865. ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L"");
  866. ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"");
  867. ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, L"");
  868. ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, FALSE); // no more cancel.
  869. SetState(PYBA_STATE_EXECUTED, S_OK); // we always return success here and let OnApplyComplete() deal with the error.
  870. SetProgressState(hrStatus);
  871. }
  872. virtual STDMETHODIMP_(int) OnResolveSource(
  873. __in_z LPCWSTR wzPackageOrContainerId,
  874. __in_z_opt LPCWSTR wzPayloadId,
  875. __in_z LPCWSTR wzLocalSource,
  876. __in_z_opt LPCWSTR wzDownloadSource
  877. ) {
  878. int nResult = IDERROR; // assume we won't resolve source and that is unexpected.
  879. if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {
  880. if (wzDownloadSource) {
  881. nResult = IDDOWNLOAD;
  882. } else {
  883. // prompt to change the source location.
  884. OPENFILENAMEW ofn = { };
  885. WCHAR wzFile[MAX_PATH] = { };
  886. ::StringCchCopyW(wzFile, countof(wzFile), wzLocalSource);
  887. ofn.lStructSize = sizeof(ofn);
  888. ofn.hwndOwner = _hWnd;
  889. ofn.lpstrFile = wzFile;
  890. ofn.nMaxFile = countof(wzFile);
  891. ofn.lpstrFilter = L"All Files\0*.*\0";
  892. ofn.nFilterIndex = 1;
  893. ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  894. ofn.lpstrTitle = _theme->sczCaption;
  895. if (::GetOpenFileNameW(&ofn)) {
  896. HRESULT hr = _engine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, ofn.lpstrFile);
  897. nResult = SUCCEEDED(hr) ? IDRETRY : IDERROR;
  898. } else {
  899. nResult = IDCANCEL;
  900. }
  901. }
  902. } else if (wzDownloadSource) {
  903. // If doing a non-interactive install and download source is available, let's try downloading the package silently
  904. nResult = IDDOWNLOAD;
  905. }
  906. // else there's nothing more we can do in non-interactive mode
  907. return CheckCanceled() ? IDCANCEL : nResult;
  908. }
  909. virtual STDMETHODIMP_(int) OnApplyComplete(__in HRESULT hrStatus, __in BOOTSTRAPPER_APPLY_RESTART restart) {
  910. _restartResult = restart; // remember the restart result so we return the correct error code no matter what the user chooses to do in the UI.
  911. // If a restart was encountered and we are not suppressing restarts, then restart is required.
  912. _restartRequired = (BOOTSTRAPPER_APPLY_RESTART_NONE != restart && BOOTSTRAPPER_RESTART_NEVER < _command.restart);
  913. // If a restart is required and we're not displaying a UI or we are not supposed to prompt for restart then allow the restart.
  914. _allowRestart = _restartRequired && (BOOTSTRAPPER_DISPLAY_FULL > _command.display || BOOTSTRAPPER_RESTART_PROMPT < _command.restart);
  915. // If we are showing UI, wait a beat before moving to the final screen.
  916. if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
  917. ::Sleep(250);
  918. }
  919. SetState(PYBA_STATE_APPLIED, hrStatus);
  920. SetTaskbarButtonProgress(100); // show full progress bar, green, yellow, or red
  921. return IDNOACTION;
  922. }
  923. virtual STDMETHODIMP_(void) OnLaunchApprovedExeComplete(__in HRESULT hrStatus, __in DWORD /*processId*/) {
  924. if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hrStatus) {
  925. //try with ShelExec next time
  926. OnClickLaunchButton();
  927. } else {
  928. ::PostMessageW(_hWnd, WM_CLOSE, 0, 0);
  929. }
  930. }
  931. private:
  932. //
  933. // UiThreadProc - entrypoint for UI thread.
  934. //
  935. static DWORD WINAPI UiThreadProc(__in LPVOID pvContext) {
  936. HRESULT hr = S_OK;
  937. PythonBootstrapperApplication* pThis = (PythonBootstrapperApplication*)pvContext;
  938. BOOL comInitialized = FALSE;
  939. BOOL ret = FALSE;
  940. MSG msg = { };
  941. // Initialize COM and theme.
  942. hr = ::CoInitialize(nullptr);
  943. BalExitOnFailure(hr, "Failed to initialize COM.");
  944. comInitialized = TRUE;
  945. hr = ThemeInitialize(pThis->_hModule);
  946. BalExitOnFailure(hr, "Failed to initialize theme manager.");
  947. hr = pThis->InitializeData();
  948. BalExitOnFailure(hr, "Failed to initialize data in bootstrapper application.");
  949. // Create main window.
  950. pThis->InitializeTaskbarButton();
  951. hr = pThis->CreateMainWindow();
  952. BalExitOnFailure(hr, "Failed to create main window.");
  953. if (FAILED(pThis->_hrFinal)) {
  954. pThis->SetState(PYBA_STATE_FAILED, hr);
  955. ::PostMessageW(pThis->_hWnd, WM_PYBA_SHOW_FAILURE, 0, 0);
  956. } else {
  957. // Okay, we're ready for packages now.
  958. pThis->SetState(PYBA_STATE_INITIALIZED, hr);
  959. ::PostMessageW(pThis->_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->_command.action ? WM_PYBA_SHOW_HELP : WM_PYBA_DETECT_PACKAGES, 0, 0);
  960. }
  961. // message pump
  962. while (0 != (ret = ::GetMessageW(&msg, nullptr, 0, 0))) {
  963. if (-1 == ret) {
  964. hr = E_UNEXPECTED;
  965. BalExitOnFailure(hr, "Unexpected return value from message pump.");
  966. } else if (!ThemeHandleKeyboardMessage(pThis->_theme, msg.hwnd, &msg)) {
  967. ::TranslateMessage(&msg);
  968. ::DispatchMessageW(&msg);
  969. }
  970. }
  971. // Succeeded thus far, check to see if anything went wrong while actually
  972. // executing changes.
  973. if (FAILED(pThis->_hrFinal)) {
  974. hr = pThis->_hrFinal;
  975. } else if (pThis->CheckCanceled()) {
  976. hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
  977. }
  978. LExit:
  979. // destroy main window
  980. pThis->DestroyMainWindow();
  981. // initiate engine shutdown
  982. DWORD dwQuit = HRESULT_CODE(hr);
  983. if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->_restartResult) {
  984. dwQuit = ERROR_SUCCESS_REBOOT_INITIATED;
  985. } else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->_restartResult) {
  986. dwQuit = ERROR_SUCCESS_REBOOT_REQUIRED;
  987. }
  988. pThis->_engine->Quit(dwQuit);
  989. ReleaseTheme(pThis->_theme);
  990. ThemeUninitialize();
  991. // uninitialize COM
  992. if (comInitialized) {
  993. ::CoUninitialize();
  994. }
  995. return hr;
  996. }
  997. //
  998. // InitializeData - initializes all the package information.
  999. //
  1000. HRESULT InitializeData() {
  1001. HRESULT hr = S_OK;
  1002. LPWSTR sczModulePath = nullptr;
  1003. IXMLDOMDocument *pixdManifest = nullptr;
  1004. hr = BalManifestLoad(_hModule, &pixdManifest);
  1005. BalExitOnFailure(hr, "Failed to load bootstrapper application manifest.");
  1006. hr = ParseOverridableVariablesFromXml(pixdManifest);
  1007. BalExitOnFailure(hr, "Failed to read overridable variables.");
  1008. hr = ProcessCommandLine(&_language);
  1009. ExitOnFailure(hr, "Unknown commandline parameters.");
  1010. hr = PathRelativeToModule(&sczModulePath, nullptr, _hModule);
  1011. BalExitOnFailure(hr, "Failed to get module path.");
  1012. hr = LoadLocalization(sczModulePath, _language);
  1013. ExitOnFailure(hr, "Failed to load localization.");
  1014. hr = LoadTheme(sczModulePath, _language);
  1015. ExitOnFailure(hr, "Failed to load theme.");
  1016. hr = BalInfoParseFromXml(&_bundle, pixdManifest);
  1017. BalExitOnFailure(hr, "Failed to load bundle information.");
  1018. hr = BalConditionsParseFromXml(&_conditions, pixdManifest, _wixLoc);
  1019. BalExitOnFailure(hr, "Failed to load conditions from XML.");
  1020. hr = LoadBootstrapperBAFunctions();
  1021. BalExitOnFailure(hr, "Failed to load bootstrapper functions.");
  1022. hr = UpdateUIStrings(_command.action);
  1023. BalExitOnFailure(hr, "Failed to load UI strings.");
  1024. GetBundleFileVersion();
  1025. // don't fail if we couldn't get the version info; best-effort only
  1026. LExit:
  1027. ReleaseObject(pixdManifest);
  1028. ReleaseStr(sczModulePath);
  1029. return hr;
  1030. }
  1031. //
  1032. // ProcessCommandLine - process the provided command line arguments.
  1033. //
  1034. HRESULT ProcessCommandLine(__inout LPWSTR* psczLanguage) {
  1035. HRESULT hr = S_OK;
  1036. int argc = 0;
  1037. LPWSTR* argv = nullptr;
  1038. LPWSTR sczVariableName = nullptr;
  1039. LPWSTR sczVariableValue = nullptr;
  1040. if (_command.wzCommandLine && *_command.wzCommandLine) {
  1041. argv = ::CommandLineToArgvW(_command.wzCommandLine, &argc);
  1042. ExitOnNullWithLastError(argv, hr, "Failed to get command line.");
  1043. for (int i = 0; i < argc; ++i) {
  1044. if (argv[i][0] == L'-' || argv[i][0] == L'/') {
  1045. if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) {
  1046. if (i + 1 >= argc) {
  1047. hr = E_INVALIDARG;
  1048. BalExitOnFailure(hr, "Must specify a language.");
  1049. }
  1050. ++i;
  1051. hr = StrAllocString(psczLanguage, &argv[i][0], 0);
  1052. BalExitOnFailure(hr, "Failed to copy language.");
  1053. }
  1054. } else if (_overridableVariables) {
  1055. int value;
  1056. const wchar_t* pwc = wcschr(argv[i], L'=');
  1057. if (pwc) {
  1058. hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
  1059. BalExitOnFailure(hr, "Failed to copy variable name.");
  1060. hr = DictKeyExists(_overridableVariables, sczVariableName);
  1061. if (E_NOTFOUND == hr) {
  1062. BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", sczVariableName);
  1063. hr = S_OK;
  1064. continue;
  1065. }
  1066. ExitOnFailure(hr, "Failed to check the dictionary of overridable variables.");
  1067. hr = StrAllocString(&sczVariableValue, ++pwc, 0);
  1068. BalExitOnFailure(hr, "Failed to copy variable value.");
  1069. if (::StrToIntEx(sczVariableValue, STIF_DEFAULT, &value)) {
  1070. hr = _engine->SetVariableNumeric(sczVariableName, value);
  1071. } else {
  1072. hr = _engine->SetVariableString(sczVariableName, sczVariableValue);
  1073. }
  1074. BalExitOnFailure(hr, "Failed to set variable.");
  1075. } else {
  1076. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);
  1077. }
  1078. }
  1079. }
  1080. }
  1081. LExit:
  1082. if (argv) {
  1083. ::LocalFree(argv);
  1084. }
  1085. ReleaseStr(sczVariableName);
  1086. ReleaseStr(sczVariableValue);
  1087. return hr;
  1088. }
  1089. HRESULT LoadLocalization(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) {
  1090. HRESULT hr = S_OK;
  1091. LPWSTR sczLocPath = nullptr;
  1092. LPCWSTR wzLocFileName = L"Default.wxl";
  1093. hr = LocProbeForFile(wzModulePath, wzLocFileName, wzLanguage, &sczLocPath);
  1094. BalExitOnFailure2(hr, "Failed to probe for loc file: %ls in path: %ls", wzLocFileName, wzModulePath);
  1095. hr = LocLoadFromFile(sczLocPath, &_wixLoc);
  1096. BalExitOnFailure1(hr, "Failed to load loc file from path: %ls", sczLocPath);
  1097. if (WIX_LOCALIZATION_LANGUAGE_NOT_SET != _wixLoc->dwLangId) {
  1098. ::SetThreadLocale(_wixLoc->dwLangId);
  1099. }
  1100. hr = StrAllocString(&_confirmCloseMessage, L"#(loc.ConfirmCancelMessage)", 0);
  1101. ExitOnFailure(hr, "Failed to initialize confirm message loc identifier.");
  1102. hr = LocLocalizeString(_wixLoc, &_confirmCloseMessage);
  1103. BalExitOnFailure1(hr, "Failed to localize confirm close message: %ls", _confirmCloseMessage);
  1104. LExit:
  1105. ReleaseStr(sczLocPath);
  1106. return hr;
  1107. }
  1108. HRESULT LoadTheme(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) {
  1109. HRESULT hr = S_OK;
  1110. LPWSTR sczThemePath = nullptr;
  1111. LPCWSTR wzThemeFileName = L"Default.thm";
  1112. LPWSTR sczCaption = nullptr;
  1113. hr = LocProbeForFile(wzModulePath, wzThemeFileName, wzLanguage, &sczThemePath);
  1114. BalExitOnFailure2(hr, "Failed to probe for theme file: %ls in path: %ls", wzThemeFileName, wzModulePath);
  1115. hr = ThemeLoadFromFile(sczThemePath, &_theme);
  1116. BalExitOnFailure1(hr, "Failed to load theme from path: %ls", sczThemePath);
  1117. hr = ThemeLocalize(_theme, _wixLoc);
  1118. BalExitOnFailure1(hr, "Failed to localize theme: %ls", sczThemePath);
  1119. // Update the caption if there are any formatted strings in it.
  1120. // If the wix developer is showing a hidden variable in the UI, then
  1121. // obviously they don't care about keeping it safe so don't go down the
  1122. // rabbit hole of making sure that this is securely freed.
  1123. hr = BalFormatString(_theme->sczCaption, &sczCaption);
  1124. if (SUCCEEDED(hr)) {
  1125. ThemeUpdateCaption(_theme, sczCaption);
  1126. }
  1127. LExit:
  1128. ReleaseStr(sczCaption);
  1129. ReleaseStr(sczThemePath);
  1130. return hr;
  1131. }
  1132. HRESULT ParseOverridableVariablesFromXml(__in IXMLDOMDocument* pixdManifest) {
  1133. HRESULT hr = S_OK;
  1134. IXMLDOMNode* pNode = nullptr;
  1135. IXMLDOMNodeList* pNodes = nullptr;
  1136. DWORD cNodes = 0;
  1137. LPWSTR scz = nullptr;
  1138. BOOL hidden = FALSE;
  1139. // get the list of variables users can override on the command line
  1140. hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes);
  1141. if (S_FALSE == hr) {
  1142. ExitFunction1(hr = S_OK);
  1143. }
  1144. ExitOnFailure(hr, "Failed to select overridable variable nodes.");
  1145. hr = pNodes->get_length((long*)&cNodes);
  1146. ExitOnFailure(hr, "Failed to get overridable variable node count.");
  1147. if (cNodes) {
  1148. hr = DictCreateStringList(&_overridableVariables, 32, DICT_FLAG_NONE);
  1149. ExitOnFailure(hr, "Failed to create the string dictionary.");
  1150. for (DWORD i = 0; i < cNodes; ++i) {
  1151. hr = XmlNextElement(pNodes, &pNode, nullptr);
  1152. ExitOnFailure(hr, "Failed to get next node.");
  1153. // @Name
  1154. hr = XmlGetAttributeEx(pNode, L"Name", &scz);
  1155. ExitOnFailure(hr, "Failed to get @Name.");
  1156. hr = XmlGetYesNoAttribute(pNode, L"Hidden", &hidden);
  1157. if (!hidden) {
  1158. hr = DictAddKey(_overridableVariables, scz);
  1159. ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", scz);
  1160. }
  1161. // prepare next iteration
  1162. ReleaseNullObject(pNode);
  1163. }
  1164. }
  1165. LExit:
  1166. ReleaseObject(pNode);
  1167. ReleaseObject(pNodes);
  1168. ReleaseStr(scz);
  1169. return hr;
  1170. }
  1171. //
  1172. // Get the file version of the bootstrapper and record in bootstrapper log file
  1173. //
  1174. HRESULT GetBundleFileVersion() {
  1175. HRESULT hr = S_OK;
  1176. ULARGE_INTEGER uliVersion = { };
  1177. LPWSTR sczCurrentPath = nullptr;
  1178. hr = PathForCurrentProcess(&sczCurrentPath, nullptr);
  1179. BalExitOnFailure(hr, "Failed to get bundle path.");
  1180. hr = FileVersion(sczCurrentPath, &uliVersion.HighPart, &uliVersion.LowPart);
  1181. BalExitOnFailure(hr, "Failed to get bundle file version.");
  1182. hr = _engine->SetVariableVersion(PYBA_VARIABLE_BUNDLE_FILE_VERSION, uliVersion.QuadPart);
  1183. BalExitOnFailure(hr, "Failed to set WixBundleFileVersion variable.");
  1184. LExit:
  1185. ReleaseStr(sczCurrentPath);
  1186. return hr;
  1187. }
  1188. //
  1189. // CreateMainWindow - creates the main install window.
  1190. //
  1191. HRESULT CreateMainWindow() {
  1192. HRESULT hr = S_OK;
  1193. HICON hIcon = reinterpret_cast<HICON>(_theme->hIcon);
  1194. WNDCLASSW wc = { };
  1195. DWORD dwWindowStyle = 0;
  1196. int x = CW_USEDEFAULT;
  1197. int y = CW_USEDEFAULT;
  1198. POINT ptCursor = { };
  1199. HMONITOR hMonitor = nullptr;
  1200. MONITORINFO mi = { };
  1201. COLORREF fg, bg;
  1202. HBRUSH bgBrush;
  1203. // If the theme did not provide an icon, try using the icon from the bundle engine.
  1204. if (!hIcon) {
  1205. HMODULE hBootstrapperEngine = ::GetModuleHandleW(nullptr);
  1206. if (hBootstrapperEngine) {
  1207. hIcon = ::LoadIconW(hBootstrapperEngine, MAKEINTRESOURCEW(1));
  1208. }
  1209. }
  1210. fg = RGB(0, 0, 0);
  1211. bg = RGB(255, 255, 255);
  1212. bgBrush = (HBRUSH)(COLOR_WINDOW+1);
  1213. if (_theme->dwFontId < _theme->cFonts) {
  1214. THEME_FONT *font = &_theme->rgFonts[_theme->dwFontId];
  1215. fg = font->crForeground;
  1216. bg = font->crBackground;
  1217. bgBrush = font->hBackground;
  1218. RemapColor(&fg, &bg, &bgBrush);
  1219. }
  1220. // Register the window class and create the window.
  1221. wc.lpfnWndProc = PythonBootstrapperApplication::WndProc;
  1222. wc.hInstance = _hModule;
  1223. wc.hIcon = hIcon;
  1224. wc.hCursor = ::LoadCursorW(nullptr, (LPCWSTR)IDC_ARROW);
  1225. wc.hbrBackground = bgBrush;
  1226. wc.lpszMenuName = nullptr;
  1227. wc.lpszClassName = PYBA_WINDOW_CLASS;
  1228. if (!::RegisterClassW(&wc)) {
  1229. ExitWithLastError(hr, "Failed to register window.");
  1230. }
  1231. _registered = TRUE;
  1232. // Calculate the window style based on the theme style and command display value.
  1233. dwWindowStyle = _theme->dwStyle;
  1234. if (BOOTSTRAPPER_DISPLAY_NONE >= _command.display) {
  1235. dwWindowStyle &= ~WS_VISIBLE;
  1236. }
  1237. // Don't show the window if there is a splash screen (it will be made visible when the splash screen is hidden)
  1238. if (::IsWindow(_command.hwndSplashScreen)) {
  1239. dwWindowStyle &= ~WS_VISIBLE;
  1240. }
  1241. // Center the window on the monitor with the mouse.
  1242. if (::GetCursorPos(&ptCursor)) {
  1243. hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST);
  1244. if (hMonitor) {
  1245. mi.cbSize = sizeof(mi);
  1246. if (::GetMonitorInfoW(hMonitor, &mi)) {
  1247. x = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - _theme->nWidth) / 2;
  1248. y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - _theme->nHeight) / 2;
  1249. }
  1250. }
  1251. }
  1252. _hWnd = ::CreateWindowExW(
  1253. 0,
  1254. wc.lpszClassName,
  1255. _theme->sczCaption,
  1256. dwWindowStyle,
  1257. x,
  1258. y,
  1259. _theme->nWidth,
  1260. _theme->nHeight,
  1261. HWND_DESKTOP,
  1262. nullptr,
  1263. _hModule,
  1264. this
  1265. );
  1266. ExitOnNullWithLastError(_hWnd, hr, "Failed to create window.");
  1267. hr = S_OK;
  1268. LExit:
  1269. return hr;
  1270. }
  1271. //
  1272. // InitializeTaskbarButton - initializes taskbar button for progress.
  1273. //
  1274. void InitializeTaskbarButton() {
  1275. HRESULT hr = S_OK;
  1276. hr = ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, __uuidof(ITaskbarList3), reinterpret_cast<LPVOID*>(&_taskbarList));
  1277. if (REGDB_E_CLASSNOTREG == hr) {
  1278. // not supported before Windows 7
  1279. ExitFunction1(hr = S_OK);
  1280. }
  1281. BalExitOnFailure(hr, "Failed to create ITaskbarList3. Continuing.");
  1282. _taskbarButtonCreatedMessage = ::RegisterWindowMessageW(L"TaskbarButtonCreated");
  1283. BalExitOnNullWithLastError(_taskbarButtonCreatedMessage, hr, "Failed to get TaskbarButtonCreated message. Continuing.");
  1284. LExit:
  1285. return;
  1286. }
  1287. //
  1288. // DestroyMainWindow - clean up all the window registration.
  1289. //
  1290. void DestroyMainWindow() {
  1291. if (::IsWindow(_hWnd)) {
  1292. ::DestroyWindow(_hWnd);
  1293. _hWnd = nullptr;
  1294. _taskbarButtonOK = FALSE;
  1295. }
  1296. if (_registered) {
  1297. ::UnregisterClassW(PYBA_WINDOW_CLASS, _hModule);
  1298. _registered = FALSE;
  1299. }
  1300. }
  1301. //
  1302. // WndProc - standard windows message handler.
  1303. //
  1304. static LRESULT CALLBACK WndProc(
  1305. __in HWND hWnd,
  1306. __in UINT uMsg,
  1307. __in WPARAM wParam,
  1308. __in LPARAM lParam
  1309. ) {
  1310. #pragma warning(suppress:4312)
  1311. auto pBA = reinterpret_cast<PythonBootstrapperApplication*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
  1312. switch (uMsg) {
  1313. case WM_NCCREATE: {
  1314. LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
  1315. pBA = reinterpret_cast<PythonBootstrapperApplication*>(lpcs->lpCreateParams);
  1316. #pragma warning(suppress:4244)
  1317. ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
  1318. break;
  1319. }
  1320. case WM_NCDESTROY: {
  1321. LRESULT lres = ThemeDefWindowProc(pBA ? pBA->_theme : nullptr, hWnd, uMsg, wParam, lParam);
  1322. ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
  1323. return lres;
  1324. }
  1325. case WM_CREATE:
  1326. if (!pBA->OnCreate(hWnd)) {
  1327. return -1;
  1328. }
  1329. break;
  1330. case WM_QUERYENDSESSION:
  1331. return IDCANCEL != pBA->OnSystemShutdown(static_cast<DWORD>(lParam), IDCANCEL);
  1332. case WM_CLOSE:
  1333. // If the user chose not to close, do *not* let the default window proc handle the message.
  1334. if (!pBA->OnClose()) {
  1335. return 0;
  1336. }
  1337. break;
  1338. case WM_DESTROY:
  1339. ::PostQuitMessage(0);
  1340. break;
  1341. case WM_PAINT: __fallthrough;
  1342. case WM_ERASEBKGND:
  1343. if (pBA && pBA->_suppressPaint) {
  1344. return TRUE;
  1345. }
  1346. break;
  1347. case WM_PYBA_SHOW_HELP:
  1348. pBA->OnShowHelp();
  1349. return 0;
  1350. case WM_PYBA_DETECT_PACKAGES:
  1351. pBA->OnDetect();
  1352. return 0;
  1353. case WM_PYBA_PLAN_PACKAGES:
  1354. pBA->OnPlan(static_cast<BOOTSTRAPPER_ACTION>(lParam));
  1355. return 0;
  1356. case WM_PYBA_APPLY_PACKAGES:
  1357. pBA->OnApply();
  1358. return 0;
  1359. case WM_PYBA_CHANGE_STATE:
  1360. pBA->OnChangeState(static_cast<PYBA_STATE>(lParam));
  1361. return 0;
  1362. case WM_PYBA_SHOW_FAILURE:
  1363. pBA->OnShowFailure();
  1364. return 0;
  1365. case WM_COMMAND:
  1366. switch (LOWORD(wParam)) {
  1367. // Customize commands
  1368. // Success/failure commands
  1369. case ID_LAUNCH_BUTTON:
  1370. pBA->OnClickLaunchButton();
  1371. return 0;
  1372. case ID_SUCCESS_RESTART_BUTTON: __fallthrough;
  1373. case ID_FAILURE_RESTART_BUTTON:
  1374. pBA->OnClickRestartButton();
  1375. return 0;
  1376. case IDCANCEL: __fallthrough;
  1377. case ID_INSTALL_CANCEL_BUTTON: __fallthrough;
  1378. case ID_CUSTOM1_CANCEL_BUTTON: __fallthrough;
  1379. case ID_CUSTOM2_CANCEL_BUTTON: __fallthrough;
  1380. case ID_MODIFY_CANCEL_BUTTON: __fallthrough;
  1381. case ID_PROGRESS_CANCEL_BUTTON: __fallthrough;
  1382. case ID_SUCCESS_CANCEL_BUTTON: __fallthrough;
  1383. case ID_FAILURE_CANCEL_BUTTON: __fallthrough;
  1384. case ID_CLOSE_BUTTON:
  1385. pBA->OnCommand(ID_CLOSE_BUTTON);
  1386. return 0;
  1387. default:
  1388. pBA->OnCommand((CONTROL_ID)LOWORD(wParam));
  1389. }
  1390. break;
  1391. case WM_NOTIFY:
  1392. if (lParam) {
  1393. LPNMHDR pnmhdr = reinterpret_cast<LPNMHDR>(lParam);
  1394. switch (pnmhdr->code) {
  1395. case NM_CLICK: __fallthrough;
  1396. case NM_RETURN:
  1397. switch (static_cast<DWORD>(pnmhdr->idFrom)) {
  1398. case ID_FAILURE_LOGFILE_LINK:
  1399. pBA->OnClickLogFileLink();
  1400. return 1;
  1401. }
  1402. }
  1403. }
  1404. break;
  1405. case WM_CTLCOLORSTATIC:
  1406. case WM_CTLCOLORBTN:
  1407. if (pBA) {
  1408. HBRUSH brush = nullptr;
  1409. if (pBA->SetControlColor((HWND)lParam, (HDC)wParam, &brush)) {
  1410. return (LRESULT)brush;
  1411. }
  1412. }
  1413. break;
  1414. }
  1415. if (pBA && pBA->_taskbarList && uMsg == pBA->_taskbarButtonCreatedMessage) {
  1416. pBA->_taskbarButtonOK = TRUE;
  1417. return 0;
  1418. }
  1419. return ThemeDefWindowProc(pBA ? pBA->_theme : nullptr, hWnd, uMsg, wParam, lParam);
  1420. }
  1421. //
  1422. // OnCreate - finishes loading the theme.
  1423. //
  1424. BOOL OnCreate(__in HWND hWnd) {
  1425. HRESULT hr = S_OK;
  1426. hr = ThemeLoadControls(_theme, hWnd, CONTROL_ID_NAMES, countof(CONTROL_ID_NAMES));
  1427. BalExitOnFailure(hr, "Failed to load theme controls.");
  1428. C_ASSERT(COUNT_PAGE == countof(PAGE_NAMES));
  1429. C_ASSERT(countof(_pageIds) == countof(PAGE_NAMES));
  1430. ThemeGetPageIds(_theme, PAGE_NAMES, _pageIds, countof(_pageIds));
  1431. // Initialize the text on all "application" (non-page) controls.
  1432. for (DWORD i = 0; i < _theme->cControls; ++i) {
  1433. THEME_CONTROL* pControl = _theme->rgControls + i;
  1434. LPWSTR text = nullptr;
  1435. LPWSTR name = nullptr;
  1436. LOC_STRING *locText = nullptr;
  1437. // If a command link has a note, then add it.
  1438. if ((pControl->dwStyle & BS_TYPEMASK) == BS_COMMANDLINK ||
  1439. (pControl->dwStyle & BS_TYPEMASK) == BS_DEFCOMMANDLINK) {
  1440. hr = StrAllocFormatted(&name, L"#(loc.%lsNote)", pControl->sczName);
  1441. if (SUCCEEDED(hr)) {
  1442. hr = LocGetString(_wixLoc, name, &locText);
  1443. ReleaseStr(name);
  1444. if (SUCCEEDED(hr) && locText && locText->wzText && locText->wzText[0]) {
  1445. hr = BalFormatString(locText->wzText, &text);
  1446. if (SUCCEEDED(hr) && text && text[0]) {
  1447. ThemeSendControlMessage(_theme, pControl->wId, BCM_SETNOTE, 0, (LPARAM)text);
  1448. ReleaseStr(text);
  1449. text = nullptr;
  1450. }
  1451. }
  1452. }
  1453. hr = S_OK;
  1454. }
  1455. if (!pControl->wPageId && pControl->sczText && *pControl->sczText) {
  1456. HRESULT hrFormat;
  1457. // If the wix developer is showing a hidden variable in the UI,
  1458. // then obviously they don't care about keeping it safe so don't
  1459. // go down the rabbit hole of making sure that this is securely
  1460. // freed.
  1461. hrFormat = BalFormatString(pControl->sczText, &text);
  1462. if (SUCCEEDED(hrFormat)) {
  1463. ThemeSetTextControl(_theme, pControl->wId, text);
  1464. ReleaseStr(text);
  1465. }
  1466. }
  1467. }
  1468. LExit:
  1469. return SUCCEEDED(hr);
  1470. }
  1471. void RemapColor(COLORREF *fg, COLORREF *bg, HBRUSH *bgBrush) {
  1472. if (*fg == RGB(0, 0, 0)) {
  1473. *fg = GetSysColor(COLOR_WINDOWTEXT);
  1474. } else if (*fg == RGB(128, 128, 128)) {
  1475. *fg = GetSysColor(COLOR_GRAYTEXT);
  1476. }
  1477. if (*bgBrush && *bg == RGB(255, 255, 255)) {
  1478. *bg = GetSysColor(COLOR_WINDOW);
  1479. *bgBrush = GetSysColorBrush(COLOR_WINDOW);
  1480. }
  1481. }
  1482. BOOL SetControlColor(HWND hWnd, HDC hDC, HBRUSH *brush) {
  1483. for (int i = 0; i < _theme->cControls; ++i) {
  1484. if (_theme->rgControls[i].hWnd != hWnd) {
  1485. continue;
  1486. }
  1487. DWORD fontId = _theme->rgControls[i].dwFontId;
  1488. if (fontId > _theme->cFonts) {
  1489. fontId = 0;
  1490. }
  1491. THEME_FONT *fnt = &_theme->rgFonts[fontId];
  1492. COLORREF fg = fnt->crForeground, bg = fnt->crBackground;
  1493. *brush = fnt->hBackground;
  1494. RemapColor(&fg, &bg, brush);
  1495. SetTextColor(hDC, fg);
  1496. SetBkColor(hDC, bg);
  1497. return TRUE;
  1498. }
  1499. return FALSE;
  1500. }
  1501. //
  1502. // OnShowFailure - display the failure page.
  1503. //
  1504. void OnShowFailure() {
  1505. SetState(PYBA_STATE_FAILED, S_OK);
  1506. // If the UI should be visible, display it now and hide the splash screen
  1507. if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
  1508. ::ShowWindow(_theme->hwndParent, SW_SHOW);
  1509. }
  1510. _engine->CloseSplashScreen();
  1511. return;
  1512. }
  1513. //
  1514. // OnShowHelp - display the help page.
  1515. //
  1516. void OnShowHelp() {
  1517. SetState(PYBA_STATE_HELP, S_OK);
  1518. // If the UI should be visible, display it now and hide the splash screen
  1519. if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
  1520. ::ShowWindow(_theme->hwndParent, SW_SHOW);
  1521. }
  1522. _engine->CloseSplashScreen();
  1523. return;
  1524. }
  1525. //
  1526. // OnDetect - start the processing of packages.
  1527. //
  1528. void OnDetect() {
  1529. HRESULT hr = S_OK;
  1530. if (_baFunction) {
  1531. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect BA function");
  1532. hr = _baFunction->OnDetect();
  1533. BalExitOnFailure(hr, "Failed calling detect BA function.");
  1534. }
  1535. SetState(PYBA_STATE_DETECTING, hr);
  1536. // If the UI should be visible, display it now and hide the splash screen
  1537. if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
  1538. ::ShowWindow(_theme->hwndParent, SW_SHOW);
  1539. }
  1540. _engine->CloseSplashScreen();
  1541. // Tell the core we're ready for the packages to be processed now.
  1542. hr = _engine->Detect();
  1543. BalExitOnFailure(hr, "Failed to start detecting chain.");
  1544. LExit:
  1545. if (FAILED(hr)) {
  1546. SetState(PYBA_STATE_DETECTING, hr);
  1547. }
  1548. return;
  1549. }
  1550. HRESULT UpdateUIStrings(__in BOOTSTRAPPER_ACTION action) {
  1551. HRESULT hr = S_OK;
  1552. LPCWSTR likeInstalling = nullptr;
  1553. LPCWSTR likeInstallation = nullptr;
  1554. switch (action) {
  1555. case BOOTSTRAPPER_ACTION_INSTALL:
  1556. likeInstalling = L"Installing";
  1557. likeInstallation = L"Installation";
  1558. break;
  1559. case BOOTSTRAPPER_ACTION_MODIFY:
  1560. // For modify, we actually want to pass INSTALL
  1561. action = BOOTSTRAPPER_ACTION_INSTALL;
  1562. likeInstalling = L"Modifying";
  1563. likeInstallation = L"Modification";
  1564. break;
  1565. case BOOTSTRAPPER_ACTION_REPAIR:
  1566. likeInstalling = L"Repairing";
  1567. likeInstallation = L"Repair";
  1568. break;
  1569. case BOOTSTRAPPER_ACTION_UNINSTALL:
  1570. likeInstalling = L"Uninstalling";
  1571. likeInstallation = L"Uninstallation";
  1572. break;
  1573. }
  1574. if (likeInstalling) {
  1575. LPWSTR locName = nullptr;
  1576. LOC_STRING *locText = nullptr;
  1577. hr = StrAllocFormatted(&locName, L"#(loc.%ls)", likeInstalling);
  1578. if (SUCCEEDED(hr)) {
  1579. hr = LocGetString(_wixLoc, locName, &locText);
  1580. ReleaseStr(locName);
  1581. }
  1582. _engine->SetVariableString(
  1583. L"ActionLikeInstalling",
  1584. SUCCEEDED(hr) && locText ? locText->wzText : likeInstalling
  1585. );
  1586. }
  1587. if (likeInstallation) {
  1588. LPWSTR locName = nullptr;
  1589. LOC_STRING *locText = nullptr;
  1590. hr = StrAllocFormatted(&locName, L"#(loc.%ls)", likeInstallation);
  1591. if (SUCCEEDED(hr)) {
  1592. hr = LocGetString(_wixLoc, locName, &locText);
  1593. ReleaseStr(locName);
  1594. }
  1595. _engine->SetVariableString(
  1596. L"ActionLikeInstallation",
  1597. SUCCEEDED(hr) && locText ? locText->wzText : likeInstallation
  1598. );
  1599. }
  1600. return hr;
  1601. }
  1602. //
  1603. // OnPlan - plan the detected changes.
  1604. //
  1605. void OnPlan(__in BOOTSTRAPPER_ACTION action) {
  1606. HRESULT hr = S_OK;
  1607. _plannedAction = action;
  1608. hr = UpdateUIStrings(action);
  1609. BalExitOnFailure(hr, "Failed to update strings");
  1610. // If we are going to apply a downgrade, bail.
  1611. if (_downgrading && BOOTSTRAPPER_ACTION_UNINSTALL < action) {
  1612. if (_suppressDowngradeFailure) {
  1613. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version of this product is installed but downgrade failure has been suppressed; continuing...");
  1614. } else {
  1615. hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
  1616. BalExitOnFailure(hr, "Cannot install a product when a newer version is installed.");
  1617. }
  1618. }
  1619. SetState(PYBA_STATE_PLANNING, hr);
  1620. if (_baFunction) {
  1621. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan BA function");
  1622. _baFunction->OnPlan();
  1623. }
  1624. hr = _engine->Plan(action);
  1625. BalExitOnFailure(hr, "Failed to start planning packages.");
  1626. LExit:
  1627. if (FAILED(hr)) {
  1628. SetState(PYBA_STATE_PLANNING, hr);
  1629. }
  1630. return;
  1631. }
  1632. //
  1633. // OnApply - apply the packages.
  1634. //
  1635. void OnApply() {
  1636. HRESULT hr = S_OK;
  1637. SetState(PYBA_STATE_APPLYING, hr);
  1638. SetProgressState(hr);
  1639. SetTaskbarButtonProgress(0);
  1640. hr = _engine->Apply(_hWnd);
  1641. BalExitOnFailure(hr, "Failed to start applying packages.");
  1642. ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, TRUE); // ensure the cancel button is enabled before starting.
  1643. LExit:
  1644. if (FAILED(hr)) {
  1645. SetState(PYBA_STATE_APPLYING, hr);
  1646. }
  1647. return;
  1648. }
  1649. //
  1650. // OnChangeState - change state.
  1651. //
  1652. void OnChangeState(__in PYBA_STATE state) {
  1653. LPWSTR unformattedText = nullptr;
  1654. _state = state;
  1655. // If our install is at the end (success or failure) and we're not showing full UI
  1656. // then exit (prompt for restart if required).
  1657. if ((PYBA_STATE_APPLIED <= _state && BOOTSTRAPPER_DISPLAY_FULL > _command.display)) {
  1658. // If a restart was required but we were not automatically allowed to
  1659. // accept the reboot then do the prompt.
  1660. if (_restartRequired && !_allowRestart) {
  1661. StrAllocFromError(&unformattedText, HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), nullptr);
  1662. _allowRestart = IDOK == ::MessageBoxW(
  1663. _hWnd,
  1664. unformattedText ? unformattedText : L"The requested operation is successful. Changes will not be effective until the system is rebooted.",
  1665. _theme->sczCaption,
  1666. MB_ICONEXCLAMATION | MB_OKCANCEL
  1667. );
  1668. }
  1669. // Quietly exit.
  1670. ::PostMessageW(_hWnd, WM_CLOSE, 0, 0);
  1671. } else { // try to change the pages.
  1672. DWORD newPageId = 0;
  1673. DeterminePageId(_state, &newPageId);
  1674. if (_visiblePageId != newPageId) {
  1675. ShowPage(newPageId);
  1676. }
  1677. }
  1678. ReleaseStr(unformattedText);
  1679. }
  1680. //
  1681. // Called before showing a page to handle all controls.
  1682. //
  1683. void ProcessPageControls(THEME_PAGE *pPage) {
  1684. if (!pPage) {
  1685. return;
  1686. }
  1687. for (DWORD i = 0; i < pPage->cControlIndices; ++i) {
  1688. THEME_CONTROL* pControl = _theme->rgControls + pPage->rgdwControlIndices[i];
  1689. // If this is a named control, try to set its default state.
  1690. if (pControl->sczName && *pControl->sczName) {
  1691. // If this is a checkable control, try to set its default state
  1692. // to the state of a matching named Burn variable.
  1693. if (IsCheckable(pControl)) {
  1694. LONGLONG llValue = 0;
  1695. HRESULT hr = BalGetNumericVariable(pControl->sczName, &llValue);
  1696. // If the control value isn't set then disable it.
  1697. if (!SUCCEEDED(hr)) {
  1698. ThemeControlEnable(_theme, pControl->wId, FALSE);
  1699. } else {
  1700. ThemeSendControlMessage(
  1701. _theme,
  1702. pControl->wId,
  1703. BM_SETCHECK,
  1704. SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED,
  1705. 0
  1706. );
  1707. }
  1708. }
  1709. // Hide or disable controls based on the control name with 'State' appended
  1710. LPWSTR controlName = nullptr;
  1711. HRESULT hr = StrAllocFormatted(&controlName, L"%lsState", pControl->sczName);
  1712. if (SUCCEEDED(hr)) {
  1713. LPWSTR controlState = nullptr;
  1714. hr = BalGetStringVariable(controlName, &controlState);
  1715. if (SUCCEEDED(hr) && controlState && *controlState) {
  1716. if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, controlState, -1, L"disable", -1)) {
  1717. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Disable control %ls", pControl->sczName);
  1718. ThemeControlEnable(_theme, pControl->wId, FALSE);
  1719. } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, controlState, -1, L"hide", -1)) {
  1720. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hide control %ls", pControl->sczName);
  1721. // TODO: This doesn't work
  1722. ThemeShowControl(_theme, pControl->wId, SW_HIDE);
  1723. }
  1724. }
  1725. StrFree(controlState);
  1726. }
  1727. StrFree(controlName);
  1728. }
  1729. // Format the text in each of the new page's controls
  1730. if (pControl->sczText && *pControl->sczText) {
  1731. // If the wix developer is showing a hidden variable
  1732. // in the UI, then obviously they don't care about
  1733. // keeping it safe so don't go down the rabbit hole
  1734. // of making sure that this is securely freed.
  1735. LPWSTR text = nullptr;
  1736. HRESULT hr = BalFormatString(pControl->sczText, &text);
  1737. if (SUCCEEDED(hr)) {
  1738. ThemeSetTextControl(_theme, pControl->wId, text);
  1739. }
  1740. }
  1741. }
  1742. }
  1743. //
  1744. // OnClose - called when the window is trying to be closed.
  1745. //
  1746. BOOL OnClose() {
  1747. BOOL close = FALSE;
  1748. // If we've already succeeded or failed or showing the help page, just close (prompts are annoying if the bootstrapper is done).
  1749. if (PYBA_STATE_APPLIED <= _state || PYBA_STATE_HELP == _state) {
  1750. close = TRUE;
  1751. } else {
  1752. // prompt the user or force the cancel if there is no UI.
  1753. close = PromptCancel(
  1754. _hWnd,
  1755. BOOTSTRAPPER_DISPLAY_FULL != _command.display,
  1756. _confirmCloseMessage ? _confirmCloseMessage : L"Are you sure you want to cancel?",
  1757. _theme->sczCaption
  1758. );
  1759. }
  1760. // If we're doing progress then we never close, we just cancel to let rollback occur.
  1761. if (PYBA_STATE_APPLYING <= _state && PYBA_STATE_APPLIED > _state) {
  1762. // If we canceled disable cancel button since clicking it again is silly.
  1763. if (close) {
  1764. ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, FALSE);
  1765. }
  1766. close = FALSE;
  1767. }
  1768. return close;
  1769. }
  1770. //
  1771. // OnClickCloseButton - close the application.
  1772. //
  1773. void OnClickCloseButton() {
  1774. ::SendMessageW(_hWnd, WM_CLOSE, 0, 0);
  1775. }
  1776. //
  1777. // OnClickLaunchButton - launch the app from the success page.
  1778. //
  1779. void OnClickLaunchButton() {
  1780. HRESULT hr = S_OK;
  1781. LPWSTR sczUnformattedLaunchTarget = nullptr;
  1782. LPWSTR sczLaunchTarget = nullptr;
  1783. LPWSTR sczLaunchTargetElevatedId = nullptr;
  1784. LPWSTR sczUnformattedArguments = nullptr;
  1785. LPWSTR sczArguments = nullptr;
  1786. int nCmdShow = SW_SHOWNORMAL;
  1787. hr = BalGetStringVariable(PYBA_VARIABLE_LAUNCH_TARGET_PATH, &sczUnformattedLaunchTarget);
  1788. BalExitOnFailure1(hr, "Failed to get launch target variable '%ls'.", PYBA_VARIABLE_LAUNCH_TARGET_PATH);
  1789. hr = BalFormatString(sczUnformattedLaunchTarget, &sczLaunchTarget);
  1790. BalExitOnFailure1(hr, "Failed to format launch target variable: %ls", sczUnformattedLaunchTarget);
  1791. if (BalStringVariableExists(PYBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID)) {
  1792. hr = BalGetStringVariable(PYBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID, &sczLaunchTargetElevatedId);
  1793. BalExitOnFailure1(hr, "Failed to get launch target elevated id '%ls'.", PYBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID);
  1794. }
  1795. if (BalStringVariableExists(PYBA_VARIABLE_LAUNCH_ARGUMENTS)) {
  1796. hr = BalGetStringVariable(PYBA_VARIABLE_LAUNCH_ARGUMENTS, &sczUnformattedArguments);
  1797. BalExitOnFailure1(hr, "Failed to get launch arguments '%ls'.", PYBA_VARIABLE_LAUNCH_ARGUMENTS);
  1798. }
  1799. if (BalStringVariableExists(PYBA_VARIABLE_LAUNCH_HIDDEN)) {
  1800. nCmdShow = SW_HIDE;
  1801. }
  1802. if (sczLaunchTargetElevatedId && !_triedToLaunchElevated) {
  1803. _triedToLaunchElevated = TRUE;
  1804. hr = _engine->LaunchApprovedExe(_hWnd, sczLaunchTargetElevatedId, sczUnformattedArguments, 0);
  1805. if (FAILED(hr)) {
  1806. BalLogError(hr, "Failed to launch elevated target: %ls", sczLaunchTargetElevatedId);
  1807. //try with ShelExec next time
  1808. OnClickLaunchButton();
  1809. }
  1810. } else {
  1811. if (sczUnformattedArguments) {
  1812. hr = BalFormatString(sczUnformattedArguments, &sczArguments);
  1813. BalExitOnFailure1(hr, "Failed to format launch arguments variable: %ls", sczUnformattedArguments);
  1814. }
  1815. hr = ShelExec(sczLaunchTarget, sczArguments, L"open", nullptr, nCmdShow, _hWnd, nullptr);
  1816. BalExitOnFailure1(hr, "Failed to launch target: %ls", sczLaunchTarget);
  1817. ::PostMessageW(_hWnd, WM_CLOSE, 0, 0);
  1818. }
  1819. LExit:
  1820. StrSecureZeroFreeString(sczArguments);
  1821. ReleaseStr(sczUnformattedArguments);
  1822. ReleaseStr(sczLaunchTargetElevatedId);
  1823. StrSecureZeroFreeString(sczLaunchTarget);
  1824. ReleaseStr(sczUnformattedLaunchTarget);
  1825. return;
  1826. }
  1827. //
  1828. // OnClickRestartButton - allows the restart and closes the app.
  1829. //
  1830. void OnClickRestartButton() {
  1831. AssertSz(_restartRequired, "Restart must be requested to be able to click on the restart button.");
  1832. _allowRestart = TRUE;
  1833. ::SendMessageW(_hWnd, WM_CLOSE, 0, 0);
  1834. return;
  1835. }
  1836. //
  1837. // OnClickLogFileLink - show the log file.
  1838. //
  1839. void OnClickLogFileLink() {
  1840. HRESULT hr = S_OK;
  1841. LPWSTR sczLogFile = nullptr;
  1842. hr = BalGetStringVariable(_bundle.sczLogVariable, &sczLogFile);
  1843. BalExitOnFailure1(hr, "Failed to get log file variable '%ls'.", _bundle.sczLogVariable);
  1844. hr = ShelExec(L"notepad.exe", sczLogFile, L"open", nullptr, SW_SHOWDEFAULT, _hWnd, nullptr);
  1845. BalExitOnFailure1(hr, "Failed to open log file target: %ls", sczLogFile);
  1846. LExit:
  1847. ReleaseStr(sczLogFile);
  1848. return;
  1849. }
  1850. //
  1851. // SetState
  1852. //
  1853. void SetState(__in PYBA_STATE state, __in HRESULT hrStatus) {
  1854. if (FAILED(hrStatus)) {
  1855. _hrFinal = hrStatus;
  1856. }
  1857. if (FAILED(_hrFinal)) {
  1858. state = PYBA_STATE_FAILED;
  1859. }
  1860. if (_state != state) {
  1861. ::PostMessageW(_hWnd, WM_PYBA_CHANGE_STATE, 0, state);
  1862. }
  1863. }
  1864. //
  1865. // GoToPage
  1866. //
  1867. void GoToPage(__in PAGE page) {
  1868. _installPage = page;
  1869. ::PostMessageW(_hWnd, WM_PYBA_CHANGE_STATE, 0, _state);
  1870. }
  1871. void DeterminePageId(__in PYBA_STATE state, __out DWORD* pdwPageId) {
  1872. LONGLONG simple;
  1873. if (BOOTSTRAPPER_DISPLAY_PASSIVE == _command.display) {
  1874. switch (state) {
  1875. case PYBA_STATE_INITIALIZED:
  1876. *pdwPageId = BOOTSTRAPPER_ACTION_HELP == _command.action
  1877. ? _pageIds[PAGE_HELP]
  1878. : _pageIds[PAGE_LOADING];
  1879. break;
  1880. case PYBA_STATE_HELP:
  1881. *pdwPageId = _pageIds[PAGE_HELP];
  1882. break;
  1883. case PYBA_STATE_DETECTING:
  1884. *pdwPageId = _pageIds[PAGE_LOADING]
  1885. ? _pageIds[PAGE_LOADING]
  1886. : _pageIds[PAGE_PROGRESS_PASSIVE]
  1887. ? _pageIds[PAGE_PROGRESS_PASSIVE]
  1888. : _pageIds[PAGE_PROGRESS];
  1889. break;
  1890. case PYBA_STATE_DETECTED: __fallthrough;
  1891. case PYBA_STATE_PLANNING: __fallthrough;
  1892. case PYBA_STATE_PLANNED: __fallthrough;
  1893. case PYBA_STATE_APPLYING: __fallthrough;
  1894. case PYBA_STATE_CACHING: __fallthrough;
  1895. case PYBA_STATE_CACHED: __fallthrough;
  1896. case PYBA_STATE_EXECUTING: __fallthrough;
  1897. case PYBA_STATE_EXECUTED:
  1898. *pdwPageId = _pageIds[PAGE_PROGRESS_PASSIVE]
  1899. ? _pageIds[PAGE_PROGRESS_PASSIVE]
  1900. : _pageIds[PAGE_PROGRESS];
  1901. break;
  1902. default:
  1903. *pdwPageId = 0;
  1904. break;
  1905. }
  1906. } else if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {
  1907. switch (state) {
  1908. case PYBA_STATE_INITIALIZING:
  1909. *pdwPageId = 0;
  1910. break;
  1911. case PYBA_STATE_INITIALIZED:
  1912. *pdwPageId = BOOTSTRAPPER_ACTION_HELP == _command.action
  1913. ? _pageIds[PAGE_HELP]
  1914. : _pageIds[PAGE_LOADING];
  1915. break;
  1916. case PYBA_STATE_HELP:
  1917. *pdwPageId = _pageIds[PAGE_HELP];
  1918. break;
  1919. case PYBA_STATE_DETECTING:
  1920. *pdwPageId = _pageIds[PAGE_LOADING];
  1921. break;
  1922. case PYBA_STATE_DETECTED:
  1923. if (_installPage == PAGE_LOADING) {
  1924. switch (_command.action) {
  1925. case BOOTSTRAPPER_ACTION_INSTALL:
  1926. if (SUCCEEDED(BalGetNumericVariable(L"SimpleInstall", &simple)) && simple) {
  1927. _installPage = PAGE_SIMPLE_INSTALL;
  1928. } else {
  1929. _installPage = PAGE_INSTALL;
  1930. }
  1931. break;
  1932. case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough;
  1933. case BOOTSTRAPPER_ACTION_REPAIR: __fallthrough;
  1934. case BOOTSTRAPPER_ACTION_UNINSTALL:
  1935. _installPage = PAGE_MODIFY;
  1936. break;
  1937. }
  1938. }
  1939. *pdwPageId = _pageIds[_installPage];
  1940. break;
  1941. case PYBA_STATE_PLANNING: __fallthrough;
  1942. case PYBA_STATE_PLANNED: __fallthrough;
  1943. case PYBA_STATE_APPLYING: __fallthrough;
  1944. case PYBA_STATE_CACHING: __fallthrough;
  1945. case PYBA_STATE_CACHED: __fallthrough;
  1946. case PYBA_STATE_EXECUTING: __fallthrough;
  1947. case PYBA_STATE_EXECUTED:
  1948. *pdwPageId = _pageIds[PAGE_PROGRESS];
  1949. break;
  1950. case PYBA_STATE_APPLIED:
  1951. *pdwPageId = _pageIds[PAGE_SUCCESS];
  1952. break;
  1953. case PYBA_STATE_FAILED:
  1954. *pdwPageId = _pageIds[PAGE_FAILURE];
  1955. break;
  1956. }
  1957. }
  1958. }
  1959. BOOL IsCrtInstalled() {
  1960. if (_crtInstalledToken > 0) {
  1961. return TRUE;
  1962. } else if (_crtInstalledToken == 0) {
  1963. return FALSE;
  1964. }
  1965. // Check whether at least CRT v10.0.9924.0 is available.
  1966. // It should only be installed as a Windows Update package, which means
  1967. // we don't need to worry about 32-bit/64-bit.
  1968. LPCWSTR crtFile = L"api-ms-win-crt-runtime-l1-1-0.dll";
  1969. DWORD cbVer = GetFileVersionInfoSizeW(crtFile, nullptr);
  1970. if (!cbVer) {
  1971. _crtInstalledToken = 0;
  1972. return FALSE;
  1973. }
  1974. void *pData = malloc(cbVer);
  1975. if (!pData) {
  1976. _crtInstalledToken = 0;
  1977. return FALSE;
  1978. }
  1979. if (!GetFileVersionInfoW(crtFile, 0, cbVer, pData)) {
  1980. free(pData);
  1981. _crtInstalledToken = 0;
  1982. return FALSE;
  1983. }
  1984. VS_FIXEDFILEINFO *ffi;
  1985. UINT cb;
  1986. BOOL result = FALSE;
  1987. if (VerQueryValueW(pData, L"\\", (LPVOID*)&ffi, &cb) &&
  1988. ffi->dwFileVersionMS == 0x000A0000 && ffi->dwFileVersionLS >= 0x26C40000) {
  1989. result = TRUE;
  1990. }
  1991. free(pData);
  1992. _crtInstalledToken = result ? 1 : 0;
  1993. return result;
  1994. }
  1995. BOOL QueryElevateForCrtInstall() {
  1996. // Called to prompt the user that even though they think they won't need
  1997. // to elevate, they actually will because of the CRT install.
  1998. if (IsCrtInstalled()) {
  1999. // CRT is already installed - no need to prompt
  2000. return TRUE;
  2001. }
  2002. LONGLONG elevated;
  2003. HRESULT hr = BalGetNumericVariable(L"WixBundleElevated", &elevated);
  2004. if (SUCCEEDED(hr) && elevated) {
  2005. // Already elevated - no need to prompt
  2006. return TRUE;
  2007. }
  2008. LOC_STRING *locStr;
  2009. hr = LocGetString(_wixLoc, L"#(loc.ElevateForCRTInstall)", &locStr);
  2010. if (FAILED(hr)) {
  2011. BalLogError(hr, "Failed to get ElevateForCRTInstall string");
  2012. return FALSE;
  2013. }
  2014. return ::MessageBoxW(_hWnd, locStr->wzText, _theme->sczCaption, MB_YESNO) != IDNO;
  2015. }
  2016. HRESULT EvaluateConditions() {
  2017. HRESULT hr = S_OK;
  2018. BOOL result = FALSE;
  2019. for (DWORD i = 0; i < _conditions.cConditions; ++i) {
  2020. BAL_CONDITION* pCondition = _conditions.rgConditions + i;
  2021. hr = BalConditionEvaluate(pCondition, _engine, &result, &_failedMessage);
  2022. BalExitOnFailure(hr, "Failed to evaluate condition.");
  2023. if (!result) {
  2024. // Hope they didn't have hidden variables in their message, because it's going in the log in plaintext.
  2025. BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "%ls", _failedMessage);
  2026. hr = E_WIXSTDBA_CONDITION_FAILED;
  2027. // todo: remove in WiX v4, in case people are relying on v3.x logging behavior
  2028. BalExitOnFailure1(hr, "Bundle condition evaluated to false: %ls", pCondition->sczCondition);
  2029. }
  2030. }
  2031. ReleaseNullStrSecure(_failedMessage);
  2032. LExit:
  2033. return hr;
  2034. }
  2035. void SetTaskbarButtonProgress(__in DWORD dwOverallPercentage) {
  2036. HRESULT hr = S_OK;
  2037. if (_taskbarButtonOK) {
  2038. hr = _taskbarList->SetProgressValue(_hWnd, dwOverallPercentage, 100UL);
  2039. BalExitOnFailure1(hr, "Failed to set taskbar button progress to: %d%%.", dwOverallPercentage);
  2040. }
  2041. LExit:
  2042. return;
  2043. }
  2044. void SetTaskbarButtonState(__in TBPFLAG tbpFlags) {
  2045. HRESULT hr = S_OK;
  2046. if (_taskbarButtonOK) {
  2047. hr = _taskbarList->SetProgressState(_hWnd, tbpFlags);
  2048. BalExitOnFailure1(hr, "Failed to set taskbar button state.", tbpFlags);
  2049. }
  2050. LExit:
  2051. return;
  2052. }
  2053. void SetProgressState(__in HRESULT hrStatus) {
  2054. TBPFLAG flag = TBPF_NORMAL;
  2055. if (IsCanceled() || HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hrStatus) {
  2056. flag = TBPF_PAUSED;
  2057. } else if (IsRollingBack() || FAILED(hrStatus)) {
  2058. flag = TBPF_ERROR;
  2059. }
  2060. SetTaskbarButtonState(flag);
  2061. }
  2062. HRESULT LoadBootstrapperBAFunctions() {
  2063. HRESULT hr = S_OK;
  2064. LPWSTR sczBafPath = nullptr;
  2065. hr = PathRelativeToModule(&sczBafPath, L"bafunctions.dll", _hModule);
  2066. BalExitOnFailure(hr, "Failed to get path to BA function DLL.");
  2067. #ifdef DEBUG
  2068. BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: LoadBootstrapperBAFunctions() - BA function DLL %ls", sczBafPath);
  2069. #endif
  2070. _hBAFModule = ::LoadLibraryW(sczBafPath);
  2071. if (_hBAFModule) {
  2072. auto pfnBAFunctionCreate = reinterpret_cast<PFN_BOOTSTRAPPER_BA_FUNCTION_CREATE>(::GetProcAddress(_hBAFModule, "CreateBootstrapperBAFunction"));
  2073. BalExitOnNullWithLastError1(pfnBAFunctionCreate, hr, "Failed to get CreateBootstrapperBAFunction entry-point from: %ls", sczBafPath);
  2074. hr = pfnBAFunctionCreate(_engine, _hBAFModule, &_baFunction);
  2075. BalExitOnFailure(hr, "Failed to create BA function.");
  2076. }
  2077. #ifdef DEBUG
  2078. else {
  2079. BalLogError(HRESULT_FROM_WIN32(::GetLastError()), "PYBA: LoadBootstrapperBAFunctions() - Failed to load DLL %ls", sczBafPath);
  2080. }
  2081. #endif
  2082. LExit:
  2083. if (_hBAFModule && !_baFunction) {
  2084. ::FreeLibrary(_hBAFModule);
  2085. _hBAFModule = nullptr;
  2086. }
  2087. ReleaseStr(sczBafPath);
  2088. return hr;
  2089. }
  2090. BOOL IsCheckable(THEME_CONTROL* pControl) {
  2091. if (!pControl->sczName || !pControl->sczName[0]) {
  2092. return FALSE;
  2093. }
  2094. if (pControl->type == THEME_CONTROL_TYPE_CHECKBOX) {
  2095. return TRUE;
  2096. }
  2097. if (pControl->type == THEME_CONTROL_TYPE_BUTTON) {
  2098. if ((pControl->dwStyle & BS_TYPEMASK) == BS_AUTORADIOBUTTON) {
  2099. return TRUE;
  2100. }
  2101. }
  2102. return FALSE;
  2103. }
  2104. void SavePageSettings() {
  2105. DWORD pageId = 0;
  2106. THEME_PAGE* pPage = nullptr;
  2107. DeterminePageId(_state, &pageId);
  2108. pPage = ThemeGetPage(_theme, pageId);
  2109. if (!pPage) {
  2110. return;
  2111. }
  2112. for (DWORD i = 0; i < pPage->cControlIndices; ++i) {
  2113. // Loop through all the checkable controls and set a Burn variable
  2114. // with that name to true or false.
  2115. THEME_CONTROL* pControl = _theme->rgControls + pPage->rgdwControlIndices[i];
  2116. if (IsCheckable(pControl) && ThemeControlEnabled(_theme, pControl->wId)) {
  2117. BOOL checked = ThemeIsControlChecked(_theme, pControl->wId);
  2118. _engine->SetVariableNumeric(pControl->sczName, checked ? 1 : 0);
  2119. }
  2120. // Loop through all the editbox controls with names and set a
  2121. // Burn variable with that name to the contents.
  2122. if (THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) {
  2123. LPWSTR sczValue = nullptr;
  2124. ThemeGetTextControl(_theme, pControl->wId, &sczValue);
  2125. _engine->SetVariableString(pControl->sczName, sczValue);
  2126. }
  2127. }
  2128. }
  2129. public:
  2130. //
  2131. // Constructor - initialize member variables.
  2132. //
  2133. PythonBootstrapperApplication(
  2134. __in HMODULE hModule,
  2135. __in BOOL fPrereq,
  2136. __in HRESULT hrHostInitialization,
  2137. __in IBootstrapperEngine* pEngine,
  2138. __in const BOOTSTRAPPER_COMMAND* pCommand
  2139. ) : CBalBaseBootstrapperApplication(pEngine, pCommand, 3, 3000) {
  2140. _hModule = hModule;
  2141. memcpy_s(&_command, sizeof(_command), pCommand, sizeof(BOOTSTRAPPER_COMMAND));
  2142. LONGLONG llInstalled = 0;
  2143. HRESULT hr = BalGetNumericVariable(L"WixBundleInstalled", &llInstalled);
  2144. if (SUCCEEDED(hr) && BOOTSTRAPPER_RESUME_TYPE_REBOOT != _command.resumeType && 0 < llInstalled && BOOTSTRAPPER_ACTION_INSTALL == _command.action) {
  2145. _command.action = BOOTSTRAPPER_ACTION_MODIFY;
  2146. } else if (0 == llInstalled && (BOOTSTRAPPER_ACTION_MODIFY == _command.action || BOOTSTRAPPER_ACTION_REPAIR == _command.action)) {
  2147. _command.action = BOOTSTRAPPER_ACTION_INSTALL;
  2148. }
  2149. _plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN;
  2150. // When resuming from restart doing some install-like operation, try to find the package that forced the
  2151. // restart. We'll use this information during planning.
  2152. _nextPackageAfterRestart = nullptr;
  2153. if (BOOTSTRAPPER_RESUME_TYPE_REBOOT == _command.resumeType && BOOTSTRAPPER_ACTION_UNINSTALL < _command.action) {
  2154. // Ensure the forced restart package variable is null when it is an empty string.
  2155. HRESULT hr = BalGetStringVariable(L"WixBundleForcedRestartPackage", &_nextPackageAfterRestart);
  2156. if (FAILED(hr) || !_nextPackageAfterRestart || !*_nextPackageAfterRestart) {
  2157. ReleaseNullStr(_nextPackageAfterRestart);
  2158. }
  2159. }
  2160. _crtInstalledToken = -1;
  2161. pEngine->SetVariableNumeric(L"CRTInstalled", IsCrtInstalled() ? 1 : 0);
  2162. _wixLoc = nullptr;
  2163. memset(&_bundle, 0, sizeof(_bundle));
  2164. memset(&_conditions, 0, sizeof(_conditions));
  2165. _confirmCloseMessage = nullptr;
  2166. _failedMessage = nullptr;
  2167. _language = nullptr;
  2168. _theme = nullptr;
  2169. memset(_pageIds, 0, sizeof(_pageIds));
  2170. _hUiThread = nullptr;
  2171. _registered = FALSE;
  2172. _hWnd = nullptr;
  2173. _state = PYBA_STATE_INITIALIZING;
  2174. _visiblePageId = 0;
  2175. _installPage = PAGE_LOADING;
  2176. _hrFinal = hrHostInitialization;
  2177. _downgrading = FALSE;
  2178. _restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
  2179. _restartRequired = FALSE;
  2180. _allowRestart = FALSE;
  2181. _suppressDowngradeFailure = FALSE;
  2182. _suppressRepair = FALSE;
  2183. _modifying = FALSE;
  2184. _overridableVariables = nullptr;
  2185. _taskbarList = nullptr;
  2186. _taskbarButtonCreatedMessage = UINT_MAX;
  2187. _taskbarButtonOK = FALSE;
  2188. _showingInternalUIThisPackage = FALSE;
  2189. _triedToLaunchElevated = FALSE;
  2190. _suppressPaint = FALSE;
  2191. pEngine->AddRef();
  2192. _engine = pEngine;
  2193. _hBAFModule = nullptr;
  2194. _baFunction = nullptr;
  2195. }
  2196. //
  2197. // Destructor - release member variables.
  2198. //
  2199. ~PythonBootstrapperApplication() {
  2200. AssertSz(!::IsWindow(_hWnd), "Window should have been destroyed before destructor.");
  2201. AssertSz(!_theme, "Theme should have been released before destructor.");
  2202. ReleaseObject(_taskbarList);
  2203. ReleaseDict(_overridableVariables);
  2204. ReleaseStr(_failedMessage);
  2205. ReleaseStr(_confirmCloseMessage);
  2206. BalConditionsUninitialize(&_conditions);
  2207. BalInfoUninitialize(&_bundle);
  2208. LocFree(_wixLoc);
  2209. ReleaseStr(_language);
  2210. ReleaseStr(_nextPackageAfterRestart);
  2211. ReleaseNullObject(_engine);
  2212. if (_hBAFModule) {
  2213. ::FreeLibrary(_hBAFModule);
  2214. _hBAFModule = nullptr;
  2215. }
  2216. }
  2217. private:
  2218. HMODULE _hModule;
  2219. BOOTSTRAPPER_COMMAND _command;
  2220. IBootstrapperEngine* _engine;
  2221. BOOTSTRAPPER_ACTION _plannedAction;
  2222. LPWSTR _nextPackageAfterRestart;
  2223. WIX_LOCALIZATION* _wixLoc;
  2224. BAL_INFO_BUNDLE _bundle;
  2225. BAL_CONDITIONS _conditions;
  2226. LPWSTR _failedMessage;
  2227. LPWSTR _confirmCloseMessage;
  2228. LPWSTR _language;
  2229. THEME* _theme;
  2230. DWORD _pageIds[countof(PAGE_NAMES)];
  2231. HANDLE _hUiThread;
  2232. BOOL _registered;
  2233. HWND _hWnd;
  2234. PYBA_STATE _state;
  2235. HRESULT _hrFinal;
  2236. DWORD _visiblePageId;
  2237. PAGE _installPage;
  2238. BOOL _startedExecution;
  2239. DWORD _calculatedCacheProgress;
  2240. DWORD _calculatedExecuteProgress;
  2241. BOOL _downgrading;
  2242. BOOTSTRAPPER_APPLY_RESTART _restartResult;
  2243. BOOL _restartRequired;
  2244. BOOL _allowRestart;
  2245. BOOL _suppressDowngradeFailure;
  2246. BOOL _suppressRepair;
  2247. BOOL _modifying;
  2248. int _crtInstalledToken;
  2249. STRINGDICT_HANDLE _overridableVariables;
  2250. ITaskbarList3* _taskbarList;
  2251. UINT _taskbarButtonCreatedMessage;
  2252. BOOL _taskbarButtonOK;
  2253. BOOL _showingInternalUIThisPackage;
  2254. BOOL _triedToLaunchElevated;
  2255. BOOL _suppressPaint;
  2256. HMODULE _hBAFModule;
  2257. IBootstrapperBAFunction* _baFunction;
  2258. };
  2259. //
  2260. // CreateBootstrapperApplication - creates a new IBootstrapperApplication object.
  2261. //
  2262. HRESULT CreateBootstrapperApplication(
  2263. __in HMODULE hModule,
  2264. __in BOOL fPrereq,
  2265. __in HRESULT hrHostInitialization,
  2266. __in IBootstrapperEngine* pEngine,
  2267. __in const BOOTSTRAPPER_COMMAND* pCommand,
  2268. __out IBootstrapperApplication** ppApplication
  2269. ) {
  2270. HRESULT hr = S_OK;
  2271. if (fPrereq) {
  2272. hr = E_INVALIDARG;
  2273. ExitWithLastError(hr, "Failed to create UI thread.");
  2274. }
  2275. PythonBootstrapperApplication* pApplication = nullptr;
  2276. pApplication = new PythonBootstrapperApplication(hModule, fPrereq, hrHostInitialization, pEngine, pCommand);
  2277. ExitOnNull(pApplication, hr, E_OUTOFMEMORY, "Failed to create new standard bootstrapper application object.");
  2278. *ppApplication = pApplication;
  2279. pApplication = nullptr;
  2280. LExit:
  2281. ReleaseObject(pApplication);
  2282. return hr;
  2283. }