main
cpp 1,486 lines 51.8 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 // constants
6
7 #define INITIAL_STRINGDICT_SIZE 48
8 const LPCWSTR vcszIgnoreDependenciesDelim = L";";
9
10
11 // internal function declarations
12
13 static HRESULT DetectPackageDependents(
14 __in BURN_PACKAGE* pPackage,
15 __in const BURN_REGISTRATION* pRegistration
16 );
17
18 static HRESULT SplitIgnoreDependencies(
19 __in_z LPCWSTR wzIgnoreDependencies,
20 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
21 __inout LPUINT pcDependencies,
22 __out BOOL* pfIgnoreAll
23 );
24
25 static HRESULT JoinIgnoreDependencies(
26 __out_z LPWSTR* psczIgnoreDependencies,
27 __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
28 __in UINT cDependencies
29 );
30
31 static HRESULT GetIgnoredDependents(
32 __in const BURN_PACKAGE* pPackage,
33 __in const BURN_PLAN* pPlan,
34 __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
35 );
36
37 static BOOL GetProviderExists(
38 __in HKEY hkRoot,
39 __in_z LPCWSTR wzProviderKey
40 );
41
42 static void CalculateDependencyActionStates(
43 __in const BURN_PACKAGE* pPackage,
44 __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
45 __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
46 );
47
48 static HRESULT AddPackageDependencyActions(
49 __in_opt DWORD *pdwInsertSequence,
50 __in const BURN_PACKAGE* pPackage,
51 __in BURN_PLAN* pPlan,
52 __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
53 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
54 );
55
56 static LPCWSTR GetPackageProviderId(
57 __in const BURN_PACKAGE* pPackage
58 );
59
60 static HRESULT RegisterPackageProvider(
61 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
62 __in LPCWSTR wzPackageId,
63 __in LPCWSTR wzPackageProviderId,
64 __in HKEY hkRoot,
65 __in BOOL fVital
66 );
67
68 static void UnregisterPackageProvider(
69 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
70 __in LPCWSTR wzPackageId,
71 __in HKEY hkRoot
72 );
73
74 static HRESULT RegisterPackageProviderDependent(
75 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
76 __in BOOL fVital,
77 __in HKEY hkRoot,
78 __in LPCWSTR wzPackageId,
79 __in_z LPCWSTR wzDependentProviderKey
80 );
81
82 static void UnregisterPackageDependency(
83 __in BOOL fPerMachine,
84 __in const BURN_PACKAGE* pPackage,
85 __in_z LPCWSTR wzDependentProviderKey
86 );
87
88 static void UnregisterPackageProviderDependent(
89 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
90 __in HKEY hkRoot,
91 __in LPCWSTR wzPackageId,
92 __in_z LPCWSTR wzDependentProviderKey
93 );
94 static void UnregisterOrphanPackageProviders(
95 __in const BURN_PACKAGE* pPackage
96 );
97
98
99 // functions
100
101 extern "C" void DependencyUninitializeProvider(
102 __in BURN_DEPENDENCY_PROVIDER* pProvider
103 )
104 {
105 ReleaseStr(pProvider->sczKey);
106 ReleaseStr(pProvider->sczVersion);
107 ReleaseStr(pProvider->sczDisplayName);
108 ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents);
109
110 memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER));
111 }
112
113 extern "C" HRESULT DependencyParseProvidersFromXml(
114 __in BURN_PACKAGE* pPackage,
115 __in IXMLDOMNode* pixnPackage
116 )
117 {
118 HRESULT hr = S_OK;
119 IXMLDOMNodeList* pixnNodes = NULL;
120 DWORD cNodes = 0;
121 IXMLDOMNode* pixnNode = NULL;
122
123 // Select dependency provider nodes.
124 hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes);
125 ExitOnFailure(hr, "Failed to select dependency provider nodes.");
126
127 // Get dependency provider node count.
128 hr = pixnNodes->get_length((long*)&cNodes);
129 ExitOnFailure(hr, "Failed to get the dependency provider node count.");
130
131 if (!cNodes)
132 {
133 ExitFunction1(hr = S_OK);
134 }
135
136 // Allocate memory for dependency provider pointers.
137 pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE);
138 ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
139
140 pPackage->cDependencyProviders = cNodes;
141
142 // Parse dependency provider elements.
143 for (DWORD i = 0; i < cNodes; i++)
144 {
145 BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i];
146
147 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
148 ExitOnFailure(hr, "Failed to get the next dependency provider node.");
149
150 // @Key
151 hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey);
152 ExitOnFailure(hr, "Failed to get the Key attribute.");
153
154 // @Version
155 hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion);
156 if (E_NOTFOUND != hr)
157 {
158 ExitOnFailure(hr, "Failed to get the Version attribute.");
159 }
160
161 // @DisplayName
162 hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName);
163 if (E_NOTFOUND != hr)
164 {
165 ExitOnFailure(hr, "Failed to get the DisplayName attribute.");
166 }
167
168 // @Imported
169 hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported);
170 if (E_NOTFOUND != hr)
171 {
172 ExitOnFailure(hr, "Failed to get the Imported attribute.");
173 }
174 else
175 {
176 pDependencyProvider->fImported = FALSE;
177 hr = S_OK;
178 }
179
180 // Prepare next iteration.
181 ReleaseNullObject(pixnNode);
182 }
183
184 hr = S_OK;
185
186 LExit:
187 ReleaseObject(pixnNode);
188 ReleaseObject(pixnNodes);
189
190 return hr;
191 }
192
193 extern "C" HRESULT DependencyInitialize(
194 __in BURN_ENGINE_COMMAND* pInternalCommand,
195 __in BURN_DEPENDENCIES* pDependencies,
196 __in BURN_REGISTRATION* pRegistration
197 )
198 {
199 AssertSz(!pDependencies->cIgnoredDependencies, "Dependencies already initalized.");
200
201 HRESULT hr = S_OK;
202
203 // If no parent was specified at all, use the bundle id as the self dependent.
204 if (!pInternalCommand->sczActiveParent)
205 {
206 pDependencies->wzSelfDependent = pRegistration->sczId;
207 }
208 else if (*pInternalCommand->sczActiveParent) // if parent was specified use that as the self dependent.
209 {
210 pDependencies->wzSelfDependent = pInternalCommand->sczActiveParent;
211 }
212 // else parent:none was used which means we should not register a dependency on ourself.
213
214 pDependencies->wzActiveParent = pInternalCommand->sczActiveParent;
215
216 // The current bundle provider key should always be ignored for dependency checks.
217 hr = DepDependencyArrayAlloc(&pDependencies->rgIgnoredDependencies, &pDependencies->cIgnoredDependencies, pRegistration->sczProviderKey, NULL);
218 ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore.");
219
220 // Add the list of dependencies to ignore.
221 if (pInternalCommand->sczIgnoreDependencies)
222 {
223 hr = SplitIgnoreDependencies(pInternalCommand->sczIgnoreDependencies, &pDependencies->rgIgnoredDependencies, &pDependencies->cIgnoredDependencies, &pDependencies->fIgnoreAllDependents);
224 ExitOnFailure(hr, "Failed to split the list of dependencies to ignore.");
225 }
226
227 pDependencies->fSelfDependent = NULL != pDependencies->wzSelfDependent;
228 pDependencies->fActiveParent = NULL != pInternalCommand->sczActiveParent && NULL != *pInternalCommand->sczActiveParent;
229
230 LExit:
231 return hr;
232 }
233
234 extern "C" void DependencyUninitialize(
235 __in BURN_DEPENDENCIES* pDependencies
236 )
237 {
238 if (pDependencies->rgIgnoredDependencies)
239 {
240 ReleaseDependencyArray(pDependencies->rgIgnoredDependencies, pDependencies->cIgnoredDependencies);
241 }
242
243 memset(pDependencies, 0, sizeof(BURN_DEPENDENCIES));
244 }
245
246 extern "C" HRESULT DependencyDetectProviderKeyBundleId(
247 __in BURN_REGISTRATION* pRegistration
248 )
249 {
250 HRESULT hr = S_OK;
251
252 hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL);
253 if (E_NOTFOUND == hr)
254 {
255 ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId);
256 ExitFunction1(hr = S_OK);
257 }
258 ExitOnFailure(hr, "Failed to get provider key bundle id.");
259
260 // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key.
261 if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId)
262 {
263 hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0);
264 ExitOnFailure(hr, "Failed to initialize provider key bundle id.");
265 }
266 else if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1))
267 {
268 pRegistration->fDetectedForeignProviderKeyBundleId = TRUE;
269 LogId(REPORT_STANDARD, MSG_DETECTED_FOREIGN_BUNDLE_PROVIDER_REGISTRATION, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId);
270 }
271
272 LExit:
273 return hr;
274 }
275
276 extern "C" HRESULT DependencyDetectBundle(
277 __in BURN_DEPENDENCIES* pDependencies,
278 __in BURN_REGISTRATION* pRegistration
279 )
280 {
281 HRESULT hr = S_OK;
282 BOOL fExists = FALSE;
283
284 hr = DependencyDetectProviderKeyBundleId(pRegistration);
285 ExitOnFailure(hr, "Failed to detect provider key bundle id.");
286
287 hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, NULL, &pRegistration->rgDependents, &pRegistration->cDependents);
288 ExitOnPathFailure(hr, fExists, "Failed dependents check on bundle.");
289
290 if (pDependencies->fSelfDependent || pDependencies->fActiveParent)
291 {
292 for (DWORD i = 0; i < pRegistration->cDependents; ++i)
293 {
294 DEPENDENCY* pDependent = pRegistration->rgDependents + i;
295
296 if (pDependencies->fActiveParent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pDependencies->wzActiveParent, -1, pDependent->sczKey, -1))
297 {
298 pRegistration->fParentRegisteredAsDependent = TRUE;
299 }
300
301 if (pDependencies->fSelfDependent && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pDependencies->wzSelfDependent, -1, pDependent->sczKey, -1))
302 {
303 pRegistration->fSelfRegisteredAsDependent = TRUE;
304 }
305 }
306 }
307
308 LExit:
309 return hr;
310 }
311
312 extern "C" HRESULT DependencyDetectChainPackage(
313 __in BURN_PACKAGE* pPackage,
314 __in BURN_REGISTRATION* pRegistration
315 )
316 {
317 HRESULT hr = S_OK;
318
319 hr = DetectPackageDependents(pPackage, pRegistration);
320 ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId);
321
322 hr = DependencyDetectCompatibleEntry(pPackage, pRegistration);
323 ExitOnFailure(hr, "Failed to detect compatible package for package '%ls'", pPackage->sczId);
324
325 LExit:
326 return hr;
327 }
328
329 extern "C" HRESULT DependencyDetectRelatedBundle(
330 __in BURN_RELATED_BUNDLE* pRelatedBundle,
331 __in BURN_REGISTRATION* pRegistration
332 )
333 {
334 HRESULT hr = S_OK;
335 BURN_PACKAGE* pPackage = &pRelatedBundle->package;
336
337 if (pRelatedBundle->fPlannable)
338 {
339 hr = DetectPackageDependents(pPackage, pRegistration);
340 ExitOnFailure(hr, "Failed to detect dependents for related bundle '%ls'", pPackage->sczId);
341 }
342
343 LExit:
344 return hr;
345 }
346
347 extern "C" HRESULT DependencyPlanInitialize(
348 __in BURN_DEPENDENCIES* pDependencies,
349 __in BURN_PLAN* pPlan
350 )
351 {
352 HRESULT hr = S_OK;
353
354 // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere.
355 for (DWORD i = 0; i < pDependencies->cIgnoredDependencies; ++i)
356 {
357 DEPENDENCY* pDependency = pDependencies->rgIgnoredDependencies + i;
358
359 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pDependency->sczKey, pDependency->sczName);
360 ExitOnFailure(hr, "Failed to add the detected provider to the list of dependencies to ignore.");
361 }
362
363 LExit:
364 return hr;
365 }
366
367 extern "C" HRESULT DependencyAllocIgnoreDependencies(
368 __in const BURN_PLAN *pPlan,
369 __out_z LPWSTR* psczIgnoreDependencies
370 )
371 {
372 HRESULT hr = S_OK;
373
374 // Join the list of dependencies to ignore for each related bundle.
375 if (0 < pPlan->cPlannedProviders)
376 {
377 hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
378 ExitOnFailure(hr, "Failed to join the list of dependencies to ignore.");
379 }
380
381 LExit:
382 return hr;
383 }
384
385 extern "C" HRESULT DependencyAddIgnoreDependencies(
386 __in STRINGDICT_HANDLE sdIgnoreDependencies,
387 __in_z LPCWSTR wzAddIgnoreDependencies
388 )
389 {
390 HRESULT hr = S_OK;
391 LPWSTR wzContext = NULL;
392
393 // Parse through the semicolon-delimited tokens and add to the array.
394 for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
395 {
396 hr = DictKeyExists(sdIgnoreDependencies, wzToken);
397 if (E_NOTFOUND != hr)
398 {
399 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
400 }
401 else
402 {
403 hr = DictAddKey(sdIgnoreDependencies, wzToken);
404 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
405 }
406 }
407
408 LExit:
409 return hr;
410 }
411
412 extern "C" HRESULT DependencyPlanPackageBegin(
413 __in BOOL fPerMachine,
414 __in BURN_PACKAGE* pPackage,
415 __in BURN_PLAN* pPlan
416 )
417 {
418 HRESULT hr = S_OK;
419 STRINGDICT_HANDLE sdIgnoredDependents = NULL;
420 BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE;
421 BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE;
422 BOOL fDependentBlocksUninstall = FALSE;
423 BOOL fAttemptingUninstall = BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute || pPackage->compatiblePackage.fRemove;
424
425 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
426 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
427
428 // Make sure the package defines at least one provider.
429 if (0 == pPackage->cDependencyProviders)
430 {
431 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId);
432 ExitFunction1(hr = S_OK);
433 }
434
435 // Make sure the package is in the same scope as the bundle.
436 if (fPerMachine != pPackage->fPerMachine)
437 {
438 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
439 ExitFunction1(hr = S_OK);
440 }
441
442 // Check if any dependents are registered which would prevent the package from being uninstalled.
443 // Build up a list of dependents to ignore, including the current bundle.
444 hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents);
445 ExitOnFailure(hr, "Failed to build the list of ignored dependents.");
446
447 // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES.
448 hr = DictKeyExists(sdIgnoredDependents, L"ALL");
449 if (E_NOTFOUND != hr)
450 {
451 ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES.");
452 }
453 else
454 {
455 hr = S_OK;
456
457 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
458 {
459 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i;
460
461 for (DWORD j = 0; j < pProvider->cDependents; ++j)
462 {
463 const DEPENDENCY* pDependency = pProvider->rgDependents + j;
464
465 hr = DictKeyExists(sdIgnoredDependents, pDependency->sczKey);
466 if (E_NOTFOUND == hr)
467 {
468 hr = S_OK;
469
470 if (!fDependentBlocksUninstall)
471 {
472 fDependentBlocksUninstall = TRUE;
473
474 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId);
475 }
476
477 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName));
478 }
479 ExitOnFailure(hr, "Failed to check the dictionary of ignored dependents.");
480 }
481 }
482 }
483
484 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
485 {
486 pPackage->fDependencyManagerWasHere = fDependentBlocksUninstall;
487 }
488
489 // Calculate the dependency actions before the package itself is planned.
490 CalculateDependencyActionStates(pPackage, &dependencyExecuteAction, &dependencyRollbackAction);
491
492 // If dependents were found, change the action to not uninstall the package.
493 if (fAttemptingUninstall && fDependentBlocksUninstall)
494 {
495 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
496 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
497
498 // Assume the compatible package has the same exact providers.
499 pPackage->compatiblePackage.fRemove = FALSE;
500 }
501 else
502 {
503 // Trust the forward compatible nature of providers - don't uninstall the package during rollback if there were dependents.
504 if (fDependentBlocksUninstall && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback)
505 {
506 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
507 }
508
509 // Only plan providers when the package is current (not obsolete).
510 if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState)
511 {
512 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
513 {
514 BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
515
516 // Only need to handle providers that were authored directly in the bundle.
517 if (pProvider->fImported)
518 {
519 continue;
520 }
521
522 pProvider->providerExecute = dependencyExecuteAction;
523 pProvider->providerRollback = dependencyRollbackAction;
524
525 // Don't overwrite providers that we don't own.
526 if (pPackage->compatiblePackage.fDetected)
527 {
528 if (BURN_DEPENDENCY_ACTION_REGISTER == pProvider->providerExecute)
529 {
530 pProvider->providerExecute = BURN_DEPENDENCY_ACTION_NONE;
531 pProvider->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
532 }
533
534 if (BURN_DEPENDENCY_ACTION_REGISTER == pProvider->providerRollback)
535 {
536 pProvider->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
537 }
538 }
539
540 if (BURN_DEPENDENCY_ACTION_UNREGISTER == pProvider->providerExecute && !pProvider->fExists)
541 {
542 pProvider->providerExecute = BURN_DEPENDENCY_ACTION_NONE;
543 }
544
545 if (BURN_DEPENDENCY_ACTION_UNREGISTER == pProvider->providerRollback && pProvider->fExists ||
546 BURN_DEPENDENCY_ACTION_REGISTER == pProvider->providerRollback && !pProvider->fExists)
547 {
548 pProvider->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
549 }
550
551 if (BURN_DEPENDENCY_ACTION_NONE != pProvider->providerExecute)
552 {
553 pPackage->fProviderExecute = TRUE;
554 }
555
556 if (BURN_DEPENDENCY_ACTION_NONE != pProvider->providerRollback)
557 {
558 pPackage->fProviderRollback = TRUE;
559 }
560 }
561 }
562
563 // If the package will be removed, add its providers to the growing list in the plan.
564 if (fAttemptingUninstall)
565 {
566 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
567 {
568 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
569
570 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL);
571 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
572 }
573 }
574 }
575
576 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
577 {
578 BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
579
580 pProvider->dependentExecute = dependencyExecuteAction;
581 pProvider->dependentRollback = dependencyRollbackAction;
582
583 if (BURN_DEPENDENCY_ACTION_REGISTER == pProvider->dependentRollback &&
584 BURN_DEPENDENCY_ACTION_UNREGISTER == pProvider->providerExecute && BURN_DEPENDENCY_ACTION_REGISTER != pProvider->providerRollback)
585 {
586 pProvider->dependentRollback = BURN_DEPENDENCY_ACTION_NONE;
587 }
588
589 if (BURN_DEPENDENCY_ACTION_UNREGISTER == pProvider->dependentExecute && !pProvider->fBundleRegisteredAsDependent)
590 {
591 pProvider->dependentExecute = BURN_DEPENDENCY_ACTION_NONE;
592 }
593
594 if (BURN_DEPENDENCY_ACTION_UNREGISTER == pProvider->dependentRollback && pProvider->fBundleRegisteredAsDependent ||
595 BURN_DEPENDENCY_ACTION_REGISTER == pProvider->dependentRollback && !pProvider->fBundleRegisteredAsDependent)
596 {
597 pProvider->dependentRollback = BURN_DEPENDENCY_ACTION_NONE;
598 }
599
600 // The highest aggregate action state found will be returned.
601 if (pPackage->dependencyExecute < pProvider->dependentExecute)
602 {
603 pPackage->dependencyExecute = pProvider->dependentExecute;
604 }
605
606 if (pPackage->dependencyRollback < pProvider->dependentRollback)
607 {
608 pPackage->dependencyRollback = pProvider->dependentRollback;
609 }
610 }
611
612 LExit:
613 ReleaseDict(sdIgnoredDependents);
614
615 return hr;
616 }
617
618 extern "C" HRESULT DependencyPlanPackage(
619 __in_opt DWORD *pdwInsertSequence,
620 __in const BURN_PACKAGE* pPackage,
621 __in BURN_PLAN* pPlan
622 )
623 {
624 HRESULT hr = S_OK;
625 BURN_EXECUTE_ACTION* pAction = NULL;
626
627 // If the dependency execution action is to unregister, add the dependency actions to the plan
628 // *before* the provider key is potentially removed.
629 if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute)
630 {
631 hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
632 ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
633 }
634
635 // Add the provider rollback plan.
636 if (pPackage->fProviderRollback)
637 {
638 hr = PlanAppendRollbackAction(pPlan, &pAction);
639 ExitOnFailure(hr, "Failed to append provider rollback action.");
640
641 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
642 pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
643
644 // Put a checkpoint before the execute action so that rollback happens
645 // if execute fails.
646 hr = PlanExecuteCheckpoint(pPlan);
647 ExitOnFailure(hr, "Failed to plan provider checkpoint action.");
648 }
649
650 // Add the provider execute plan. This comes after rollback so if something goes wrong
651 // rollback will try to clean up after us.
652 if (pPackage->fProviderExecute)
653 {
654 if (NULL != pdwInsertSequence)
655 {
656 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
657 ExitOnFailure(hr, "Failed to insert provider execute action.");
658
659 // Always move the sequence after this dependency action so the provider registration
660 // stays in front of the inserted actions.
661 ++(*pdwInsertSequence);
662 }
663 else
664 {
665 hr = PlanAppendExecuteAction(pPlan, &pAction);
666 ExitOnFailure(hr, "Failed to append provider execute action.");
667 }
668
669 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
670 pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
671 }
672
673 LExit:
674 return hr;
675 }
676
677 extern "C" HRESULT DependencyPlanPackageComplete(
678 __in BURN_PACKAGE* pPackage,
679 __in BURN_PLAN* pPlan
680 )
681 {
682 HRESULT hr = S_OK;
683
684 // Registration of dependencies happens here, after the package is planned to be
685 // installed and all that good stuff.
686 if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
687 {
688 hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
689 ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
690 }
691
692 LExit:
693 return hr;
694 }
695
696 extern "C" HRESULT DependencyExecutePackageProviderAction(
697 __in const BURN_EXECUTE_ACTION* pAction,
698 __in BOOL fRollback
699 )
700 {
701 AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function.");
702
703 HRESULT hr = S_OK;
704 const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage;
705 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
706 LPCWSTR wzId = GetPackageProviderId(pPackage);
707
708 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
709 {
710 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i;
711 BURN_DEPENDENCY_ACTION action = fRollback ? pProvider->providerRollback : pProvider->providerExecute;
712 HRESULT hrProvider = S_OK;
713
714 // Register or unregister the package provider.
715 switch (action)
716 {
717 case BURN_DEPENDENCY_ACTION_REGISTER:
718 hrProvider = RegisterPackageProvider(pProvider, pPackage->sczId, wzId, hkRoot, pPackage->fVital);
719 if (SUCCEEDED(hr) && FAILED(hrProvider))
720 {
721 hr = hrProvider;
722 }
723 break;
724 case BURN_DEPENDENCY_ACTION_UNREGISTER:
725 UnregisterPackageProvider(pProvider, pPackage->sczId, hkRoot);
726 break;
727 }
728 }
729
730 if (!pPackage->fVital)
731 {
732 hr = S_OK;
733 }
734
735 return hr;
736 }
737
738 extern "C" HRESULT DependencyExecutePackageDependencyAction(
739 __in BOOL fPerMachine,
740 __in const BURN_EXECUTE_ACTION* pAction,
741 __in BOOL fRollback
742 )
743 {
744 AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function.");
745
746 HRESULT hr = S_OK;
747 const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage;
748 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
749
750 // Do not register a dependency on a package in a different install context.
751 if (fPerMachine != pPackage->fPerMachine)
752 {
753 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
754 ExitFunction1(hr = S_OK);
755 }
756
757 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
758 {
759 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i;
760 BURN_DEPENDENCY_ACTION action = fRollback ? pProvider->dependentRollback : pProvider->dependentExecute;
761 HRESULT hrProvider = S_OK;
762
763 // Register or unregister the bundle as a dependent of the package dependency provider.
764 switch (action)
765 {
766 case BURN_DEPENDENCY_ACTION_REGISTER:
767 hrProvider = RegisterPackageProviderDependent(pProvider, pPackage->fVital, hkRoot, pPackage->sczId, pAction->packageDependency.sczBundleProviderKey);
768 if (SUCCEEDED(hr) && FAILED(hrProvider))
769 {
770 hr = hrProvider;
771 }
772 break;
773 case BURN_DEPENDENCY_ACTION_UNREGISTER:
774 UnregisterPackageProviderDependent(pProvider, hkRoot, pPackage->sczId, pAction->packageDependency.sczBundleProviderKey);
775 break;
776 }
777 }
778
779 LExit:
780 if (!pPackage->fVital)
781 {
782 hr = S_OK;
783 }
784
785 return hr;
786 }
787
788 extern "C" HRESULT DependencyRegisterBundle(
789 __in const BURN_REGISTRATION* pRegistration
790 )
791 {
792 HRESULT hr = S_OK;
793
794 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion);
795
796 // Register the bundle provider key.
797 hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, pRegistration->pVersion->sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0);
798 ExitOnFailure(hr, "Failed to register the bundle dependency provider.");
799
800 LExit:
801 return hr;
802 }
803
804 extern "C" HRESULT DependencyProcessDependentRegistration(
805 __in const BURN_REGISTRATION* pRegistration,
806 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
807 )
808 {
809 HRESULT hr = S_OK;
810 BOOL fDeleted = FALSE;
811
812 switch (pAction->type)
813 {
814 case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER:
815 hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0);
816 ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey);
817 break;
818
819 case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER:
820 hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey);
821 ExitOnPathFailure(hr, fDeleted, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey);
822 break;
823
824 default:
825 ExitWithRootFailure(hr, E_INVALIDARG, "Unrecognized registration action type: %d", pAction->type);
826 }
827
828 LExit:
829 return hr;
830 }
831
832 extern "C" void DependencyUnregisterBundle(
833 __in const BURN_REGISTRATION* pRegistration,
834 __in const BURN_PACKAGES* pPackages
835 )
836 {
837 HRESULT hr = S_OK;
838 LPCWSTR wzDependentProviderKey = pRegistration->sczId;
839
840 // If we own the bundle dependency then remove it.
841 if (!pRegistration->fDetectedForeignProviderKeyBundleId)
842 {
843 // Remove the bundle provider key.
844 hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey);
845 if (SUCCEEDED(hr) || E_FILENOTFOUND == hr)
846 {
847 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey);
848 }
849 else
850 {
851 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr);
852 }
853 }
854
855 // Best effort to make sure this bundle is not registered as a dependent for anything.
856 for (DWORD i = 0; i < pPackages->cPackages; ++i)
857 {
858 const BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
859 UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey);
860 }
861
862 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
863 {
864 const BURN_PACKAGE* pPackage = &pRegistration->relatedBundles.rgRelatedBundles[i].package;
865 UnregisterPackageDependency(pPackage->fPerMachine, pPackage, wzDependentProviderKey);
866 }
867
868 // Best effort to make sure package providers are removed if unused.
869 for (DWORD i = 0; i < pPackages->cPackages; ++i)
870 {
871 const BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
872 UnregisterOrphanPackageProviders(pPackage);
873 }
874 }
875
876 extern "C" HRESULT DependencyDetectCompatibleEntry(
877 __in BURN_PACKAGE* pPackage,
878 __in BURN_REGISTRATION* pRegistration
879 )
880 {
881 HRESULT hr = S_OK;
882 LPWSTR sczId = NULL;
883 LPWSTR sczName = NULL;
884 LPWSTR sczVersion = NULL;
885 LPCWSTR wzPackageProviderId = GetPackageProviderId(pPackage);
886 HKEY hkHive = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
887
888 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
889 {
890 BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
891
892 hr = DepGetProviderInformation(hkHive, pProvider->sczKey, &sczId, &sczName, &sczVersion);
893 if (E_NOTFOUND == hr)
894 {
895 hr = S_OK;
896 continue;
897 }
898 ExitOnFailure(hr, "Failed to get provider information for compatible package: %ls", pProvider->sczKey);
899
900 // Make sure the compatible package is not the package itself.
901 if (!wzPackageProviderId)
902 {
903 if (!sczId)
904 {
905 continue;
906 }
907 }
908 else if (sczId && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzPackageProviderId, -1, sczId, -1))
909 {
910 continue;
911 }
912
913 pPackage->compatiblePackage.fDetected = TRUE;
914
915 hr = StrAllocString(&pPackage->compatiblePackage.compatibleEntry.sczProviderKey, pProvider->sczKey, 0);
916 ExitOnFailure(hr, "Failed to copy provider key for compatible entry.");
917
918 pPackage->compatiblePackage.compatibleEntry.sczId = sczId;
919 sczId = NULL;
920
921 pPackage->compatiblePackage.compatibleEntry.sczName = sczName;
922 sczName = NULL;
923
924 pPackage->compatiblePackage.compatibleEntry.sczVersion = sczVersion;
925 sczVersion = NULL;
926
927 break;
928 }
929
930 LExit:
931 return hr;
932 }
933
934 // internal functions
935
936
937 static HRESULT DetectPackageDependents(
938 __in BURN_PACKAGE* pPackage,
939 __in const BURN_REGISTRATION* pRegistration
940 )
941 {
942 HRESULT hr = S_OK;
943 HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
944 BOOL fCanIgnorePresence = pPackage->fCanAffectRegistration && 0 < pPackage->cDependencyProviders &&
945 (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState || BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState);
946 BOOL fBundleRegisteredAsDependent = FALSE;
947
948 // There's currently no point in getting the dependents if the scope doesn't match,
949 // because they will just get ignored.
950 if (pRegistration->fPerMachine != pPackage->fPerMachine)
951 {
952 ExitFunction();
953 }
954
955 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
956 {
957 BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
958 BOOL fExists = FALSE;
959
960 hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, NULL, &pProvider->rgDependents, &pProvider->cDependents);
961 ExitOnPathFailure(hr, fExists, "Failed dependents check on package provider: %ls", pProvider->sczKey);
962
963 if (0 < pProvider->cDependents || GetProviderExists(hkHive, pProvider->sczKey))
964 {
965 pProvider->fExists = TRUE;
966 }
967
968 for (DWORD iDependent = 0; iDependent < pProvider->cDependents; ++iDependent)
969 {
970 DEPENDENCY* pDependent = pProvider->rgDependents + iDependent;
971
972 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pDependent->sczKey, -1))
973 {
974 pProvider->fBundleRegisteredAsDependent = TRUE;
975 fBundleRegisteredAsDependent = TRUE;
976 break;
977 }
978 }
979 }
980
981 if (fCanIgnorePresence && !fBundleRegisteredAsDependent)
982 {
983 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->cacheRegistrationState)
984 {
985 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
986 }
987 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pPackage->installRegistrationState)
988 {
989 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
990 }
991 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
992 {
993 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
994 {
995 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
996
997 if (BURN_PACKAGE_REGISTRATION_STATE_PRESENT == pTargetProduct->registrationState)
998 {
999 pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_IGNORED;
1000 }
1001 }
1002 }
1003 }
1004
1005 LExit:
1006 return hr;
1007 }
1008
1009 /********************************************************************
1010 SplitIgnoreDependencies - Splits a semicolon-delimited
1011 string into a list of unique dependencies to ignore.
1012
1013 *********************************************************************/
1014 static HRESULT SplitIgnoreDependencies(
1015 __in_z LPCWSTR wzIgnoreDependencies,
1016 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
1017 __inout LPUINT pcDependencies,
1018 __out BOOL* pfIgnoreAll
1019 )
1020 {
1021 HRESULT hr = S_OK;
1022 LPWSTR wzContext = NULL;
1023 STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
1024 *pfIgnoreAll = FALSE;
1025
1026 // Create a dictionary to hold unique dependencies.
1027 hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
1028 ExitOnFailure(hr, "Failed to create the string dictionary.");
1029
1030 // Parse through the semicolon-delimited tokens and add to the array.
1031 for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
1032 {
1033 hr = DictKeyExists(sdIgnoreDependencies, wzToken);
1034 if (E_NOTFOUND != hr)
1035 {
1036 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
1037 }
1038 else
1039 {
1040 hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL);
1041 ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken);
1042
1043 hr = DictAddKey(sdIgnoreDependencies, wzToken);
1044 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
1045
1046 if (!*pfIgnoreAll && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"ALL", -1, wzToken, -1))
1047 {
1048 *pfIgnoreAll = TRUE;
1049 }
1050 }
1051 }
1052
1053 LExit:
1054 ReleaseDict(sdIgnoreDependencies);
1055
1056 return hr;
1057 }
1058
1059 /********************************************************************
1060 JoinIgnoreDependencies - Joins a list of dependencies
1061 to ignore into a semicolon-delimited string of unique values.
1062
1063 *********************************************************************/
1064 static HRESULT JoinIgnoreDependencies(
1065 __out_z LPWSTR* psczIgnoreDependencies,
1066 __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
1067 __in UINT cDependencies
1068 )
1069 {
1070 HRESULT hr = S_OK;
1071 STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
1072
1073 // Make sure we pass back an empty string if there are no dependencies.
1074 if (0 == cDependencies)
1075 {
1076 ExitFunction1(hr = S_OK);
1077 }
1078
1079 // Create a dictionary to hold unique dependencies.
1080 hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
1081 ExitOnFailure(hr, "Failed to create the string dictionary.");
1082
1083 for (UINT i = 0; i < cDependencies; ++i)
1084 {
1085 const DEPENDENCY* pDependency = &rgDependencies[i];
1086
1087 hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey);
1088 if (E_NOTFOUND != hr)
1089 {
1090 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
1091 }
1092 else
1093 {
1094 if (0 < i)
1095 {
1096 hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1);
1097 ExitOnFailure(hr, "Failed to append the string delimiter.");
1098 }
1099
1100 hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0);
1101 ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey);
1102
1103 hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey);
1104 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey);
1105 }
1106 }
1107
1108 LExit:
1109 ReleaseDict(sdIgnoreDependencies);
1110
1111 return hr;
1112 }
1113
1114 /********************************************************************
1115 GetIgnoredDependents - Combines the current bundle's
1116 provider key, packages' provider keys that are being uninstalled,
1117 and any ignored dependencies authored for packages into a string
1118 list to pass to deputil.
1119
1120 *********************************************************************/
1121 static HRESULT GetIgnoredDependents(
1122 __in const BURN_PACKAGE* pPackage,
1123 __in const BURN_PLAN* pPlan,
1124 __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
1125 )
1126 {
1127 HRESULT hr = S_OK;
1128 LPWSTR sczIgnoreDependencies = NULL;
1129
1130 // Create the dictionary and add the bundle provider key initially.
1131 hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
1132 ExitOnFailure(hr, "Failed to create the string dictionary.");
1133
1134 hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey);
1135 ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey);
1136
1137 // Add previously planned package providers to the dictionary.
1138 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
1139 {
1140 const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i];
1141
1142 hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey);
1143 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey);
1144 }
1145
1146 // Get the IGNOREDEPENDENCIES property if defined.
1147 hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies);
1148 if (E_NOTFOUND != hr)
1149 {
1150 ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES);
1151
1152 // TODO: this is the raw value of the property, all property values are currently formatted in a different part of planning.
1153 hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies);
1154 ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies.");
1155 }
1156 else
1157 {
1158 hr = S_OK;
1159 }
1160
1161 LExit:
1162 ReleaseStr(sczIgnoreDependencies);
1163
1164 return hr;
1165 }
1166
1167 /********************************************************************
1168 GetProviderExists - Gets whether the provider key is registered.
1169
1170 *********************************************************************/
1171 static BOOL GetProviderExists(
1172 __in HKEY hkRoot,
1173 __in_z LPCWSTR wzProviderKey
1174 )
1175 {
1176 HRESULT hr = DepGetProviderInformation(hkRoot, wzProviderKey, NULL, NULL, NULL);
1177 return SUCCEEDED(hr);
1178 }
1179
1180 /********************************************************************
1181 CalculateDependencyActionStates - Calculates the dependency execute and
1182 rollback actions for a package.
1183
1184 *********************************************************************/
1185 static void CalculateDependencyActionStates(
1186 __in const BURN_PACKAGE* pPackage,
1187 __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
1188 __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
1189 )
1190 {
1191 switch (pPackage->execute)
1192 {
1193 case BOOTSTRAPPER_ACTION_STATE_NONE:
1194 switch (pPackage->requested)
1195 {
1196 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
1197 // Unregister if the package is not requested but already not installed.
1198 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
1199 break;
1200 case BOOTSTRAPPER_REQUEST_STATE_NONE:
1201 // Register if a newer, compatible package is already installed.
1202 switch (pPackage->currentState)
1203 {
1204 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
1205 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
1206 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
1207 break;
1208 }
1209 break;
1210 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
1211 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
1212 // Register if the package is requested but already installed.
1213 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
1214 break;
1215 }
1216 break;
1217 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1218 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
1219 break;
1220 case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
1221 case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
1222 case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough;
1223 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
1224 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
1225 break;
1226 }
1227
1228 switch (*pDependencyExecuteAction)
1229 {
1230 case BURN_DEPENDENCY_ACTION_REGISTER:
1231 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
1232 break;
1233 case BURN_DEPENDENCY_ACTION_UNREGISTER:
1234 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER;
1235 break;
1236 }
1237 }
1238
1239 /********************************************************************
1240 AddPackageDependencyActions - Adds the dependency execute and rollback
1241 actions to the plan.
1242
1243 *********************************************************************/
1244 static HRESULT AddPackageDependencyActions(
1245 __in_opt DWORD *pdwInsertSequence,
1246 __in const BURN_PACKAGE* pPackage,
1247 __in BURN_PLAN* pPlan,
1248 __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
1249 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
1250 )
1251 {
1252 HRESULT hr = S_OK;
1253 BURN_EXECUTE_ACTION* pAction = NULL;
1254
1255 // Add the rollback plan.
1256 if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction)
1257 {
1258 hr = PlanAppendRollbackAction(pPlan, &pAction);
1259 ExitOnFailure(hr, "Failed to append rollback action.");
1260
1261 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
1262 pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
1263
1264 hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
1265 ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
1266
1267 // Put a checkpoint before the execute action so that rollback happens
1268 // if execute fails.
1269 hr = PlanExecuteCheckpoint(pPlan);
1270 ExitOnFailure(hr, "Failed to plan dependency checkpoint action.");
1271 }
1272
1273 // Add the execute plan. This comes after rollback so if something goes wrong
1274 // rollback will try to clean up after us correctly.
1275 if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction)
1276 {
1277 if (NULL != pdwInsertSequence)
1278 {
1279 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
1280 ExitOnFailure(hr, "Failed to insert execute action.");
1281
1282 // Always move the sequence after this dependency action so the dependency registration
1283 // stays in front of the inserted actions.
1284 ++(*pdwInsertSequence);
1285 }
1286 else
1287 {
1288 hr = PlanAppendExecuteAction(pPlan, &pAction);
1289 ExitOnFailure(hr, "Failed to append execute action.");
1290 }
1291
1292 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
1293 pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
1294
1295 hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
1296 ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
1297 }
1298
1299 LExit:
1300 return hr;
1301 }
1302
1303 static LPCWSTR GetPackageProviderId(
1304 __in const BURN_PACKAGE* pPackage
1305 )
1306 {
1307 LPCWSTR wzId = NULL;
1308
1309 switch (pPackage->type)
1310 {
1311 case BURN_PACKAGE_TYPE_MSI:
1312 wzId = pPackage->Msi.sczProductCode;
1313 break;
1314 case BURN_PACKAGE_TYPE_MSP:
1315 wzId = pPackage->Msp.sczPatchCode;
1316 break;
1317 }
1318
1319 return wzId;
1320 }
1321
1322 static HRESULT RegisterPackageProvider(
1323 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
1324 __in LPCWSTR wzPackageId,
1325 __in LPCWSTR wzPackageProviderId,
1326 __in HKEY hkRoot,
1327 __in BOOL fVital
1328 )
1329 {
1330 HRESULT hr = S_OK;
1331
1332 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, wzPackageId);
1333
1334 hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzPackageProviderId, 0);
1335 ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey);
1336
1337 LExit:
1338 if (!fVital)
1339 {
1340 hr = S_OK;
1341 }
1342
1343 return hr;
1344 }
1345
1346 /********************************************************************
1347 UnregisterPackageProvider - Removes the dependency provider.
1348
1349 Note: Does not check for existing dependents before removing the key.
1350 *********************************************************************/
1351 static void UnregisterPackageProvider(
1352 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
1353 __in LPCWSTR wzPackageId,
1354 __in HKEY hkRoot
1355 )
1356 {
1357 HRESULT hr = S_OK;
1358
1359 hr = DepUnregisterDependency(hkRoot, pProvider->sczKey);
1360 if (SUCCEEDED(hr) || E_FILENOTFOUND == hr)
1361 {
1362 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, wzPackageId);
1363 }
1364 else
1365 {
1366 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, wzPackageId, hr);
1367 }
1368 }
1369
1370 /********************************************************************
1371 RegisterPackageProviderDependent - Registers the provider key
1372 as a dependent of a package provider.
1373
1374 *********************************************************************/
1375 static HRESULT RegisterPackageProviderDependent(
1376 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
1377 __in BOOL fVital,
1378 __in HKEY hkRoot,
1379 __in LPCWSTR wzPackageId,
1380 __in_z LPCWSTR wzDependentProviderKey
1381 )
1382 {
1383 HRESULT hr = S_OK;
1384 BOOL fExists = FALSE;
1385
1386 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, wzPackageId);
1387
1388 hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0);
1389 ExitOnPathFailure(hr, fExists, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey);
1390
1391 if (!fExists)
1392 {
1393 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, wzPackageId);
1394 }
1395
1396 LExit:
1397 if (!fVital)
1398 {
1399 hr = S_OK;
1400 }
1401
1402 return hr;
1403 }
1404
1405 /********************************************************************
1406 UnregisterPackageDependency - Unregisters the provider key
1407 as a dependent of a package.
1408
1409 *********************************************************************/
1410 static void UnregisterPackageDependency(
1411 __in BOOL fPerMachine,
1412 __in const BURN_PACKAGE* pPackage,
1413 __in_z LPCWSTR wzDependentProviderKey
1414 )
1415 {
1416 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1417
1418 // Should be no registration to remove since we don't write keys across contexts.
1419 if (fPerMachine != pPackage->fPerMachine)
1420 {
1421 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
1422 return;
1423 }
1424
1425 // Loop through each package provider and remove the bundle dependency key.
1426 if (pPackage->rgDependencyProviders)
1427 {
1428 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1429 {
1430 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1431
1432 UnregisterPackageProviderDependent(pProvider, hkRoot, pPackage->sczId, wzDependentProviderKey);
1433 }
1434 }
1435 }
1436
1437 static void UnregisterPackageProviderDependent(
1438 __in const BURN_DEPENDENCY_PROVIDER* pProvider,
1439 __in HKEY hkRoot,
1440 __in LPCWSTR wzPackageId,
1441 __in_z LPCWSTR wzDependentProviderKey
1442 )
1443 {
1444 HRESULT hr = S_OK;
1445
1446 hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey);
1447 if (SUCCEEDED(hr) || E_FILENOTFOUND == hr)
1448 {
1449 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, wzPackageId);
1450 }
1451 else
1452 {
1453 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, wzPackageId, hr);
1454 }
1455 }
1456
1457 static void UnregisterOrphanPackageProviders(
1458 __in const BURN_PACKAGE* pPackage
1459 )
1460 {
1461 HRESULT hr = S_OK;
1462 DEPENDENCY* rgDependents = NULL;
1463 UINT cDependents = 0;
1464 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1465
1466 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1467 {
1468 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1469
1470 // Skip providers not owned by the bundle.
1471 if (pProvider->fImported)
1472 {
1473 continue;
1474 }
1475
1476 hr = DepCheckDependents(hkRoot, pProvider->sczKey, 0, NULL, &rgDependents, &cDependents);
1477 if (SUCCEEDED(hr) && !cDependents)
1478 {
1479 UnregisterPackageProvider(pProvider, pPackage->sczId, hkRoot);
1480 }
1481
1482 ReleaseDependencyArray(rgDependents, cDependents);
1483 rgDependents = NULL;
1484 cDependents = 0;
1485 }
1486 }