main
cpp 1,160 lines 43.2 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 HRESULT DetectArpEntry(
6 __in const BURN_PACKAGE* pPackage,
7 __out BOOTSTRAPPER_PACKAGE_STATE* pPackageState,
8 __out_opt LPWSTR* psczQuietUninstallString
9 );
10
11 // function definitions
12
13 extern "C" HRESULT ExeEngineParsePackageFromXml(
14 __in IXMLDOMNode* pixnExePackage,
15 __in BURN_PACKAGE* pPackage
16 )
17 {
18 HRESULT hr = S_OK;
19 BOOL fFoundXml = FALSE;
20 IXMLDOMNodeList* pixnNodes = NULL;
21 IXMLDOMNode* pixnNode = NULL;
22 LPWSTR scz = NULL;
23
24 // @DetectionType
25 hr = XmlGetAttributeEx(pixnExePackage, L"DetectionType", &scz);
26 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @DetectionType.");
27
28 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"condition", -1))
29 {
30 pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_CONDITION;
31 }
32 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"arp", -1))
33 {
34 pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_ARP;
35 }
36 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
37 {
38 pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_NONE;
39 }
40 else
41 {
42 ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid detection type: %ls", scz);
43 }
44
45 if (BURN_EXE_DETECTION_TYPE_CONDITION == pPackage->Exe.detectionType)
46 {
47 // @DetectCondition
48 hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition);
49 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @DetectCondition.");
50
51 // @UninstallArguments
52 hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
53 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments.");
54
55 // @Uninstallable
56 hr = XmlGetYesNoAttribute(pixnExePackage, L"Uninstallable", &pPackage->Exe.fUninstallable);
57 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Uninstallable.");
58 }
59 else if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType)
60 {
61 // @ArpId
62 hr = XmlGetAttributeEx(pixnExePackage, L"ArpId", &scz);
63 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @ArpId.");
64
65 hr = PathConcatRelativeToBase(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\", scz, &pPackage->Exe.sczArpKeyPath);
66 ExitOnFailure(hr, "Failed to build full key path.");
67
68 // @ArpDisplayVersion
69 hr = XmlGetAttributeEx(pixnExePackage, L"ArpDisplayVersion", &scz);
70 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @ArpDisplayVersion.");
71
72 hr = VerParseVersion(scz, 0, FALSE, &pPackage->Exe.pArpDisplayVersion);
73 ExitOnFailure(hr, "Failed to parse @ArpDisplayVersion: %ls", scz);
74
75 if (pPackage->Exe.pArpDisplayVersion->fInvalid)
76 {
77 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
78 }
79
80 // @ArpWin64
81 hr = XmlGetYesNoAttribute(pixnExePackage, L"ArpWin64", &pPackage->Exe.fArpWin64);
82 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @ArpWin64.");
83
84 // @ArpUseUninstallString
85 hr = XmlGetYesNoAttribute(pixnExePackage, L"ArpUseUninstallString", &pPackage->Exe.fArpUseUninstallString);
86 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @ArpWin64.");
87
88 // @UninstallArguments
89 hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
90 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments.");
91
92 pPackage->Exe.fUninstallable = TRUE;
93 }
94
95 // @InstallArguments
96 hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments);
97 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments.");
98
99 // @RepairArguments
100 hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments);
101 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairArguments.");
102
103 // @Repairable
104 hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable);
105 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Repairable.");
106
107 // @Bundle
108 hr = XmlGetYesNoAttribute(pixnExePackage, L"Bundle", &pPackage->Exe.fBundle);
109 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Bundle.");
110
111 // @Protocol
112 hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz);
113 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Protocol.");
114
115 if (fFoundXml)
116 {
117 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1))
118 {
119 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN;
120 }
121 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1))
122 {
123 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4;
124 }
125 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
126 {
127 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE;
128 }
129 else
130 {
131 ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid protocol type: %ls", scz);
132 }
133 }
134
135 hr = ExeEngineParseExitCodesFromXml(pixnExePackage, &pPackage->Exe.rgExitCodes, &pPackage->Exe.cExitCodes);
136 ExitOnFailure(hr, "Failed to parse exit codes.");
137
138 hr = ExeEngineParseCommandLineArgumentsFromXml(pixnExePackage, &pPackage->Exe.rgCommandLineArguments, &pPackage->Exe.cCommandLineArguments);
139 ExitOnFailure(hr, "Failed to parse command lines.");
140
141 LExit:
142 ReleaseObject(pixnNodes);
143 ReleaseObject(pixnNode);
144 ReleaseStr(scz);
145
146 return hr;
147 }
148
149 extern "C" void ExeEnginePackageUninitialize(
150 __in BURN_PACKAGE* pPackage
151 )
152 {
153 ReleaseStr(pPackage->Exe.sczDetectCondition);
154 ReleaseStr(pPackage->Exe.sczInstallArguments);
155 ReleaseStr(pPackage->Exe.sczRepairArguments);
156 ReleaseStr(pPackage->Exe.sczUninstallArguments);
157 ReleaseStr(pPackage->Exe.sczArpKeyPath);
158 ReleaseVerutilVersion(pPackage->Exe.pArpDisplayVersion);
159 ReleaseMem(pPackage->Exe.rgExitCodes);
160
161 // free command-line arguments
162 if (pPackage->Exe.rgCommandLineArguments)
163 {
164 for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i)
165 {
166 ExeEngineCommandLineArgumentUninitialize(pPackage->Exe.rgCommandLineArguments + i);
167 }
168 MemFree(pPackage->Exe.rgCommandLineArguments);
169 }
170
171 // clear struct
172 memset(&pPackage->Exe, 0, sizeof(pPackage->Exe));
173 }
174
175 extern "C" void ExeEngineCommandLineArgumentUninitialize(
176 __in BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument
177 )
178 {
179 ReleaseStr(pCommandLineArgument->sczInstallArgument);
180 ReleaseStr(pCommandLineArgument->sczUninstallArgument);
181 ReleaseStr(pCommandLineArgument->sczRepairArgument);
182 ReleaseStr(pCommandLineArgument->sczCondition);
183 }
184
185 extern "C" HRESULT ExeEngineDetectPackage(
186 __in BURN_PACKAGE* pPackage,
187 __in BURN_REGISTRATION* pRegistration,
188 __in BURN_VARIABLES* pVariables
189 )
190 {
191 HRESULT hr = S_OK;
192 BOOL fDetected = FALSE;
193
194 switch (pPackage->Exe.detectionType)
195 {
196 case BURN_EXE_DETECTION_TYPE_NONE:
197 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
198 break;
199 case BURN_EXE_DETECTION_TYPE_CONDITION:
200 // evaluate detect condition
201 if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition)
202 {
203 hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected);
204 ExitOnFailure(hr, "Failed to evaluate EXE package detect condition.");
205 }
206
207 // update detect state
208 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
209
210 break;
211 case BURN_EXE_DETECTION_TYPE_ARP:
212 hr = DetectArpEntry(pPackage, &pPackage->currentState, NULL);
213 ExitOnFailure(hr, "Failed to detect EXE package by ArpEntry.");
214
215 break;
216 default:
217 ExitWithRootFailure(hr, E_INVALIDARG, "Unknown EXE package detection type: %d.", pPackage->Exe.detectionType);
218 }
219
220 if (pPackage->fCanAffectRegistration)
221 {
222 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
223 }
224
225 hr = DependencyDetectChainPackage(pPackage, pRegistration);
226 ExitOnFailure(hr, "Failed to detect dependencies for EXE package.");
227
228 LExit:
229 return hr;
230 }
231
232 //
233 // PlanCalculate - calculates the execute and rollback state for the requested package state.
234 //
235 extern "C" HRESULT ExeEnginePlanCalculatePackage(
236 __in BURN_PACKAGE* pPackage
237 )
238 {
239 HRESULT hr = S_OK;
240 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
241 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
242
243 // execute action
244 switch (pPackage->currentState)
245 {
246 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
247 switch (pPackage->requested)
248 {
249 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
250 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
251 break;
252 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
253 execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE;
254 break;
255 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
256 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
257 execute = !pPackage->fPermanent ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
258 break;
259 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
260 execute = pPackage->Exe.fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
261 break;
262 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT:
263 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
264 break;
265 default:
266 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
267 break;
268 }
269 break;
270
271 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
272 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
273 switch (pPackage->requested)
274 {
275 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
276 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
277 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
278 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
279 break;
280 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
281 execute = pPackage->Exe.fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
282 break;
283 default:
284 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
285 break;
286 }
287 break;
288
289 default:
290 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid package current state: %d.", pPackage->currentState);
291 }
292
293 // Calculate the rollback action if there is an execute action.
294 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
295 {
296 switch (pPackage->currentState)
297 {
298 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
299 switch (pPackage->requested)
300 {
301 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
302 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
303 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
304 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
305 break;
306 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
307 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
308 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
309 break;
310 default:
311 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
312 break;
313 }
314 break;
315
316 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
317 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
318 switch (pPackage->requested)
319 {
320 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
321 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough;
322 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
323 rollback = !pPackage->fPermanent ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
324 break;
325 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
326 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
327 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
328 break;
329 default:
330 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
331 break;
332 }
333 break;
334
335 default:
336 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid package expected state.");
337 }
338 }
339
340 // return values
341 pPackage->execute = execute;
342 pPackage->rollback = rollback;
343
344 LExit:
345 return hr;
346 }
347
348 //
349 // PlanAdd - adds the calculated execute and rollback actions for the package.
350 //
351 extern "C" HRESULT ExeEnginePlanAddPackage(
352 __in BURN_PACKAGE* pPackage,
353 __in BURN_PLAN* pPlan,
354 __in BURN_LOGGING* pLog,
355 __in BURN_VARIABLES* pVariables
356 )
357 {
358 HRESULT hr = S_OK;
359 BURN_EXECUTE_ACTION* pAction = NULL;
360
361 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
362 ExitOnFailure(hr, "Failed to plan package dependency actions.");
363
364 // add rollback action
365 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
366 {
367 hr = PlanAppendRollbackAction(pPlan, &pAction);
368 ExitOnFailure(hr, "Failed to append rollback action.");
369
370 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
371 pAction->exePackage.pPackage = pPackage;
372 pAction->exePackage.action = pPackage->rollback;
373
374 if (pPackage->Exe.wzAncestors)
375 {
376 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
377 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
378 }
379
380 if (pPackage->Exe.wzEngineWorkingDirectory)
381 {
382 hr = StrAllocString(&pAction->exePackage.sczEngineWorkingDirectory, pPackage->Exe.wzEngineWorkingDirectory, 0);
383 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
384 }
385
386 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
387
388 hr = PlanExecuteCheckpoint(pPlan);
389 ExitOnFailure(hr, "Failed to append execute checkpoint.");
390 }
391
392 // add execute action
393 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
394 {
395 hr = PlanAppendExecuteAction(pPlan, &pAction);
396 ExitOnFailure(hr, "Failed to append execute action.");
397
398 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
399 pAction->exePackage.pPackage = pPackage;
400 pAction->exePackage.action = pPackage->execute;
401
402 if (pPackage->Exe.wzAncestors)
403 {
404 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.wzAncestors, 0);
405 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
406 }
407
408 if (pPackage->Exe.wzEngineWorkingDirectory)
409 {
410 hr = StrAllocString(&pAction->exePackage.sczEngineWorkingDirectory, pPackage->Exe.wzEngineWorkingDirectory, 0);
411 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
412 }
413
414 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
415 }
416
417 LExit:
418 return hr;
419 }
420
421 extern "C" HRESULT ExeEngineExecutePackage(
422 __in BURN_EXECUTE_ACTION* pExecuteAction,
423 __in BURN_CACHE* pCache,
424 __in BURN_VARIABLES* pVariables,
425 __in BOOL fRollback,
426 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
427 __in LPVOID pvContext,
428 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
429 )
430 {
431 HRESULT hr = S_OK;
432 LPCWSTR wzArguments = NULL;
433 LPWSTR sczCachedDirectory = NULL;
434 LPWSTR sczExecutablePath = NULL;
435 LPWSTR sczBaseCommand = NULL;
436 LPWSTR sczUnformattedUserArgs = NULL;
437 LPWSTR sczUserArgs = NULL;
438 LPWSTR sczUserArgsObfuscated = NULL;
439 LPWSTR sczCommandObfuscated = NULL;
440 LPWSTR sczArpUninstallString = NULL;
441 int argcArp = 0;
442 LPWSTR* argvArp = NULL;
443 BOOTSTRAPPER_PACKAGE_STATE applyState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
444 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
445 BURN_PIPE_CONNECTION connection = { };
446 DWORD dwExitCode = 0;
447 BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage;
448 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
449
450 if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType &&
451 (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action ||
452 BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->exePackage.action && fRollback))
453 {
454 hr = DetectArpEntry(pPackage, &applyState, &sczArpUninstallString);
455 ExitOnFailure(hr, "Failed to query ArpEntry for %hs.", BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action ? "uninstall" : "install");
456
457 if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == applyState && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action)
458 {
459 if (fRollback)
460 {
461 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), LoggingPackageStateToString(applyState));
462 }
463 else
464 {
465 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId);
466 }
467
468 ExitFunction();
469 }
470 else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT != applyState && BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->exePackage.action)
471 {
472 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), LoggingPackageStateToString(applyState));
473 ExitFunction();
474 }
475 }
476
477 if (pPackage->Exe.fPseudoPackage && BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE != pPackagePayload->verification)
478 {
479 if (!PathIsFullyQualified(pPackagePayload->sczFilePath))
480 {
481 ExitWithRootFailure(hr, E_INVALIDSTATE, "Pseudo ExePackages must have a fully qualified target path.");
482 }
483
484 hr = StrAllocString(&sczExecutablePath, pPackagePayload->sczFilePath, 0);
485 ExitOnFailure(hr, "Failed to build executable path.");
486
487 hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory);
488 ExitOnFailure(hr, "Failed to get parent directory for pseudo-package: %ls", pPackage->sczId);
489 }
490 else if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action)
491 {
492 ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "%hs is null.", pPackage->Exe.fArpUseUninstallString ? "UninstallString" : "QuietUninstallString");
493
494 hr = AppParseCommandLine(sczArpUninstallString, &argcArp, &argvArp);
495 ExitOnFailure(hr, "Failed to parse QuietUninstallString: %ls.", sczArpUninstallString);
496
497 ExitOnNull(argcArp, hr, E_INVALIDARG, "QuietUninstallString must contain an executable path.");
498
499 hr = StrAllocString(&sczExecutablePath, argvArp[0], 0);
500 ExitOnFailure(hr, "Failed to copy executable path.");
501
502 if (pPackage->fPerMachine)
503 {
504 hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath);
505 ExitOnFailure(hr, "Failed to verify the QuietUninstallString executable path is in a secure location: %ls", sczExecutablePath);
506 if (S_FALSE == hr)
507 {
508 LogStringLine(REPORT_STANDARD, "The QuietUninstallString executable path is not in a secure location: %ls", sczExecutablePath);
509 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
510 }
511 }
512
513 hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory);
514 ExitOnFailure(hr, "Failed to get parent directory for QuietUninstallString executable path: %ls", sczExecutablePath);
515 }
516 else
517 {
518 // get cached executable path
519 hr = CacheGetCompletedPath(pCache, pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory);
520 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId);
521
522 hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pPackagePayload->sczFilePath, &sczExecutablePath);
523 ExitOnFailure(hr, "Failed to build executable path.");
524 }
525
526 // Best effort to set the execute package cache folder and action variables.
527 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
528 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE);
529
530 // pick arguments
531 switch (pExecuteAction->exePackage.action)
532 {
533 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
534 wzArguments = pPackage->Exe.sczInstallArguments;
535 break;
536
537 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
538 wzArguments = pPackage->Exe.sczUninstallArguments;
539 break;
540
541 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
542 wzArguments = pPackage->Exe.sczRepairArguments;
543 break;
544
545 default:
546 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
547 }
548
549 // now add optional arguments
550 hr = StrAllocString(&sczUnformattedUserArgs, wzArguments && *wzArguments ? wzArguments : L"", 0);
551 ExitOnFailure(hr, "Failed to copy package arguments.");
552
553 for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i)
554 {
555 BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
556 BOOL fCondition = FALSE;
557
558 hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition);
559 ExitOnFailure(hr, "Failed to evaluate executable package command-line condition.");
560
561 if (fCondition)
562 {
563 hr = StrAllocConcat(&sczUnformattedUserArgs, L" ", 0);
564 ExitOnFailure(hr, "Failed to separate command-line arguments.");
565
566 switch (pExecuteAction->exePackage.action)
567 {
568 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
569 hr = StrAllocConcat(&sczUnformattedUserArgs, commandLineArgument->sczInstallArgument, 0);
570 ExitOnFailure(hr, "Failed to get command-line argument for install.");
571 break;
572
573 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
574 hr = StrAllocConcat(&sczUnformattedUserArgs, commandLineArgument->sczUninstallArgument, 0);
575 ExitOnFailure(hr, "Failed to get command-line argument for uninstall.");
576 break;
577
578 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
579 hr = StrAllocConcat(&sczUnformattedUserArgs, commandLineArgument->sczRepairArgument, 0);
580 ExitOnFailure(hr, "Failed to get command-line argument for repair.");
581 break;
582
583 default:
584 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
585 }
586 }
587 }
588
589 // build base command
590 hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath);
591 ExitOnFailure(hr, "Failed to allocate base command.");
592
593 for (int i = 1; i < argcArp; ++i)
594 {
595 hr = AppAppendCommandLineArgument(&sczBaseCommand, argvArp[i]);
596 ExitOnFailure(hr, "Failed to append argument from ARP.");
597 }
598
599 if (pPackage->Exe.fBundle)
600 {
601 hr = StrAllocConcat(&sczBaseCommand, L" -norestart", 0);
602 ExitOnFailure(hr, "Failed to append norestart argument.");
603
604 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls", BURN_COMMANDLINE_SWITCH_RELATED_CHAIN_PACKAGE);
605 ExitOnFailure(hr, "Failed to append the relation type to the command line.");
606
607 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=ALL", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
608 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
609
610 // Add the list of ancestors, if any, to the burn command line.
611 if (pExecuteAction->exePackage.sczAncestors)
612 {
613 hr = StrAllocConcatFormatted(&sczBaseCommand, L" -%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
614 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
615 }
616
617 if (pExecuteAction->exePackage.sczEngineWorkingDirectory)
618 {
619 hr = CoreAppendEngineWorkingDirectoryToCommandLine(pExecuteAction->exePackage.sczEngineWorkingDirectory, &sczBaseCommand, NULL);
620 ExitOnFailure(hr, "Failed to append the custom working directory to the exepackage command line.");
621 }
622
623 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczBaseCommand, NULL);
624 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
625 }
626
627 // For bundles, append logging to command line if it doesn't contain '-log'
628 if (pPackage->Exe.fBundle || BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
629 {
630 CoreAppendLogToCommandLine(&sczBaseCommand, &sczCommandObfuscated, fRollback, pVariables, pPackage);
631 }
632
633 // build user args
634 if (sczUnformattedUserArgs && *sczUnformattedUserArgs)
635 {
636 hr = VariableFormatString(pVariables, sczUnformattedUserArgs, &sczUserArgs, NULL);
637 ExitOnFailure(hr, "Failed to format argument string.");
638
639 hr = VariableFormatStringObfuscated(pVariables, sczUnformattedUserArgs, &sczUserArgsObfuscated, NULL);
640 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
641
642 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls %ls", sczBaseCommand, sczUserArgsObfuscated);
643 ExitOnFailure(hr, "Failed to allocate obfuscated exe command.");
644 }
645
646 // Log obfuscated command, which won't include raw hidden variable values or protocol specific arguments to avoid exposing secrets.
647 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated ? sczCommandObfuscated : sczBaseCommand);
648
649 if (!pPackage->Exe.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
650 {
651 hr = EmbeddedRunBundle(&connection, sczExecutablePath, sczBaseCommand, sczUserArgs, pfnGenericMessageHandler, pvContext, &dwExitCode);
652 ExitOnFailure(hr, "Failed to run exe with Burn protocol from path: %ls", sczExecutablePath);
653 }
654 else if (!pPackage->Exe.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pPackage->Exe.protocol)
655 {
656 hr = NetFxRunChainer(sczExecutablePath, sczBaseCommand, sczUserArgs, pfnGenericMessageHandler, pvContext, &dwExitCode);
657 ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
658 }
659 else
660 {
661 hr = ExeEngineRunProcess(pfnGenericMessageHandler, pvContext, pPackage, sczExecutablePath, sczBaseCommand, sczUserArgs, sczCachedDirectory, &dwExitCode);
662 ExitOnFailure(hr, "Failed to run EXE process");
663 }
664
665 hr = ExeEngineHandleExitCode(pPackage->Exe.rgExitCodes, pPackage->Exe.cExitCodes, pPackage->sczId, dwExitCode, pRestart);
666 ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode);
667
668 LExit:
669 ReleaseStr(sczCachedDirectory);
670 ReleaseStr(sczExecutablePath);
671 ReleaseStr(sczBaseCommand);
672 ReleaseStr(sczUnformattedUserArgs);
673 StrSecureZeroFreeString(sczUserArgs);
674 ReleaseStr(sczUserArgsObfuscated);
675 ReleaseStr(sczCommandObfuscated);
676 ReleaseStr(sczArpUninstallString);
677
678 if (argvArp)
679 {
680 AppFreeCommandLineArgs(argvArp);
681 }
682
683 ReleaseFileHandle(hExecutableFile);
684
685 // Best effort to clear the execute package cache folder and action variables.
686 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
687 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
688
689 return hr;
690 }
691
692 extern "C" HRESULT ExeEngineRunProcess(
693 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
694 __in LPVOID pvContext,
695 __in BURN_PACKAGE* pPackage,
696 __in_z LPCWSTR wzExecutablePath,
697 __in_z LPWSTR sczBaseCommand,
698 __in_z_opt LPCWSTR wzUserArgs,
699 __in_z_opt LPCWSTR wzCachedDirectory,
700 __inout DWORD* pdwExitCode
701 )
702 {
703 HRESULT hr = S_OK;
704 LPWSTR sczCommand = NULL;
705 PROCESS_INFORMATION pi = { };
706 GENERIC_EXECUTE_MESSAGE message = { };
707 int nResult = IDNOACTION;
708 DWORD dwProcessId = 0;
709 BOOL fDelayedCancel = FALSE;
710 BOOL fFireAndForget = BURN_PACKAGE_TYPE_EXE == pPackage->type && pPackage->Exe.fFireAndForget;
711 BOOL fInheritHandles = BURN_PACKAGE_TYPE_BUNDLE == pPackage->type;
712
713 // Always add user supplied arguments last.
714 if (wzUserArgs)
715 {
716 hr = StrAllocFormattedSecure(&sczCommand, L"%ls %ls", sczBaseCommand, wzUserArgs);
717 ExitOnFailure(hr, "Failed to append user args.");
718 }
719
720 // Make the cache location of the executable the current directory to help those executables
721 // that expect stuff to be relative to them.
722 hr = CoreCreateProcess(wzExecutablePath, sczCommand ? sczCommand : sczBaseCommand, fInheritHandles, CREATE_NO_WINDOW, wzCachedDirectory, 0, &pi);
723 ExitOnFailure(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
724
725 message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_STARTED;
726 message.dwUIHint = MB_OK;
727 pfnGenericMessageHandler(&message, pvContext);
728
729 if (fFireAndForget)
730 {
731 ::WaitForInputIdle(pi.hProcess, 5000);
732 ExitFunction();
733 }
734
735 dwProcessId = ::GetProcessId(pi.hProcess);
736
737 // Wait for the executable process while sending fake progress to allow cancel.
738 do
739 {
740 memset(&message, 0, sizeof(message));
741 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
742 message.dwUIHint = MB_OKCANCEL;
743 message.progress.dwPercentage = 50;
744 nResult = pfnGenericMessageHandler(&message, pvContext);
745
746 if (IDCANCEL == nResult)
747 {
748 memset(&message, 0, sizeof(message));
749 message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_CANCEL;
750 message.dwUIHint = MB_ABORTRETRYIGNORE;
751 message.processCancel.dwProcessId = dwProcessId;
752 nResult = pfnGenericMessageHandler(&message, pvContext);
753
754 if (IDIGNORE == nResult) // abandon
755 {
756 nResult = IDCANCEL;
757 fDelayedCancel = FALSE;
758 }
759 //else if (IDABORT == nResult) // kill
760 else // wait
761 {
762 if (!fDelayedCancel)
763 {
764 fDelayedCancel = TRUE;
765
766 LogId(REPORT_STANDARD, MSG_EXECUTE_PROCESS_DELAYED_CANCEL_REQUESTED, pPackage->sczId);
767 }
768
769 nResult = IDNOACTION;
770 }
771 }
772
773 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
774 ExitOnRootFailure(hr, "Bootstrapper application aborted during package process progress.");
775
776 hr = CoreWaitForProcCompletion(pi.hProcess, 500, pdwExitCode);
777 if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
778 {
779 ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", wzExecutablePath);
780 }
781 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
782
783 memset(&message, 0, sizeof(message));
784 message.type = GENERIC_EXECUTE_MESSAGE_PROCESS_COMPLETED;
785 message.dwUIHint = MB_OK;
786 pfnGenericMessageHandler(&message, pvContext);
787
788 if (fDelayedCancel)
789 {
790 ExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT), "Bootstrapper application cancelled during package process progress, exit code: 0x%x", *pdwExitCode);
791 }
792
793 LExit:
794 StrSecureZeroFreeString(sczCommand);
795 ReleaseHandle(pi.hThread);
796 ReleaseHandle(pi.hProcess);
797
798 return hr;
799 }
800
801 extern "C" void ExeEngineUpdateInstallRegistrationState(
802 __in BURN_EXECUTE_ACTION* pAction,
803 __in HRESULT hrExecute
804 )
805 {
806 BURN_PACKAGE* pPackage = pAction->exePackage.pPackage;
807
808 if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration)
809 {
810 ExitFunction();
811 }
812
813 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->exePackage.action)
814 {
815 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
816 }
817 else
818 {
819 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT;
820 }
821
822 LExit:
823 return;
824 }
825
826 extern "C" HRESULT ExeEngineParseExitCodesFromXml(
827 __in IXMLDOMNode* pixnPackage,
828 __inout BURN_EXE_EXIT_CODE** prgExitCodes,
829 __inout DWORD* pcExitCodes
830 )
831 {
832 HRESULT hr = S_OK;
833 IXMLDOMNodeList* pixnNodes = NULL;
834 IXMLDOMNode* pixnNode = NULL;
835 DWORD cNodes = 0;
836 LPWSTR scz = NULL;
837
838 // select exit code nodes
839 hr = XmlSelectNodes(pixnPackage, L"ExitCode", &pixnNodes);
840 ExitOnFailure(hr, "Failed to select exit code nodes.");
841
842 // get exit code node count
843 hr = pixnNodes->get_length((long*) &cNodes);
844 ExitOnFailure(hr, "Failed to get exit code node count.");
845
846 if (cNodes)
847 {
848 // allocate memory for exit codes
849 *prgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE);
850 ExitOnNull(*prgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs.");
851
852 *pcExitCodes = cNodes;
853
854 // parse package elements
855 for (DWORD i = 0; i < cNodes; ++i)
856 {
857 BURN_EXE_EXIT_CODE* pExitCode = *prgExitCodes + i;
858
859 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
860 ExitOnFailure(hr, "Failed to get next node.");
861
862 // @Type
863 hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type);
864 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Type.");
865
866 // @Code
867 hr = XmlGetAttributeEx(pixnNode, L"Code", &scz);
868 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Code.");
869
870 if (L'*' == scz[0])
871 {
872 pExitCode->fWildcard = TRUE;
873 }
874 else
875 {
876 hr = StrStringToInt32(scz, 0, reinterpret_cast<INT*>(&pExitCode->dwCode));
877 ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz);
878 }
879
880 // prepare next iteration
881 ReleaseNullObject(pixnNode);
882 }
883 }
884
885 hr = S_OK;
886
887 LExit:
888 ReleaseObject(pixnNodes);
889 ReleaseObject(pixnNode);
890 ReleaseStr(scz);
891
892 return hr;
893 }
894
895 extern "C" HRESULT ExeEngineParseCommandLineArgumentsFromXml(
896 __in IXMLDOMNode* pixnPackage,
897 __inout BURN_EXE_COMMAND_LINE_ARGUMENT** prgCommandLineArguments,
898 __inout DWORD* pcCommandLineArguments
899 )
900 {
901 HRESULT hr = S_OK;
902 IXMLDOMNodeList* pixnNodes = NULL;
903 IXMLDOMNode* pixnNode = NULL;
904 DWORD cNodes = 0;
905 LPWSTR scz = NULL;
906
907 // Select command-line argument nodes.
908 hr = XmlSelectNodes(pixnPackage, L"CommandLine", &pixnNodes);
909 ExitOnFailure(hr, "Failed to select command-line argument nodes.");
910
911 // Get command-line argument node count.
912 hr = pixnNodes->get_length((long*) &cNodes);
913 ExitOnFailure(hr, "Failed to get command-line argument count.");
914
915 if (cNodes)
916 {
917 *prgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE);
918 ExitOnNull(*prgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs.");
919
920 *pcCommandLineArguments = cNodes;
921
922 // Parse command-line argument elements.
923 for (DWORD i = 0; i < cNodes; ++i)
924 {
925 BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = *prgCommandLineArguments + i;
926 BOOL fFoundXml = FALSE;
927
928 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
929 ExitOnFailure(hr, "Failed to get next command-line argument node.");
930
931 // @InstallArgument
932 hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument);
933 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArgument.");
934
935 // @UninstallArgument
936 hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument);
937 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArgument.");
938
939 // @RepairArgument
940 hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument);
941 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairArgument.");
942
943 // @Condition
944 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition);
945 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Condition.");
946
947 // Prepare next iteration.
948 ReleaseNullObject(pixnNode);
949 }
950 }
951
952 hr = S_OK;
953
954 LExit:
955 ReleaseObject(pixnNodes);
956 ReleaseObject(pixnNode);
957 ReleaseStr(scz);
958
959 return hr;
960 }
961
962 extern "C" HRESULT ExeEngineHandleExitCode(
963 __in BURN_EXE_EXIT_CODE* rgCustomExitCodes,
964 __in DWORD cCustomExitCodes,
965 __in_z LPCWSTR wzId,
966 __in DWORD dwExitCode,
967 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
968 )
969 {
970 HRESULT hr = S_OK;
971 BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE;
972
973 for (DWORD i = 0; i < cCustomExitCodes; ++i)
974 {
975 BURN_EXE_EXIT_CODE* pExitCode = rgCustomExitCodes + i;
976
977 // If this is a wildcard, use the last one we come across.
978 if (pExitCode->fWildcard)
979 {
980 typeCode = pExitCode->type;
981 }
982 else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking.
983 {
984 typeCode = pExitCode->type;
985 break;
986 }
987 }
988
989 // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts
990 // and everything else as an error.
991 if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode)
992 {
993 if (0 == dwExitCode)
994 {
995 typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS;
996 }
997 else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode ||
998 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode) ||
999 ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode ||
1000 HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast<HRESULT>(dwExitCode))
1001 {
1002 typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT;
1003 }
1004 else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode ||
1005 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast<HRESULT>(dwExitCode))
1006 {
1007 typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT;
1008 }
1009 else if (ERROR_FAIL_REBOOT_REQUIRED == dwExitCode ||
1010 HRESULT_FROM_WIN32(ERROR_FAIL_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode))
1011 {
1012 typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR_SCHEDULE_REBOOT;
1013 }
1014 else if (ERROR_FAIL_REBOOT_INITIATED == dwExitCode ||
1015 HRESULT_FROM_WIN32(ERROR_FAIL_REBOOT_INITIATED) == static_cast<HRESULT>(dwExitCode))
1016 {
1017 typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR_FORCE_REBOOT;
1018 }
1019 else
1020 {
1021 typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR;
1022 }
1023 }
1024
1025 switch (typeCode)
1026 {
1027 case BURN_EXE_EXIT_CODE_TYPE_SUCCESS:
1028 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1029 hr = S_OK;
1030 break;
1031
1032 case BURN_EXE_EXIT_CODE_TYPE_ERROR:
1033 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1034 hr = HRESULT_FROM_WIN32(dwExitCode);
1035 if (SUCCEEDED(hr))
1036 {
1037 hr = E_FAIL;
1038 }
1039 break;
1040
1041 case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT:
1042 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1043 hr = S_OK;
1044 break;
1045
1046 case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT:
1047 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1048 hr = S_OK;
1049 break;
1050
1051 case BURN_EXE_EXIT_CODE_TYPE_ERROR_SCHEDULE_REBOOT:
1052 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1053 hr = HRESULT_FROM_WIN32(dwExitCode);
1054 if (SUCCEEDED(hr))
1055 {
1056 hr = HRESULT_FROM_WIN32(ERROR_FAIL_REBOOT_REQUIRED);
1057 }
1058 break;
1059
1060 case BURN_EXE_EXIT_CODE_TYPE_ERROR_FORCE_REBOOT:
1061 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1062 hr = HRESULT_FROM_WIN32(dwExitCode);
1063 if (SUCCEEDED(hr))
1064 {
1065 hr = HRESULT_FROM_WIN32(ERROR_FAIL_REBOOT_INITIATED);
1066 }
1067 break;
1068
1069 default:
1070 hr = E_UNEXPECTED;
1071 break;
1072 }
1073
1074 //LExit:
1075 LogId(REPORT_STANDARD, MSG_EXECUTE_PACKAGE_PROCESS_EXITED, wzId, dwExitCode, LoggingExitCodeTypeToString(typeCode), LoggingRestartToString(*pRestart));
1076
1077 return hr;
1078 }
1079
1080 static HRESULT DetectArpEntry(
1081 __in const BURN_PACKAGE* pPackage,
1082 __out BOOTSTRAPPER_PACKAGE_STATE* pPackageState,
1083 __out_opt LPWSTR* psczQuietUninstallString
1084 )
1085 {
1086 HRESULT hr = S_OK;
1087 HKEY hKey = NULL;
1088 BOOL fExists = FALSE;
1089 VERUTIL_VERSION* pVersion = NULL;
1090 int nCompareResult = 0;
1091 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1092 REG_KEY_BITNESS keyBitness = pPackage->Exe.fArpWin64 ? REG_KEY_64BIT : REG_KEY_32BIT;
1093
1094 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
1095 if (psczQuietUninstallString)
1096 {
1097 ReleaseNullStr(*psczQuietUninstallString);
1098 }
1099
1100 hr = RegOpenEx(hkRoot, pPackage->Exe.sczArpKeyPath, KEY_READ, keyBitness, &hKey);
1101 ExitOnPathFailure(hr, fExists, "Failed to open registry key: %ls.", pPackage->Exe.sczArpKeyPath);
1102
1103 if (!fExists)
1104 {
1105 ExitFunction();
1106 }
1107
1108 hr = RegReadWixVersion(hKey, L"DisplayVersion", &pVersion);
1109 ExitOnPathFailure(hr, fExists, "Failed to read DisplayVersion.");
1110
1111 if (!fExists)
1112 {
1113 ExitFunction();
1114 }
1115
1116 if (pVersion->fInvalid)
1117 {
1118 LogId(REPORT_WARNING, MSG_DETECTED_EXE_PACKAGE_INVALID_VERSION, pPackage->Exe.sczArpKeyPath, pVersion->sczVersion);
1119 }
1120
1121 hr = VerCompareParsedVersions(pPackage->Exe.pArpDisplayVersion, pVersion, &nCompareResult);
1122 ExitOnFailure(hr, "Failed to compare versions.");
1123
1124 if (nCompareResult < 0)
1125 {
1126 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
1127 }
1128 else if (nCompareResult > 0)
1129 {
1130 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
1131 }
1132 else
1133 {
1134 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
1135 }
1136
1137 if (psczQuietUninstallString)
1138 {
1139 LPCWSTR sczUninstallStringName = pPackage->Exe.fArpUseUninstallString ? L"UninstallString" : L"QuietUninstallString";
1140
1141 hr = RegReadString(hKey, sczUninstallStringName, psczQuietUninstallString);
1142 ExitOnPathFailure(hr, fExists, "Failed to read %ls.", sczUninstallStringName);
1143
1144 // If the uninstall string is an executable path then ensure it is enclosed in quotes
1145 if (fExists && *psczQuietUninstallString && (L'\"' != **psczQuietUninstallString) && FileExistsEx(*psczQuietUninstallString, nullptr))
1146 {
1147 hr = StrAllocPrefix(psczQuietUninstallString, L"\"", 0);
1148 ExitOnFailure(hr, "Failed to prepend UninstallString with quote.");
1149
1150 hr = StrAllocConcat(psczQuietUninstallString, L"\"", 0);
1151 ExitOnFailure(hr, "Failed to append quote to UninstallString.");
1152 }
1153 }
1154
1155 LExit:
1156 ReleaseRegKey(hKey);
1157 ReleaseVerutilVersion(pVersion);
1158
1159 return hr;
1160 }