main
cpp 884 lines 24.5 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 static const LPCWSTR WIXIUIBA_WINDOW_CLASS = L"WixInternalUIBA";
6
7 enum WM_WIXIUIBA
8 {
9 WM_WIXIUIBA_DETECT_PACKAGES = WM_APP + 100,
10 WM_WIXIUIBA_PLAN_PACKAGES,
11 WM_WIXIUIBA_APPLY_PACKAGES,
12 WM_WIXIUIBA_DETECT_FOR_CLEANUP,
13 WM_WIXIUIBA_PLAN_PACKAGES_FOR_CLEANUP,
14 };
15
16
17 class CWixInternalUIBootstrapperApplication : public CBootstrapperApplicationBase
18 {
19 public: // IBootstrapperApplication
20 virtual STDMETHODIMP OnCreate(
21 __in IBootstrapperEngine* pEngine,
22 __in BOOTSTRAPPER_COMMAND* pCommand
23 )
24 {
25 HRESULT hr = S_OK;
26
27 hr = __super::OnCreate(pEngine, pCommand);
28 BalExitOnFailure(hr, "CBootstrapperApplicationBase initialization failed.");
29
30 m_commandAction = pCommand->action;
31 m_commandDisplay = pCommand->display;
32
33 hr = InitializeData();
34 BalExitOnFailure(hr, "Failed to initialize data in internal UI bootstrapper application.");
35
36 LExit:
37 return hr;
38 }
39
40 virtual STDMETHODIMP OnStartup()
41 {
42 HRESULT hr = S_OK;
43 DWORD dwUIThreadId = 0;
44
45 // create UI thread
46 m_hUiThread = ::CreateThread(NULL, 0, UiThreadProc, this, 0, &dwUIThreadId);
47 if (!m_hUiThread)
48 {
49 BalExitWithLastError(hr, "Failed to create UI thread.");
50 }
51
52 LExit:
53 return hr;
54 }
55
56
57 virtual STDMETHODIMP OnShutdown(
58 __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
59 )
60 {
61 // wait for UI thread to terminate
62 if (m_hUiThread)
63 {
64 ::WaitForSingleObject(m_hUiThread, INFINITE);
65 ReleaseHandle(m_hUiThread);
66 }
67
68 if (m_fFailedToLoadPackage)
69 {
70 Assert(FAILED(m_hrFinal));
71 // TODO: Should we really do what this error message says? Going back to the prereq BA
72 // to show the error dialog is pretty overkill vs. showing an error dialog in this BA.
73 BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load primary package as the BA. The bootstrapper application will be reloaded to show the error.");
74 *pAction = BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER;
75 }
76
77 return S_OK;
78 }
79
80
81 virtual STDMETHODIMP OnDetectPackageComplete(
82 __in_z LPCWSTR wzPackageId,
83 __in HRESULT hrStatus,
84 __in BOOTSTRAPPER_PACKAGE_STATE state,
85 __in BOOL fCached
86 )
87 {
88 BAL_INFO_PACKAGE* pPackage = NULL;
89
90 if (SUCCEEDED(hrStatus) && SUCCEEDED(BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage)) &&
91 BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT == pPackage->primaryPackageType)
92 {
93 BOOL fInstalled = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < state;
94
95 // Maybe modify the action state if the primary package is or is not already installed.
96 if (fInstalled && BOOTSTRAPPER_ACTION_INSTALL == m_commandAction)
97 {
98 m_commandAction = BOOTSTRAPPER_ACTION_MODIFY;
99 }
100 else if (!fInstalled && (BOOTSTRAPPER_ACTION_MODIFY == m_commandAction || BOOTSTRAPPER_ACTION_REPAIR == m_commandAction))
101 {
102 m_commandAction = BOOTSTRAPPER_ACTION_INSTALL;
103 }
104
105 if (m_fApplied && !fInstalled && fCached)
106 {
107 m_fAutomaticRemoval = TRUE;
108 }
109 }
110
111 return __super::OnDetectPackageComplete(wzPackageId, hrStatus, state, fCached);
112 }
113
114
115 virtual STDMETHODIMP OnDetectComplete(
116 __in HRESULT hrStatus,
117 __in BOOL fEligibleForCleanup
118 )
119 {
120 if (m_fAutomaticRemoval && SUCCEEDED(hrStatus))
121 {
122 ::PostMessageW(m_hWnd, WM_WIXIUIBA_PLAN_PACKAGES_FOR_CLEANUP, 0, BOOTSTRAPPER_ACTION_UNINSTALL);
123 ExitFunction();
124 }
125 else if (m_fApplied)
126 {
127 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
128 ExitFunction();
129 }
130
131 // If we're performing an action that modifies machine state then evaluate conditions.
132 BOOL fEvaluateConditions = SUCCEEDED(hrStatus) &&
133 (BOOTSTRAPPER_ACTION_LAYOUT < m_commandAction && BOOTSTRAPPER_ACTION_UPDATE_REPLACE > m_commandAction);
134
135 if (fEvaluateConditions)
136 {
137 hrStatus = EvaluateConditions();
138 }
139
140 if (SUCCEEDED(hrStatus))
141 {
142 ::PostMessageW(m_hWnd, WM_WIXIUIBA_PLAN_PACKAGES, 0, m_commandAction);
143 }
144 else
145 {
146 SetLoadPackageFailure(hrStatus);
147 }
148
149 LExit:
150 return __super::OnDetectComplete(hrStatus, fEligibleForCleanup);
151 }
152
153
154 virtual STDMETHODIMP OnPlanPackageBegin(
155 __in_z LPCWSTR wzPackageId,
156 __in BOOTSTRAPPER_PACKAGE_STATE state,
157 __in BOOL fCached,
158 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition,
159 __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition,
160 __in BOOTSTRAPPER_REQUEST_STATE recommendedState,
161 __in BOOTSTRAPPER_CACHE_TYPE recommendedCacheType,
162 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState,
163 __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType,
164 __inout BOOL* pfCancel
165 )
166 {
167 HRESULT hr = S_OK;
168 BAL_INFO_PACKAGE* pPackage = NULL;
169
170 hr = BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage);
171 if (FAILED(hr))
172 {
173 // Non-chain package, keep default.
174 }
175 else if (BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT != pPackage->primaryPackageType)
176 {
177 // Only the primary package should be cached or executed.
178 if (BOOTSTRAPPER_CACHE_TYPE_FORCE == *pRequestedCacheType)
179 {
180 *pRequestedCacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP;
181 }
182
183 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
184 }
185 else if (BOOTSTRAPPER_DISPLAY_FULL == m_commandDisplay && !m_fAutomaticRemoval)
186 {
187 // Make sure the MSI UI is shown regardless of the current state of the package.
188 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT;
189 }
190
191 return __super::OnPlanPackageBegin(wzPackageId, state, fCached, installCondition, repairCondition, recommendedState, recommendedCacheType, pRequestState, pRequestedCacheType, pfCancel);
192 }
193
194
195 virtual STDMETHODIMP OnPlanMsiPackage(
196 __in_z LPCWSTR wzPackageId,
197 __in BOOL fExecute,
198 __in BOOTSTRAPPER_ACTION_STATE action,
199 __in BOOTSTRAPPER_MSI_FILE_VERSIONING recommendedFileVersioning,
200 __inout BOOL* pfCancel,
201 __inout BURN_MSI_PROPERTY* pActionMsiProperty,
202 __inout INSTALLUILEVEL* pUiLevel,
203 __inout BOOL* pfDisableExternalUiHandler,
204 __inout BOOTSTRAPPER_MSI_FILE_VERSIONING* pFileVersioning
205 )
206 {
207 INSTALLUILEVEL uiLevel = INSTALLUILEVEL_NOCHANGE;
208
209 if (m_fAutomaticRemoval)
210 {
211 ExitFunction();
212 }
213
214 switch (m_commandDisplay)
215 {
216 case BOOTSTRAPPER_DISPLAY_FULL:
217 uiLevel = INSTALLUILEVEL_FULL;
218 break;
219
220 case BOOTSTRAPPER_DISPLAY_PASSIVE:
221 uiLevel = INSTALLUILEVEL_REDUCED;
222 break;
223 }
224
225 if (INSTALLUILEVEL_NOCHANGE != uiLevel)
226 {
227 *pUiLevel = uiLevel;
228 }
229
230 *pActionMsiProperty = BURN_MSI_PROPERTY_NONE;
231 *pfDisableExternalUiHandler = TRUE;
232
233 LExit:
234 return __super::OnPlanMsiPackage(wzPackageId, fExecute, action, recommendedFileVersioning, pfCancel, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler, pFileVersioning);
235 }
236
237
238 virtual STDMETHODIMP OnPlanComplete(
239 __in HRESULT hrStatus
240 )
241 {
242 if (SUCCEEDED(hrStatus))
243 {
244 ::PostMessageW(m_hWnd, WM_WIXIUIBA_APPLY_PACKAGES, 0, 0);
245 }
246 else if (m_fAutomaticRemoval)
247 {
248 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
249 }
250 else
251 {
252 SetLoadPackageFailure(hrStatus);
253 }
254
255 return __super::OnPlanComplete(hrStatus);
256 }
257
258
259 virtual STDMETHODIMP OnApplyBegin(
260 __in DWORD dwPhaseCount,
261 __inout BOOL* pfCancel
262 )
263 {
264 m_fApplying = TRUE;
265 return __super::OnApplyBegin(dwPhaseCount, pfCancel);
266 }
267
268
269 virtual STDMETHODIMP OnCacheComplete(
270 __in HRESULT hrStatus
271 )
272 {
273 if (FAILED(hrStatus) && !m_fAutomaticRemoval)
274 {
275 SetLoadPackageFailure(hrStatus);
276 }
277
278 return __super::OnCacheComplete(hrStatus);
279 }
280
281
282 virtual STDMETHODIMP OnExecuteBegin(
283 __in DWORD cExecutingPackages,
284 __in BOOL* pfCancel
285 )
286 {
287 m_pEngine->CloseSplashScreen();
288
289 return __super::OnExecuteBegin(cExecutingPackages, pfCancel);
290 }
291
292
293 virtual STDMETHODIMP OnApplyComplete(
294 __in HRESULT hrStatus,
295 __in BOOTSTRAPPER_APPLY_RESTART restart,
296 __in BOOTSTRAPPER_APPLYCOMPLETE_ACTION recommendation,
297 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
298 )
299 {
300 HRESULT hr = __super::OnApplyComplete(hrStatus, restart, recommendation, pAction);
301
302 *pAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
303 m_fApplying = FALSE;
304
305 if (m_fAutomaticRemoval)
306 {
307 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
308 }
309 else
310 {
311 m_restartResult = restart; // remember the restart result so we return the correct error code.
312 m_fApplied = TRUE;
313
314 if (FAILED(hrStatus))
315 {
316 m_hrFinal = hrStatus;
317 }
318
319 ::PostMessageW(m_hWnd, WM_WIXIUIBA_DETECT_FOR_CLEANUP, 0, 0);
320 }
321
322 return hr;
323 }
324
325
326 private:
327 //
328 // UiThreadProc - entrypoint for UI thread.
329 //
330 static DWORD WINAPI UiThreadProc(
331 __in LPVOID pvContext
332 )
333 {
334 HRESULT hr = S_OK;
335 CWixInternalUIBootstrapperApplication* pThis = (CWixInternalUIBootstrapperApplication*)pvContext;
336 BOOL fComInitialized = FALSE;
337 BOOL fRet = FALSE;
338 MSG msg = { };
339 DWORD dwQuit = 0;
340
341 // Initialize COM.
342 hr = ::CoInitialize(NULL);
343 BalExitOnFailure(hr, "Failed to initialize COM.");
344 fComInitialized = TRUE;
345
346 // Create main window.
347 hr = pThis->CreateMainWindow();
348 BalExitOnFailure(hr, "Failed to create internal UI bootstrapper application main window.");
349
350 ::PostMessageW(pThis->m_hWnd, WM_WIXIUIBA_DETECT_PACKAGES, 0, 0);
351
352 // message pump
353 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
354 {
355 if (-1 == fRet)
356 {
357 hr = E_UNEXPECTED;
358 BalExitOnFailure(hr, "Unexpected return value from message pump.");
359 }
360 else if (!::IsDialogMessageW(pThis->m_hWnd, &msg))
361 {
362 ::TranslateMessage(&msg);
363 ::DispatchMessageW(&msg);
364 }
365 }
366
367 // Succeeded thus far, check to see if anything went wrong while actually
368 // executing changes.
369 if (FAILED(pThis->m_hrFinal))
370 {
371 hr = pThis->m_hrFinal;
372 }
373 else if (pThis->CheckCanceled())
374 {
375 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
376 }
377
378 LExit:
379 // destroy main window
380 pThis->DestroyMainWindow();
381
382 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->m_restartResult)
383 {
384 dwQuit = SUCCEEDED(hr) ? ERROR_SUCCESS_REBOOT_INITIATED : ERROR_FAIL_REBOOT_INITIATED;
385 }
386 else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->m_restartResult)
387 {
388 dwQuit = SUCCEEDED(hr) ? ERROR_SUCCESS_REBOOT_REQUIRED : ERROR_FAIL_REBOOT_REQUIRED;
389 }
390 else if (SEVERITY_ERROR == HRESULT_SEVERITY(hr) && FACILITY_WIN32 == HRESULT_FACILITY(hr))
391 {
392 // Convert Win32 HRESULTs back to the error code.
393 dwQuit = HRESULT_CODE(hr);
394 }
395 else
396 {
397 dwQuit = hr;
398 }
399
400 // initiate engine shutdown
401 pThis->m_pEngine->Quit(dwQuit);
402
403 // uninitialize COM
404 if (fComInitialized)
405 {
406 ::CoUninitialize();
407 }
408
409 return hr;
410 }
411
412
413 //
414 // InitializeData - initializes all the package and prerequisite information.
415 //
416 HRESULT InitializeData()
417 {
418 HRESULT hr = S_OK;
419 IXMLDOMDocument* pixdManifest = NULL;
420
421 hr = XmlInitialize();
422 BalExitOnFailure(hr, "Failed to initialize XML.");
423
424 hr = BalManifestLoad(m_hModule, &pixdManifest);
425 BalExitOnFailure(hr, "Failed to load bootstrapper application manifest.");
426
427 hr = BalInfoParseFromXml(&m_Bundle, pixdManifest);
428 BalExitOnFailure(hr, "Failed to load bundle information.");
429
430 hr = EnsureSinglePrimaryPackage();
431 BalExitOnFailure(hr, "Failed to ensure single primary package.");
432
433 hr = ProcessCommandLine();
434 ExitOnFailure(hr, "Unknown commandline parameters.");
435
436 hr = BalConditionsParseFromXml(&m_Conditions, pixdManifest, NULL);
437 BalExitOnFailure(hr, "Failed to load conditions from XML.");
438
439 LExit:
440 ReleaseObject(pixdManifest);
441
442 return hr;
443 }
444
445
446 //
447 // ProcessCommandLine - process the provided command line arguments.
448 //
449 HRESULT ProcessCommandLine()
450 {
451 HRESULT hr = S_OK;
452 int argc = 0;
453 LPWSTR* argv = NULL;
454
455 argc = m_BalInfoCommand.cUnknownArgs;
456 argv = m_BalInfoCommand.rgUnknownArgs;
457
458 for (int i = 0; i < argc; ++i)
459 {
460 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);
461 }
462
463 hr = BalSetOverridableVariablesFromEngine(&m_Bundle.overridableVariables, &m_BalInfoCommand, m_pEngine);
464 BalExitOnFailure(hr, "Failed to set overridable variables from the command line.");
465
466 LExit:
467 return hr;
468 }
469
470 HRESULT EnsureSinglePrimaryPackage()
471 {
472 HRESULT hr = S_OK;
473 BAL_INFO_PACKAGE* pDefaultPackage = NULL;
474 BOOL fPrimaryArchSpecific = FALSE;
475 USHORT usNativeMachine = 0;
476 BAL_INFO_PRIMARY_PACKAGE_TYPE nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_NONE;
477
478 hr = ProcNativeMachine(::GetCurrentProcess(), &usNativeMachine);
479 BalExitOnFailure(hr, "Failed to get native machine value.");
480
481 if (S_FALSE != hr)
482 {
483 switch (usNativeMachine)
484 {
485 case IMAGE_FILE_MACHINE_I386:
486 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X86;
487 break;
488 case IMAGE_FILE_MACHINE_AMD64:
489 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X64;
490 break;
491 case IMAGE_FILE_MACHINE_ARM64:
492 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_ARM64;
493 break;
494 }
495 }
496 else
497 {
498 #if !defined(_WIN64)
499 BOOL fIsWow64 = FALSE;
500
501 ProcWow64(::GetCurrentProcess(), &fIsWow64);
502 if (!fIsWow64)
503 {
504 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X86;
505 }
506 else
507 #endif
508 {
509 nativeType = BAL_INFO_PRIMARY_PACKAGE_TYPE_X64;
510 }
511 }
512
513 for (DWORD i = 0; i < m_Bundle.packages.cPackages; ++i)
514 {
515 BAL_INFO_PACKAGE* pPackage = m_Bundle.packages.rgPackages + i;
516
517 if (BAL_INFO_PRIMARY_PACKAGE_TYPE_NONE == pPackage->primaryPackageType)
518 {
519 // Skip.
520 }
521 else if (nativeType == pPackage->primaryPackageType)
522 {
523 if (fPrimaryArchSpecific)
524 {
525 BalExitWithRootFailure(hr, E_INVALIDDATA, "Bundle contains multiple primary packages for same architecture: %u.", nativeType);
526 }
527
528 pPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT;
529 fPrimaryArchSpecific = TRUE;
530 }
531 else if (BAL_INFO_PRIMARY_PACKAGE_TYPE_DEFAULT == pPackage->primaryPackageType)
532 {
533 if (pDefaultPackage)
534 {
535 BalExitWithRootFailure(hr, E_INVALIDDATA, "Bundle contains multiple default primary packages.");
536 }
537
538 pDefaultPackage = pPackage;
539 }
540 }
541
542 BalExitOnNull(pDefaultPackage, hr, E_INVALIDSTATE, "Bundle did not contain default primary package.");
543
544 if (fPrimaryArchSpecific)
545 {
546 pDefaultPackage->primaryPackageType = BAL_INFO_PRIMARY_PACKAGE_TYPE_NONE;
547 }
548
549 LExit:
550 return hr;
551 }
552
553
554 //
555 // CreateMainWindow - creates the main install window.
556 //
557 HRESULT CreateMainWindow()
558 {
559 HRESULT hr = S_OK;
560 WNDCLASSW wc = { };
561 DWORD dwWindowStyle = WS_POPUP;
562
563 wc.lpfnWndProc = CWixInternalUIBootstrapperApplication::WndProc;
564 wc.hInstance = m_hModule;
565 wc.lpszClassName = WIXIUIBA_WINDOW_CLASS;
566
567 if (!::RegisterClassW(&wc))
568 {
569 ExitWithLastError(hr, "Failed to register window.");
570 }
571
572 m_fRegistered = TRUE;
573
574 // If the UI should be visible, allow it to be visible and activated so we are the foreground window.
575 // This allows the UAC prompt and MSI UI to automatically be activated.
576 if (BOOTSTRAPPER_DISPLAY_NONE < m_commandDisplay)
577 {
578 dwWindowStyle |= WS_VISIBLE;
579 }
580
581 m_hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, dwWindowStyle, 0, 0, 0, 0, HWND_DESKTOP, NULL, m_hModule, this);
582 ExitOnNullWithLastError(m_hWnd, hr, "Failed to create internal UI main window.");
583
584 LExit:
585 return hr;
586 }
587
588 //
589 // DestroyMainWindow - clean up all the window registration.
590 //
591 void DestroyMainWindow()
592 {
593 if (::IsWindow(m_hWnd))
594 {
595 ::DestroyWindow(m_hWnd);
596 m_hWnd = NULL;
597 }
598
599 if (m_fRegistered)
600 {
601 ::UnregisterClassW(WIXIUIBA_WINDOW_CLASS, m_hModule);
602 m_fRegistered = FALSE;
603 }
604 }
605
606 //
607 // WndProc - standard windows message handler.
608 //
609 static LRESULT CALLBACK WndProc(
610 __in HWND hWnd,
611 __in UINT uMsg,
612 __in WPARAM wParam,
613 __in LPARAM lParam
614 )
615 {
616 #pragma warning(suppress:4312)
617 CWixInternalUIBootstrapperApplication* pBA = reinterpret_cast<CWixInternalUIBootstrapperApplication*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
618
619 switch (uMsg)
620 {
621 case WM_NCCREATE:
622 {
623 LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
624 pBA = reinterpret_cast<CWixInternalUIBootstrapperApplication*>(lpcs->lpCreateParams);
625 #pragma warning(suppress:4244)
626 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
627 }
628 break;
629
630 case WM_NCDESTROY:
631 {
632 LRESULT lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
633 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
634 ::PostQuitMessage(0);
635 return lres;
636 }
637
638 case WM_CLOSE:
639 // If the user chose not to close, do *not* let the default window proc handle the message.
640 if (!pBA->OnClose())
641 {
642 return 0;
643 }
644 break;
645
646 case WM_WIXIUIBA_DETECT_PACKAGES: __fallthrough;
647 case WM_WIXIUIBA_DETECT_FOR_CLEANUP:
648 pBA->OnDetect();
649 return 0;
650
651 case WM_WIXIUIBA_PLAN_PACKAGES:
652 case WM_WIXIUIBA_PLAN_PACKAGES_FOR_CLEANUP:
653 pBA->OnPlan(static_cast<BOOTSTRAPPER_ACTION>(lParam));
654 return 0;
655
656 case WM_WIXIUIBA_APPLY_PACKAGES:
657 pBA->OnApply();
658 return 0;
659 }
660
661 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
662 }
663
664
665 //
666 // OnDetect - start the processing of packages.
667 //
668 void OnDetect()
669 {
670 HRESULT hr = S_OK;
671
672 hr = m_pEngine->Detect();
673 BalExitOnFailure(hr, "Failed to start detecting chain.");
674
675 LExit:
676 if (FAILED(hr))
677 {
678 SetLoadPackageFailure(hr);
679 }
680 }
681
682
683 //
684 // OnPlan - plan the detected changes.
685 //
686 void OnPlan(
687 __in BOOTSTRAPPER_ACTION action
688 )
689 {
690 HRESULT hr = S_OK;
691
692 m_plannedAction = action;
693
694 hr = m_pEngine->Plan(action);
695 BalExitOnFailure(hr, "Failed to start planning packages.");
696
697 LExit:
698 if (FAILED(hr))
699 {
700 SetLoadPackageFailure(hr);
701 }
702 }
703
704
705 //
706 // OnApply - apply the packages.
707 //
708 void OnApply()
709 {
710 HRESULT hr = S_OK;
711
712 hr = m_pEngine->Apply(m_hWnd);
713 BalExitOnFailure(hr, "Failed to start applying packages.");
714
715 LExit:
716 if (FAILED(hr))
717 {
718 SetLoadPackageFailure(hr);
719 }
720 }
721
722
723 //
724 // OnClose - called when the window is trying to be closed.
725 //
726 BOOL OnClose()
727 {
728 BOOL fClose = FALSE;
729
730 // If we've already applied, just close.
731 if (m_fApplied)
732 {
733 fClose = TRUE;
734 }
735 else
736 {
737 PromptCancel(m_hWnd, TRUE, NULL, NULL);
738
739 // If we're inside Apply then we never close, we just cancel to let rollback occur.
740 fClose = !m_fApplying;
741 }
742
743 return fClose;
744 }
745
746
747 HRESULT EvaluateConditions()
748 {
749 HRESULT hr = S_OK;
750 BOOL fResult = FALSE;
751
752 for (DWORD i = 0; i < m_Conditions.cConditions; ++i)
753 {
754 BAL_CONDITION* pCondition = m_Conditions.rgConditions + i;
755
756 hr = BalConditionEvaluate(pCondition, m_pEngine, &fResult, &m_sczFailedMessage);
757 BalExitOnFailure(hr, "Failed to evaluate condition.");
758
759 if (!fResult)
760 {
761 hr = E_WIXSTDBA_CONDITION_FAILED;
762 BalExitOnFailure(hr, "%ls", m_sczFailedMessage);
763 }
764 }
765
766 ReleaseNullStrSecure(m_sczFailedMessage);
767
768 LExit:
769 return hr;
770 }
771
772
773 void SetLoadPackageFailure(
774 __in HRESULT hrStatus
775 )
776 {
777 Assert(FAILED(hrStatus));
778
779 if (!m_fApplied)
780 {
781 m_hrFinal = hrStatus;
782 m_fFailedToLoadPackage = TRUE;
783 }
784
785 // Quietly exit.
786 ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0);
787 }
788
789
790 public:
791 //
792 // Constructor - initialize member variables.
793 //
794 CWixInternalUIBootstrapperApplication(
795 __in HMODULE hModule
796 ) : CBootstrapperApplicationBase(3, 3000)
797 {
798 m_hModule = hModule;
799 m_commandAction = BOOTSTRAPPER_ACTION_UNKNOWN;
800 m_commandDisplay = BOOTSTRAPPER_DISPLAY_UNKNOWN;
801
802 m_plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN;
803
804 m_Bundle = { };
805 m_Conditions = { };
806 m_sczConfirmCloseMessage = NULL;
807 m_sczFailedMessage = NULL;
808
809 m_hUiThread = NULL;
810 m_fRegistered = FALSE;
811 m_hWnd = NULL;
812
813 m_hrFinal = S_OK;
814
815 m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
816
817 m_fApplying = FALSE;
818 m_fApplied = FALSE;
819 m_fAutomaticRemoval = FALSE;
820 m_fFailedToLoadPackage = FALSE;
821 }
822
823
824 //
825 // Destructor - release member variables.
826 //
827 ~CWixInternalUIBootstrapperApplication()
828 {
829 ReleaseStr(m_sczFailedMessage);
830 ReleaseStr(m_sczConfirmCloseMessage);
831 BalConditionsUninitialize(&m_Conditions);
832 BalInfoUninitialize(&m_Bundle);
833
834 ReleaseNullObject(m_pEngine);
835 }
836
837 private:
838 HMODULE m_hModule;
839 BOOTSTRAPPER_ACTION m_commandAction;
840 BOOTSTRAPPER_DISPLAY m_commandDisplay;
841
842 BOOTSTRAPPER_ACTION m_plannedAction;
843
844 BAL_INFO_BUNDLE m_Bundle;
845 BAL_CONDITIONS m_Conditions;
846 LPWSTR m_sczFailedMessage;
847 LPWSTR m_sczConfirmCloseMessage;
848
849 HANDLE m_hUiThread;
850 BOOL m_fRegistered;
851 HWND m_hWnd;
852
853 HRESULT m_hrFinal;
854
855 BOOTSTRAPPER_APPLY_RESTART m_restartResult;
856
857 BOOL m_fApplying;
858 BOOL m_fApplied;
859 BOOL m_fAutomaticRemoval;
860 BOOL m_fFailedToLoadPackage;
861 };
862
863
864 //
865 // CreateBootstrapperApplication - creates a new IBootstrapperApplication object.
866 //
867 EXTERN_C HRESULT CreateWixInternalUIBootstrapperApplication(
868 __in HMODULE hInstance,
869 __out IBootstrapperApplication** ppApplication
870 )
871 {
872 HRESULT hr = S_OK;
873
874 CWixInternalUIBootstrapperApplication* pApplication = new CWixInternalUIBootstrapperApplication(hInstance);
875 ExitOnNull(pApplication, hr, E_OUTOFMEMORY, "Failed to create new internal UI bootstrapper application.");
876
877 hr = pApplication->QueryInterface(IID_PPV_ARGS(ppApplication));
878 ExitOnRootFailure(hr, "Failed to query for IBootstrapperApplication.");
879
880 LExit:
881 ReleaseObject(pApplication);
882
883 return hr;
884 }