diff options
Diffstat (limited to 'src/ext/Bal/stdbas/WixInternalUIBootstrapperApplication.cpp')
-rw-r--r-- | src/ext/Bal/stdbas/WixInternalUIBootstrapperApplication.cpp | 885 |
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 | |||
6 | static const LPCWSTR WIXIUIBA_WINDOW_CLASS = L"WixInternalUIBA"; | ||
7 | |||
8 | enum 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 | |||
18 | class CWixInternalUIBootstrapperApplication : public CBalBaseBootstrapperApplication | ||
19 | { | ||
20 | public: // 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 | |||
327 | private: | ||
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 | |||
791 | public: | ||
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 | |||
838 | private: | ||
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 | // | ||
868 | EXTERN_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 | |||
881 | LExit: | ||
882 | ReleaseObject(pApplication); | ||
883 | |||
884 | return hr; | ||
885 | } | ||