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