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.

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