summaryrefslogtreecommitdiff
path: root/src/burn/engine/exeengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/exeengine.cpp')
-rw-r--r--src/burn/engine/exeengine.cpp295
1 files changed, 265 insertions, 30 deletions
diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp
index 808577bc..3a64ecd8 100644
--- a/src/burn/engine/exeengine.cpp
+++ b/src/burn/engine/exeengine.cpp
@@ -2,6 +2,16 @@
2 2
3#include "precomp.h" 3#include "precomp.h"
4 4
5static HRESULT DetectArpEntry(
6 __in const BURN_PACKAGE* pPackage,
7 __out BOOTSTRAPPER_PACKAGE_STATE* pPackageState,
8 __out_opt LPWSTR* psczQuietUninstallString
9 );
10static HRESULT ParseArpUninstallString(
11 __in_z LPCWSTR wzArpUninstallString,
12 __inout LPWSTR* psczExecutablePath,
13 __inout LPWSTR* psczArguments
14 );
5 15
6// function definitions 16// function definitions
7 17
@@ -16,18 +26,73 @@ extern "C" HRESULT ExeEngineParsePackageFromXml(
16 IXMLDOMNode* pixnNode = NULL; 26 IXMLDOMNode* pixnNode = NULL;
17 LPWSTR scz = NULL; 27 LPWSTR scz = NULL;
18 28
19 // @DetectCondition 29 // @DetectionType
20 hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition); 30 hr = XmlGetAttributeEx(pixnExePackage, L"DetectionType", &scz);
21 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @DetectCondition."); 31 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @DetectionType.");
32
33 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"condition", -1))
34 {
35 pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_CONDITION;
36 }
37 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"arp", -1))
38 {
39 pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_ARP;
40 }
41 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
42 {
43 pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_NONE;
44 }
45 else
46 {
47 ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid detection type: %ls", scz);
48 }
49
50 if (BURN_EXE_DETECTION_TYPE_CONDITION == pPackage->Exe.detectionType)
51 {
52 // @DetectCondition
53 hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition);
54 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @DetectCondition.");
55
56 // @UninstallArguments
57 hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
58 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments.");
59
60 // @Uninstallable
61 hr = XmlGetYesNoAttribute(pixnExePackage, L"Uninstallable", &pPackage->Exe.fUninstallable);
62 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Uninstallable.");
63 }
64 else if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType)
65 {
66 // @ArpId
67 hr = XmlGetAttributeEx(pixnExePackage, L"ArpId", &scz);
68 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @ArpId.");
69
70 hr = PathConcatRelativeToBase(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\", scz, &pPackage->Exe.sczArpKeyPath);
71 ExitOnFailure(hr, "Failed to build full key path.");
72
73 // @ArpDisplayVersion
74 hr = XmlGetAttributeEx(pixnExePackage, L"ArpDisplayVersion", &scz);
75 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @ArpDisplayVersion.");
76
77 hr = VerParseVersion(scz, 0, FALSE, &pPackage->Exe.pArpDisplayVersion);
78 ExitOnFailure(hr, "Failed to parse @ArpDisplayVersion: %ls", scz);
79
80 if (pPackage->Exe.pArpDisplayVersion->fInvalid)
81 {
82 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
83 }
84
85 // @ArpWin64
86 hr = XmlGetYesNoAttribute(pixnExePackage, L"ArpWin64", &pPackage->Exe.fArpWin64);
87 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @ArpWin64.");
88
89 pPackage->Exe.fUninstallable = TRUE;
90 }
22 91
23 // @InstallArguments 92 // @InstallArguments
24 hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments); 93 hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments);
25 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments."); 94 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments.");
26 95
27 // @UninstallArguments
28 hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
29 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments.");
30
31 // @RepairArguments 96 // @RepairArguments
32 hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments); 97 hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments);
33 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairArguments."); 98 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairArguments.");
@@ -36,10 +101,6 @@ extern "C" HRESULT ExeEngineParsePackageFromXml(
36 hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable); 101 hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable);
37 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Repairable."); 102 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Repairable.");
38 103
39 // @Uninstallable
40 hr = XmlGetYesNoAttribute(pixnExePackage, L"Uninstallable", &pPackage->Exe.fUninstallable);
41 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Uninstallable.");
42
43 // @Bundle 104 // @Bundle
44 hr = XmlGetYesNoAttribute(pixnExePackage, L"Bundle", &pPackage->Exe.fBundle); 105 hr = XmlGetYesNoAttribute(pixnExePackage, L"Bundle", &pPackage->Exe.fBundle);
45 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Bundle."); 106 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Bundle.");
@@ -64,8 +125,7 @@ extern "C" HRESULT ExeEngineParsePackageFromXml(
64 } 125 }
65 else 126 else
66 { 127 {
67 hr = E_UNEXPECTED; 128 ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid protocol type: %ls", scz);
68 ExitOnFailure(hr, "Invalid protocol type: %ls", scz);
69 } 129 }
70 } 130 }
71 131
@@ -91,6 +151,8 @@ extern "C" void ExeEnginePackageUninitialize(
91 ReleaseStr(pPackage->Exe.sczInstallArguments); 151 ReleaseStr(pPackage->Exe.sczInstallArguments);
92 ReleaseStr(pPackage->Exe.sczRepairArguments); 152 ReleaseStr(pPackage->Exe.sczRepairArguments);
93 ReleaseStr(pPackage->Exe.sczUninstallArguments); 153 ReleaseStr(pPackage->Exe.sczUninstallArguments);
154 ReleaseStr(pPackage->Exe.sczArpKeyPath);
155 ReleaseVerutilVersion(pPackage->Exe.pArpDisplayVersion);
94 ReleaseMem(pPackage->Exe.rgExitCodes); 156 ReleaseMem(pPackage->Exe.rgExitCodes);
95 157
96 // free command-line arguments 158 // free command-line arguments
@@ -126,15 +188,31 @@ extern "C" HRESULT ExeEngineDetectPackage(
126 HRESULT hr = S_OK; 188 HRESULT hr = S_OK;
127 BOOL fDetected = FALSE; 189 BOOL fDetected = FALSE;
128 190
129 // evaluate detect condition 191 switch (pPackage->Exe.detectionType)
130 if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition)
131 { 192 {
132 hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected); 193 case BURN_EXE_DETECTION_TYPE_NONE:
133 ExitOnFailure(hr, "Failed to evaluate executable package detect condition."); 194 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
134 } 195 break;
196 case BURN_EXE_DETECTION_TYPE_CONDITION:
197 // evaluate detect condition
198 if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition)
199 {
200 hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected);
201 ExitOnFailure(hr, "Failed to evaluate EXE package detect condition.");
202 }
203
204 // update detect state
205 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
135 206
136 // update detect state 207 break;
137 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; 208 case BURN_EXE_DETECTION_TYPE_ARP:
209 hr = DetectArpEntry(pPackage, &pPackage->currentState, NULL);
210 ExitOnFailure(hr, "Failed to detect EXE package by ArpEntry.");
211
212 break;
213 default:
214 ExitWithRootFailure(hr, E_INVALIDARG, "Unknown EXE package detection type: %d.", pPackage->Exe.detectionType);
215 }
138 216
139 if (pPackage->fCanAffectRegistration) 217 if (pPackage->fCanAffectRegistration)
140 { 218 {
@@ -187,6 +265,7 @@ extern "C" HRESULT ExeEnginePlanCalculatePackage(
187 } 265 }
188 break; 266 break;
189 267
268 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
190 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: 269 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
191 switch (pPackage->requested) 270 switch (pPackage->requested)
192 { 271 {
@@ -205,8 +284,7 @@ extern "C" HRESULT ExeEnginePlanCalculatePackage(
205 break; 284 break;
206 285
207 default: 286 default:
208 hr = E_INVALIDARG; 287 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid package current state: %d.", pPackage->currentState);
209 ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState);
210 } 288 }
211 289
212 // Calculate the rollback action if there is an execute action. 290 // Calculate the rollback action if there is an execute action.
@@ -232,6 +310,7 @@ extern "C" HRESULT ExeEnginePlanCalculatePackage(
232 } 310 }
233 break; 311 break;
234 312
313 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
235 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: 314 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
236 switch (pPackage->requested) 315 switch (pPackage->requested)
237 { 316 {
@@ -251,8 +330,7 @@ extern "C" HRESULT ExeEnginePlanCalculatePackage(
251 break; 330 break;
252 331
253 default: 332 default:
254 hr = E_INVALIDARG; 333 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid package expected state.");
255 ExitOnRootFailure(hr, "Invalid package expected state.");
256 } 334 }
257 } 335 }
258 336
@@ -356,10 +434,41 @@ extern "C" HRESULT ExeEngineExecutePackage(
356 LPWSTR sczUserArgs = NULL; 434 LPWSTR sczUserArgs = NULL;
357 LPWSTR sczUserArgsObfuscated = NULL; 435 LPWSTR sczUserArgsObfuscated = NULL;
358 LPWSTR sczCommandObfuscated = NULL; 436 LPWSTR sczCommandObfuscated = NULL;
437 LPWSTR sczArpUninstallString = NULL;
438 LPWSTR sczArpArguments = NULL;
439 BOOTSTRAPPER_PACKAGE_STATE applyState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
359 HANDLE hExecutableFile = INVALID_HANDLE_VALUE; 440 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
360 DWORD dwExitCode = 0; 441 DWORD dwExitCode = 0;
361 BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; 442 BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage;
362 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; 443 BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload;
444 LPCWSTR wzUninstallArguments = pPackage->Exe.sczUninstallArguments;
445
446 if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType &&
447 (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action ||
448 BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->exePackage.action && fRollback))
449 {
450 hr = DetectArpEntry(pPackage, &applyState, &sczArpUninstallString);
451 ExitOnFailure(hr, "Failed to query ArpEntry for uninstall.");
452
453 if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == applyState && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action)
454 {
455 if (fRollback)
456 {
457 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), LoggingPackageStateToString(applyState));
458 }
459 else
460 {
461 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId);
462 }
463
464 ExitFunction();
465 }
466 else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT != applyState && BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->exePackage.action)
467 {
468 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), LoggingPackageStateToString(applyState));
469 ExitFunction();
470 }
471 }
363 472
364 if (pPackage->Exe.fPseudoPackage && BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE != pPackagePayload->verification) 473 if (pPackage->Exe.fPseudoPackage && BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE != pPackagePayload->verification)
365 { 474 {
@@ -372,7 +481,30 @@ extern "C" HRESULT ExeEngineExecutePackage(
372 ExitOnFailure(hr, "Failed to build executable path."); 481 ExitOnFailure(hr, "Failed to build executable path.");
373 482
374 hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory); 483 hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory);
375 ExitOnFailure(hr, "Failed to get cached path for pseudo-package: %ls", pPackage->sczId); 484 ExitOnFailure(hr, "Failed to get parent directory for pseudo-package: %ls", pPackage->sczId);
485 }
486 else if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action)
487 {
488 ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "QuietUninstallString is null.");
489
490 hr = ParseArpUninstallString(sczArpUninstallString, &sczExecutablePath, &sczArpArguments);
491 ExitOnFailure(hr, "Failed to parse QuietUninstallString: %ls.", sczArpUninstallString);
492
493 if (pPackage->fPerMachine)
494 {
495 hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath);
496 ExitOnFailure(hr, "Failed to verify the QuietUninstallString executable path is in a secure location: %ls", sczExecutablePath);
497 if (S_FALSE == hr)
498 {
499 LogStringLine(REPORT_STANDARD, "The QuietUninstallString executable path is not in a secure location: %ls", sczExecutablePath);
500 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
501 }
502 }
503
504 hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory);
505 ExitOnFailure(hr, "Failed to get parent directory for QuietUninstallString executable path: %ls", sczExecutablePath);
506
507 wzUninstallArguments = sczArpArguments;
376 } 508 }
377 else 509 else
378 { 510 {
@@ -396,7 +528,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
396 break; 528 break;
397 529
398 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: 530 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
399 wzArguments = pPackage->Exe.sczUninstallArguments; 531 wzArguments = wzUninstallArguments;
400 break; 532 break;
401 533
402 case BOOTSTRAPPER_ACTION_STATE_REPAIR: 534 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
@@ -404,8 +536,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
404 break; 536 break;
405 537
406 default: 538 default:
407 hr = E_INVALIDARG; 539 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
408 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
409 } 540 }
410 541
411 // now add optional arguments 542 // now add optional arguments
@@ -443,8 +574,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
443 break; 574 break;
444 575
445 default: 576 default:
446 hr = E_INVALIDARG; 577 ExitWithRootFailure(hr, E_INVALIDARG, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
447 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
448 } 578 }
449 } 579 }
450 } 580 }
@@ -524,6 +654,8 @@ LExit:
524 StrSecureZeroFreeString(sczUserArgs); 654 StrSecureZeroFreeString(sczUserArgs);
525 ReleaseStr(sczUserArgsObfuscated); 655 ReleaseStr(sczUserArgsObfuscated);
526 ReleaseStr(sczCommandObfuscated); 656 ReleaseStr(sczCommandObfuscated);
657 ReleaseStr(sczArpUninstallString);
658 ReleaseStr(sczArpArguments);
527 659
528 ReleaseFileHandle(hExecutableFile); 660 ReleaseFileHandle(hExecutableFile);
529 661
@@ -894,3 +1026,106 @@ extern "C" HRESULT ExeEngineHandleExitCode(
894//LExit: 1026//LExit:
895 return hr; 1027 return hr;
896} 1028}
1029
1030static HRESULT DetectArpEntry(
1031 __in const BURN_PACKAGE* pPackage,
1032 __out BOOTSTRAPPER_PACKAGE_STATE* pPackageState,
1033 __out_opt LPWSTR* psczQuietUninstallString
1034 )
1035{
1036 HRESULT hr = S_OK;
1037 HKEY hKey = NULL;
1038 VERUTIL_VERSION* pVersion = NULL;
1039 int nCompareResult = 0;
1040 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1041 REG_KEY_BITNESS keyBitness = pPackage->Exe.fArpWin64 ? REG_KEY_64BIT : REG_KEY_32BIT;
1042
1043 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
1044 if (psczQuietUninstallString)
1045 {
1046 ReleaseNullStr(*psczQuietUninstallString);
1047 }
1048
1049 hr = RegOpenEx(hkRoot, pPackage->Exe.sczArpKeyPath, KEY_READ, keyBitness, &hKey);
1050 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
1051 {
1052 ExitFunction1(hr = S_OK);
1053 }
1054 ExitOnFailure(hr, "Failed to open registry key: %ls.", pPackage->Exe.sczArpKeyPath);
1055
1056 hr = RegReadWixVersion(hKey, L"DisplayVersion", &pVersion);
1057 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
1058 {
1059 ExitFunction1(hr = S_OK);
1060 }
1061 ExitOnFailure(hr, "Failed to read DisplayVersion.");
1062
1063 if (pVersion->fInvalid)
1064 {
1065 LogId(REPORT_WARNING, MSG_DETECTED_EXE_PACKAGE_INVALID_VERSION, pPackage->Exe.sczArpKeyPath, pVersion->sczVersion);
1066 }
1067
1068 hr = VerCompareParsedVersions(pPackage->Exe.pArpDisplayVersion, pVersion, &nCompareResult);
1069 ExitOnFailure(hr, "Failed to compare versions.");
1070
1071 if (nCompareResult < 0)
1072 {
1073 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
1074 }
1075 else if (nCompareResult > 0)
1076 {
1077 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
1078 }
1079 else
1080 {
1081 *pPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
1082 }
1083
1084 if (psczQuietUninstallString)
1085 {
1086 hr = RegReadString(hKey, L"QuietUninstallString", psczQuietUninstallString);
1087 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
1088 {
1089 hr = S_OK;
1090 }
1091 ExitOnFailure(hr, "Failed to read QuietUninstallString.");
1092 }
1093
1094LExit:
1095 ReleaseRegKey(hKey);
1096 ReleaseVerutilVersion(pVersion);
1097
1098 return hr;
1099}
1100
1101static HRESULT ParseArpUninstallString(
1102 __in_z LPCWSTR wzArpUninstallString,
1103 __inout LPWSTR* psczExecutablePath,
1104 __inout LPWSTR* psczArguments
1105 )
1106{
1107 HRESULT hr = S_OK;
1108 int argc = 0;
1109 LPWSTR* argv = NULL;
1110
1111 hr = AppParseCommandLine(wzArpUninstallString, &argc, &argv);
1112 ExitOnFailure(hr, "Failed to parse uninstall string as command line: %ls.", wzArpUninstallString);
1113 ExitOnNull(argc, hr, E_INVALIDARG, "Uninstall string must contain an executable path.");
1114
1115 hr = StrAllocString(psczExecutablePath, argv[0], 0);
1116 ExitOnFailure(hr, "Failed to copy executable path for ArpCommand.");
1117
1118 for (int i = 1; i < argc; ++i)
1119 {
1120 hr = AppAppendCommandLineArgument(psczArguments, argv[i]);
1121 ExitOnFailure(hr, "Failed to append argument for ArpCommand.");
1122 }
1123
1124LExit:
1125 if (argv)
1126 {
1127 AppFreeCommandLineArgs(argv);
1128 }
1129
1130 return hr;
1131}