Refactor related bundle enumeration into butil.
Related to #3693
Sean Hall committed
Feb 28, 2022 at 18:42 UTC
befcd209d62a25020f46a688002b259c59e4dc3b
10 files changed
+731
-405
src/burn/engine/precomp.h
+1
@@ -55,6 +55,7 @@
55
#include <atomutil.h>
56
#include <apuputil.h>
57
#include <dpiutil.h>
58
+#include <butil.h>
59
60
#include "BootstrapperEngine.h"
61
#include "BootstrapperApplication.h"
src/burn/engine/relatedbundle.cpp
+53
-337
@@ -2,6 +2,12 @@
2
3
#include "precomp.h"
4
5
+typedef struct _BUNDLE_QUERY_CONTEXT
6
+{
7
+ BURN_REGISTRATION* pRegistration;
8
+ BURN_RELATED_BUNDLES* pRelatedBundles;
9
+} BUNDLE_QUERY_CONTEXT;
10
+
11
// internal function declarations
12
13
static __callback int __cdecl CompareRelatedBundles(
@@ -9,25 +15,15 @@ static __callback int __cdecl CompareRelatedBundles(
15
__in const void* pvLeft,
16
__in const void* pvRight
17
);
12
-static HRESULT InitializeForScopeAndBitness(
13
- __in BOOL fPerMachine,
14
- __in REG_KEY_BITNESS regBitness,
15
- __in BURN_REGISTRATION* pRegistration,
16
- __in BURN_RELATED_BUNDLES* pRelatedBundles
18
+static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
19
+ __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
20
+ __in_opt LPVOID pvContext
21
);
22
static HRESULT LoadIfRelatedBundle(
19
- __in BOOL fPerMachine,
20
- __in REG_KEY_BITNESS regBitness,
21
- __in HKEY hkUninstallKey,
22
- __in_z LPCWSTR sczRelatedBundleId,
23
+ __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
24
__in BURN_REGISTRATION* pRegistration,
25
__in BURN_RELATED_BUNDLES* pRelatedBundles
26
);
26
-static HRESULT DetermineRelationType(
27
- __in HKEY hkBundleId,
28
- __in BURN_REGISTRATION* pRegistration,
29
- __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
30
- );
27
static HRESULT LoadRelatedBundleFromKey(
28
__in_z LPCWSTR wzRelatedBundleId,
29
__in HKEY hkBundleId,
@@ -46,12 +42,25 @@ extern "C" HRESULT RelatedBundlesInitializeForScope(
42
)
43
{
44
HRESULT hr = S_OK;
49
-
50
- hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_32BIT, pRegistration, pRelatedBundles);
51
- ExitOnFailure(hr, "Failed to open 32-bit uninstall registry key.");
52
-
53
- hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_64BIT, pRegistration, pRelatedBundles);
54
- ExitOnFailure(hr, "Failed to open 64-bit uninstall registry key.");
45
+ BUNDLE_INSTALL_CONTEXT installContext = fPerMachine ? BUNDLE_INSTALL_CONTEXT_MACHINE : BUNDLE_INSTALL_CONTEXT_USER;
46
+ BUNDLE_QUERY_CONTEXT queryContext = { };
47
+
48
+ queryContext.pRegistration = pRegistration;
49
+ queryContext.pRelatedBundles = pRelatedBundles;
50
+
51
+ hr = BundleQueryRelatedBundles(
52
+ installContext,
53
+ const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes),
54
+ pRegistration->cDetectCodes,
55
+ const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes),
56
+ pRegistration->cUpgradeCodes,
57
+ const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes),
58
+ pRegistration->cAddonCodes,
59
+ const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes),
60
+ pRegistration->cPatchCodes,
61
+ QueryRelatedBundlesCallback,
62
+ &queryContext);
63
+ ExitOnFailure(hr, "Failed to initialize related bundles for scope.");
64
65
LExit:
66
return hr;
@@ -166,346 +175,53 @@ static __callback int __cdecl CompareRelatedBundles(
175
return ret;
176
}
177
169
-static HRESULT InitializeForScopeAndBitness(
170
- __in BOOL fPerMachine,
171
- __in REG_KEY_BITNESS regBitness,
172
- __in BURN_REGISTRATION * pRegistration,
173
- __in BURN_RELATED_BUNDLES * pRelatedBundles
174
-)
178
+static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
179
+ __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
180
+ __in_opt LPVOID pvContext
181
+ )
182
{
183
HRESULT hr = S_OK;
177
- HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
178
- HKEY hkUninstallKey = NULL;
179
- LPWSTR sczRelatedBundleId = NULL;
184
+ BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
185
+ BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext);
186
181
- hr = RegOpenEx(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, regBitness, &hkUninstallKey);
182
- if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
183
- {
184
- ExitFunction1(hr = S_OK);
185
- }
186
- ExitOnFailure(hr, "Failed to open uninstall registry key.");
187
-
188
- for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
189
- {
190
- hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
191
- if (E_NOMOREITEMS == hr)
192
- {
193
- hr = S_OK;
194
- break;
195
- }
196
- ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
197
-
198
- // If we did not find our bundle id, try to load the subkey as a related bundle.
199
- if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1))
200
- {
201
- // Ignore failures here since we'll often find products that aren't actually
202
- // related bundles (or even bundles at all).
203
- HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, regBitness, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles);
204
- UNREFERENCED_PARAMETER(hrRelatedBundle);
205
- }
206
- }
187
+ hr = LoadIfRelatedBundle(pBundle, pContext->pRegistration, pContext->pRelatedBundles);
188
+ ExitOnFailure(hr, "Failed to load related bundle: %ls", pBundle->wzBundleId);
189
190
LExit:
209
- ReleaseStr(sczRelatedBundleId);
210
- ReleaseRegKey(hkUninstallKey);
211
-
212
- return hr;
191
+ return result;
192
}
193
194
static HRESULT LoadIfRelatedBundle(
216
- __in BOOL fPerMachine,
217
- __in REG_KEY_BITNESS regBitness,
218
- __in HKEY hkUninstallKey,
219
- __in_z LPCWSTR sczRelatedBundleId,
195
+ __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
196
__in BURN_REGISTRATION* pRegistration,
197
__in BURN_RELATED_BUNDLES* pRelatedBundles
198
)
199
{
200
HRESULT hr = S_OK;
225
- HKEY hkBundleId = NULL;
226
- BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE;
227
-
228
- hr = RegOpenEx(hkUninstallKey, sczRelatedBundleId, KEY_READ, regBitness, &hkBundleId);
229
- ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId);
230
-
231
- hr = DetermineRelationType(hkBundleId, pRegistration, &relationType);
232
- if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType)
233
- {
234
- // Must not be a related bundle.
235
- hr = E_NOTFOUND;
236
- }
237
- else // load the related bundle.
238
- {
239
- hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
240
- ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
241
-
242
- BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
243
-
244
- hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle);
245
- ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId);
246
-
247
- hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration);
248
- ExitOnFailure(hr, "Failed to detect dependencies for related bundle.");
249
-
250
- ++pRelatedBundles->cRelatedBundles;
251
- }
252
-
253
-LExit:
254
- ReleaseRegKey(hkBundleId);
255
-
256
- return hr;
257
-}
258
-
259
-static HRESULT DetermineRelationType(
260
- __in HKEY hkBundleId,
261
- __in BURN_REGISTRATION* pRegistration,
262
- __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
263
- )
264
-{
265
- HRESULT hr = S_OK;
266
- LPWSTR* rgsczUpgradeCodes = NULL;
267
- DWORD cUpgradeCodes = 0;
268
- STRINGDICT_HANDLE sdUpgradeCodes = NULL;
269
- LPWSTR* rgsczAddonCodes = NULL;
270
- DWORD cAddonCodes = 0;
271
- STRINGDICT_HANDLE sdAddonCodes = NULL;
272
- LPWSTR* rgsczDetectCodes = NULL;
273
- DWORD cDetectCodes = 0;
274
- STRINGDICT_HANDLE sdDetectCodes = NULL;
275
- LPWSTR* rgsczPatchCodes = NULL;
276
- DWORD cPatchCodes = 0;
277
- STRINGDICT_HANDLE sdPatchCodes = NULL;
278
-
279
- *pRelationType = BOOTSTRAPPER_RELATION_NONE;
280
-
281
- // All remaining operations should treat all related bundles as non-vital.
282
- hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
283
- if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
284
- {
285
- TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
286
-
287
- rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
288
- ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
289
-
290
- hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
291
- if (SUCCEEDED(hr))
292
- {
293
- cUpgradeCodes = 1;
294
- }
295
- }
296
-
297
- // Compare upgrade codes.
298
- if (SUCCEEDED(hr))
299
- {
300
- hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
301
- ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
302
-
303
- // Upgrade relationship: when their upgrade codes match our upgrade codes.
304
- hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
305
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
306
- {
307
- hr = S_OK;
308
- }
309
- else
310
- {
311
- ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
312
-
313
- *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE;
314
- ExitFunction();
315
- }
316
-
317
- // Detect relationship: when their upgrade codes match our detect codes.
318
- hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
319
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
320
- {
321
- hr = S_OK;
322
- }
323
- else
324
- {
325
- ExitOnFailure(hr, "Failed to do array search for detect code match.");
326
-
327
- *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
328
- ExitFunction();
329
- }
330
-
331
- // Dependent relationship: when their upgrade codes match our addon codes.
332
- hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
333
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
334
- {
335
- hr = S_OK;
336
- }
337
- else
338
- {
339
- ExitOnFailure(hr, "Failed to do array search for addon code match.");
340
-
341
- *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
342
- ExitFunction();
343
- }
344
-
345
- // Dependent relationship: when their upgrade codes match our patch codes.
346
- hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
347
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
348
- {
349
- hr = S_OK;
350
- }
351
- else
352
- {
353
- ExitOnFailure(hr, "Failed to do array search for addon code match.");
354
-
355
- *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
356
- ExitFunction();
357
- }
358
-
359
- ReleaseNullDict(sdUpgradeCodes);
360
- ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
361
- }
362
-
363
- // Compare addon codes.
364
- hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
365
- if (SUCCEEDED(hr))
366
- {
367
- hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
368
- ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
369
-
370
- // Addon relationship: when their addon codes match our detect codes.
371
- hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
372
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
373
- {
374
- hr = S_OK;
375
- }
376
- else
377
- {
378
- ExitOnFailure(hr, "Failed to do array search for addon code match.");
379
-
380
- *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
381
- ExitFunction();
382
- }
383
-
384
- // Addon relationship: when their addon codes match our upgrade codes.
385
- hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
386
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
387
- {
388
- hr = S_OK;
389
- }
390
- else
391
- {
392
- ExitOnFailure(hr, "Failed to do array search for addon code match.");
393
-
394
- *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
395
- ExitFunction();
396
- }
397
-
398
- ReleaseNullDict(sdAddonCodes);
399
- ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
400
- }
201
+ BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
202
+ BOOTSTRAPPER_RELATION_TYPE relationType = (BOOTSTRAPPER_RELATION_TYPE)pBundle->relationType;
203
+ BURN_RELATED_BUNDLE* pRelatedBundle = NULL;
204
402
- // Compare patch codes.
403
- hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
404
- if (SUCCEEDED(hr))
205
+ // If we found our bundle id, it's not a related bundle.
206
+ if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pRegistration->sczId, -1))
207
{
406
- hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
407
- ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
408
-
409
- // Patch relationship: when their patch codes match our detect codes.
410
- hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
411
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
412
- {
413
- hr = S_OK;
414
- }
415
- else
416
- {
417
- ExitOnFailure(hr, "Failed to do array search for patch code match.");
418
-
419
- *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
420
- ExitFunction();
421
- }
422
-
423
- // Patch relationship: when their patch codes match our upgrade codes.
424
- hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
425
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
426
- {
427
- hr = S_OK;
428
- }
429
- else
430
- {
431
- ExitOnFailure(hr, "Failed to do array search for patch code match.");
432
-
433
- *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
434
- ExitFunction();
435
- }
436
-
437
- ReleaseNullDict(sdPatchCodes);
438
- ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
208
+ ExitFunction1(hr = S_FALSE);
209
}
210
441
- // Compare detect codes.
442
- hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
443
- if (SUCCEEDED(hr))
444
- {
445
- hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
446
- ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
211
+ hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
212
+ ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
213
448
- // Detect relationship: when their detect codes match our detect codes.
449
- hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
450
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
451
- {
452
- hr = S_OK;
453
- }
454
- else
455
- {
456
- ExitOnFailure(hr, "Failed to do array search for detect code match.");
214
+ pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
215
458
- *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
459
- ExitFunction();
460
- }
216
+ hr = LoadRelatedBundleFromKey(pBundle->wzBundleId, pBundle->hkBundle, fPerMachine, relationType, pRelatedBundle);
217
+ ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", pBundle->wzBundleId);
218
462
- // Dependent relationship: when their detect codes match our addon codes.
463
- hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
464
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
465
- {
466
- hr = S_OK;
467
- }
468
- else
469
- {
470
- ExitOnFailure(hr, "Failed to do array search for addon code match.");
471
-
472
- *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
473
- ExitFunction();
474
- }
475
-
476
- // Dependent relationship: when their detect codes match our patch codes.
477
- hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
478
- if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
479
- {
480
- hr = S_OK;
481
- }
482
- else
483
- {
484
- ExitOnFailure(hr, "Failed to do array search for addon code match.");
219
+ hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration);
220
+ ExitOnFailure(hr, "Failed to detect dependencies for related bundle.");
221
486
- *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
487
- ExitFunction();
488
- }
489
-
490
- ReleaseNullDict(sdDetectCodes);
491
- ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
492
- }
222
+ ++pRelatedBundles->cRelatedBundles;
223
224
LExit:
495
- if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType)
496
- {
497
- hr = E_NOTFOUND;
498
- }
499
-
500
- ReleaseDict(sdUpgradeCodes);
501
- ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
502
- ReleaseDict(sdAddonCodes);
503
- ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
504
- ReleaseDict(sdDetectCodes);
505
- ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
506
- ReleaseDict(sdPatchCodes);
507
- ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
508
-
225
return hr;
226
}
227
src/burn/test/BurnUnitTest/BurnTestException.h
+3
-8
@@ -13,19 +13,14 @@ namespace Test
13
namespace Bootstrapper
14
{
15
using namespace System;
16
+ using namespace WixBuildTools::TestSupport;
17
17
- public ref struct BurnTestException : public System::Exception
18
+ public ref struct BurnTestException : public SucceededException
19
{
20
public:
20
- BurnTestException(HRESULT error)
21
- {
22
- this->HResult = error;
23
- }
24
-
21
BurnTestException(HRESULT error, String^ message)
26
- : Exception(message)
22
+ : SucceededException(error, message)
23
{
28
- this->HResult = error;
24
}
25
26
property Int32 ErrorCode
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+1
@@ -56,6 +56,7 @@
56
<DisableSpecificWarnings>4564;4691</DisableSpecificWarnings>
57
</ClCompile>
58
<ClCompile Include="RegistrationTest.cpp" />
59
+ <ClCompile Include="RelatedBundleTest.cpp" />
60
<ClCompile Include="SearchTest.cpp" />
61
<ClCompile Include="TestRegistryFixture.cpp" />
62
<ClCompile Include="VariableHelpers.cpp" />
src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
+3
@@ -39,6 +39,9 @@
39
<ClCompile Include="RegistrationTest.cpp">
40
<Filter>Source Files</Filter>
41
</ClCompile>
42
+ <ClCompile Include="RelatedBundleTest.cpp">
43
+ <Filter>Source Files</Filter>
44
+ </ClCompile>
45
<ClCompile Include="SearchTest.cpp">
46
<Filter>Source Files</Filter>
47
</ClCompile>
src/burn/test/BurnUnitTest/RelatedBundleTest.cpp
new
+199
@@ -0,0 +1,199 @@
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
+
6
+namespace Microsoft
7
+{
8
+namespace Tools
9
+{
10
+namespace WindowsInstallerXml
11
+{
12
+namespace Test
13
+{
14
+namespace Bootstrapper
15
+{
16
+ using namespace System;
17
+ using namespace System::IO;
18
+ using namespace Xunit;
19
+ using namespace WixBuildTools::TestSupport;
20
+
21
+ public ref class RelatedBundleTest : BurnUnitTest, IClassFixture<TestRegistryFixture^>
22
+ {
23
+ private:
24
+ TestRegistryFixture^ testRegistry;
25
+ public:
26
+ RelatedBundleTest(BurnTestFixture^ fixture, TestRegistryFixture^ registryFixture) : BurnUnitTest(fixture)
27
+ {
28
+ this->testRegistry = registryFixture;
29
+ }
30
+
31
+ [Fact]
32
+ void RelatedBundleDetectPerMachineTest()
33
+ {
34
+ HRESULT hr = S_OK;
35
+ IXMLDOMElement* pixeBundle = NULL;
36
+ BURN_REGISTRATION registration = { };
37
+ BURN_RELATED_BUNDLES relatedBundles = { };
38
+ BURN_CACHE cache = { };
39
+ BURN_ENGINE_COMMAND internalCommand = { };
40
+
41
+ try
42
+ {
43
+ this->testRegistry->SetUp();
44
+ this->RegisterFakeBundles();
45
+
46
+ LPCWSTR wzDocument =
47
+ L"<Bundle>"
48
+ L" <UX>"
49
+ L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' />"
50
+ L" </UX>"
51
+ L" <RelatedBundle Id='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Action='Upgrade' />"
52
+ L" <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='yes'>"
53
+ L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
54
+ L" </Registration>"
55
+ L"</Bundle>";
56
+
57
+ // load XML document
58
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
59
+
60
+ hr = CacheInitialize(&cache, &internalCommand);
61
+ TestThrowOnFailure(hr, L"Failed initialize cache.");
62
+
63
+ hr = RegistrationParseFromXml(®istration, &cache, pixeBundle);
64
+ TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
65
+
66
+ RelatedBundlesInitializeForScope(registration.fPerMachine, ®istration, &relatedBundles);
67
+
68
+ Assert::Equal(1lu, relatedBundles.cRelatedBundles);
69
+
70
+ BURN_RELATED_BUNDLE* pRelatedBundle = relatedBundles.rgRelatedBundles + 0;
71
+ NativeAssert::StringEqual(L"{AD75BE46-B5D7-4208-BC8B-918553C72D83}", pRelatedBundle->package.sczId);
72
+ //{E2355133-384C-4332-9B62-1FA950D707B7} should be missing because it causes an error while processing it. It's important that this doesn't cause initialization to fail.
73
+ }
74
+ finally
75
+ {
76
+ ReleaseObject(pixeBundle);
77
+ RegistrationUninitialize(®istration);
78
+
79
+ this->testRegistry->TearDown();
80
+ }
81
+ }
82
+
83
+ [Fact]
84
+ void RelatedBundleDetectPerUserTest()
85
+ {
86
+ HRESULT hr = S_OK;
87
+ IXMLDOMElement* pixeBundle = NULL;
88
+ BURN_REGISTRATION registration = { };
89
+ BURN_RELATED_BUNDLES relatedBundles = { };
90
+ BURN_CACHE cache = { };
91
+ BURN_ENGINE_COMMAND internalCommand = { };
92
+
93
+ try
94
+ {
95
+ this->testRegistry->SetUp();
96
+ this->RegisterFakeBundles();
97
+
98
+ LPCWSTR wzDocument =
99
+ L"<Bundle>"
100
+ L" <UX>"
101
+ L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' />"
102
+ L" </UX>"
103
+ L" <RelatedBundle Id='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Action='Upgrade' />"
104
+ L" <Registration Id='{3DB49D3D-1FB8-4147-A465-BBE8BFD0DAD0}' Tag='foo' ProviderKey='foo' Version='4.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
105
+ L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='4.0.0.0' />"
106
+ L" </Registration>"
107
+ L"</Bundle>";
108
+
109
+ // load XML document
110
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
111
+
112
+ hr = CacheInitialize(&cache, &internalCommand);
113
+ TestThrowOnFailure(hr, L"Failed initialize cache.");
114
+
115
+ hr = RegistrationParseFromXml(®istration, &cache, pixeBundle);
116
+ TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
117
+
118
+ RelatedBundlesInitializeForScope(registration.fPerMachine, ®istration, &relatedBundles);
119
+
120
+ Assert::Equal(1lu, relatedBundles.cRelatedBundles);
121
+
122
+ BURN_RELATED_BUNDLE* pRelatedBundle = relatedBundles.rgRelatedBundles + 0;
123
+ NativeAssert::StringEqual(L"{6DB5D48C-CD7D-40D2-BCBC-AF630E136761}", pRelatedBundle->package.sczId);
124
+ //{42D16EBE-8B6B-4A9A-9AE9-5300F30011AA} should be missing because it causes an error while processing it. It's important that this doesn't cause initialization to fail.
125
+ }
126
+ finally
127
+ {
128
+ ReleaseObject(pixeBundle);
129
+ RegistrationUninitialize(®istration);
130
+
131
+ this->testRegistry->TearDown();
132
+ }
133
+ }
134
+
135
+ void RegisterFakeBundles()
136
+ {
137
+ this->RegisterFakeBundle(L"{D54F896D-1952-43E6-9C67-B5652240618C}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"1.0.0.0", TRUE);
138
+ this->RegisterFakeBundle(L"{E2355133-384C-4332-9B62-1FA950D707B7}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", L"", L"1.1.0.0", TRUE);
139
+ this->RegisterFakeBundle(L"{AD75BE46-B5D7-4208-BC8B-918553C72D83}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"2.0.0.0", TRUE);
140
+ this->RegisterFakeBundle(L"{6DB5D48C-CD7D-40D2-BCBC-AF630E136761}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"3.0.0.0", FALSE);
141
+ this->RegisterFakeBundle(L"{42D16EBE-8B6B-4A9A-9AE9-5300F30011AA}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", L"", L"3.1.0.0", FALSE);
142
+ this->RegisterFakeBundle(L"{3DB49D3D-1FB8-4147-A465-BBE8BFD0DAD0}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"4.0.0.0", FALSE);
143
+ }
144
+
145
+ void RegisterFakeBundle(LPCWSTR wzBundleId, LPCWSTR wzUpgradeCodes, LPCWSTR wzCachePath, LPCWSTR wzVersion, BOOL fPerMachine)
146
+ {
147
+ HRESULT hr = S_OK;
148
+ LPWSTR* rgsczUpgradeCodes = NULL;
149
+ DWORD cUpgradeCodes = 0;
150
+ LPWSTR sczRegistrationKey = NULL;
151
+ LPWSTR sczCachePath = NULL;
152
+ HKEY hkRegistration = NULL;
153
+ HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
154
+
155
+ try
156
+ {
157
+ hr = StrSplitAllocArray(&rgsczUpgradeCodes, reinterpret_cast<UINT*>(&cUpgradeCodes), wzUpgradeCodes, L";");
158
+ NativeAssert::Succeeded(hr, "Failed to split upgrade codes.");
159
+
160
+ hr = StrAllocFormatted(&sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId);
161
+ NativeAssert::Succeeded(hr, "Failed to build uninstall registry key path.");
162
+
163
+ if (!wzCachePath)
164
+ {
165
+ hr = StrAllocFormatted(&sczCachePath, L"%ls.exe", wzBundleId);
166
+ NativeAssert::Succeeded(hr, "Failed to build cache path.");
167
+
168
+ wzCachePath = sczCachePath;
169
+ }
170
+
171
+ hr = RegCreate(hkRoot, sczRegistrationKey, KEY_WRITE, &hkRegistration);
172
+ NativeAssert::Succeeded(hr, "Failed to create registration key.");
173
+
174
+ hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, rgsczUpgradeCodes, cUpgradeCodes);
175
+ NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE);
176
+
177
+ if (wzCachePath && *wzCachePath)
178
+ {
179
+ hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, wzCachePath);
180
+ NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH);
181
+ }
182
+
183
+ hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, wzVersion);
184
+ NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION);
185
+ }
186
+ finally
187
+ {
188
+ ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
189
+ ReleaseStr(sczRegistrationKey);
190
+ ReleaseStr(sczCachePath);
191
+ ReleaseRegKey(hkRegistration);
192
+ }
193
+ }
194
+ };
195
+}
196
+}
197
+}
198
+}
199
+}
src/burn/test/BurnUnitTest/precomp.h
+1
@@ -53,6 +53,7 @@
53
#include "update.h"
54
#include "pseudobundle.h"
55
#include "registration.h"
56
+#include "relatedbundle.h"
57
#include "plan.h"
58
#include "pipe.h"
59
#include "logging.h"
src/internal/WixBuildTools.TestSupport/SucceededException.cs
+1
@@ -13,6 +13,7 @@ namespace WixBuildTools.TestSupport
13
"Message: {1}",
14
hr, userMessage))
15
{
16
+ this.HResult = hr;
17
}
18
}
19
}
src/libs/dutil/WixToolset.DUtil/butil.cpp
+420
-60
@@ -19,6 +19,9 @@
19
// constants
20
// From engine/registration.h
21
const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
22
+const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode";
23
+const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode";
24
+const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode";
25
const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
26
const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
27
const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
@@ -30,7 +33,41 @@ enum INTERNAL_BUNDLE_STATUS
33
INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY,
34
};
35
36
+typedef struct _BUNDLE_QUERY_CONTEXT
37
+{
38
+ BUNDLE_INSTALL_CONTEXT installContext;
39
+ REG_KEY_BITNESS regBitness;
40
+ PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback;
41
+ LPVOID pvContext;
42
+
43
+ LPCWSTR* rgwzDetectCodes;
44
+ DWORD cDetectCodes;
45
+
46
+ LPCWSTR* rgwzUpgradeCodes;
47
+ DWORD cUpgradeCodes;
48
+
49
+ LPCWSTR* rgwzAddonCodes;
50
+ DWORD cAddonCodes;
51
+
52
+ LPCWSTR* rgwzPatchCodes;
53
+ DWORD cPatchCodes;
54
+} BUNDLE_QUERY_CONTEXT;
55
+
56
// Forward declarations.
57
+static HRESULT QueryRelatedBundlesForScopeAndBitness(
58
+ __in BUNDLE_QUERY_CONTEXT* pQueryContext
59
+ );
60
+static HRESULT QueryPotentialRelatedBundle(
61
+ __in BUNDLE_QUERY_CONTEXT* pQueryContext,
62
+ __in HKEY hkUninstallKey,
63
+ __in_z LPCWSTR wzRelatedBundleId,
64
+ __inout BUNDLE_QUERY_CALLBACK_RESULT* pResult
65
+ );
66
+static HRESULT DetermineRelationType(
67
+ __in BUNDLE_QUERY_CONTEXT* pQueryContext,
68
+ __in HKEY hkBundleId,
69
+ __out BUNDLE_RELATION_TYPE* pRelationType
70
+ );
71
/********************************************************************
72
LocateAndQueryBundleValue - Locates the requested key for the bundle,
73
then queries the registry type for requested value.
@@ -160,11 +197,13 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
197
HKEY hkBundle = NULL;
198
LPWSTR sczUninstallSubKey = NULL;
199
LPWSTR sczUninstallSubKeyPath = NULL;
163
- LPWSTR sczValue = NULL;
164
- DWORD dwType = 0;
165
- LPWSTR* rgsczBundleUpgradeCodes = NULL;
166
- DWORD cBundleUpgradeCodes = 0;
200
HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
201
+ BUNDLE_QUERY_CONTEXT queryContext = { };
202
+ BUNDLE_RELATION_TYPE relationType = BUNDLE_RELATION_NONE;
203
+
204
+ queryContext.installContext = context;
205
+ queryContext.rgwzUpgradeCodes = &wzUpgradeCode;
206
+ queryContext.cUpgradeCodes = 1;
207
208
if (!wzUpgradeCode || !pdwStartIndex)
209
{
@@ -185,60 +224,12 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
224
hr = RegOpenEx(hkRoot, sczUninstallSubKeyPath, KEY_READ, kbKeyBitness, &hkBundle);
225
ButilExitOnFailure(hr, "Failed to open uninstall key path.");
226
188
- // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ
189
- hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType);
190
- if (FAILED(hr))
227
+ hr = DetermineRelationType(&queryContext, hkBundle, &relationType);
228
+ if (SUCCEEDED(hr) && BUNDLE_RELATION_UPGRADE == relationType)
229
{
192
- ReleaseRegKey(hkBundle);
193
- ReleaseNullStr(sczUninstallSubKey);
194
- ReleaseNullStr(sczUninstallSubKeyPath);
195
- // Not a bundle
196
- continue;
197
- }
198
-
199
- switch (dwType)
200
- {
201
- case REG_SZ:
202
- hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue);
203
- ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property.");
204
-
205
- if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1))
206
- {
207
- *pdwStartIndex = dwIndex;
208
- fUpgradeCodeFound = TRUE;
209
- break;
210
- }
211
-
212
- ReleaseNullStr(sczValue);
213
-
214
- break;
215
- case REG_MULTI_SZ:
216
- hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes);
217
- ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property.");
230
+ fUpgradeCodeFound = TRUE;
231
+ *pdwStartIndex = dwIndex;
232
219
- for (DWORD i = 0; i < cBundleUpgradeCodes; i++)
220
- {
221
- LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i];
222
- if (wzBundleUpgradeCode && *wzBundleUpgradeCode)
223
- {
224
- if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1))
225
- {
226
- *pdwStartIndex = dwIndex;
227
- fUpgradeCodeFound = TRUE;
228
- break;
229
- }
230
- }
231
- }
232
- ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
233
-
234
- break;
235
-
236
- default:
237
- ButilExitWithRootFailure(hr, E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType);
238
- }
239
-
240
- if (fUpgradeCodeFound)
241
- {
233
if (psczBundleId)
234
{
235
*psczBundleId = sczUninstallSubKey;
@@ -250,17 +241,13 @@ DAPI_(HRESULT) BundleEnumRelatedBundle(
241
242
// Cleanup before next iteration
243
ReleaseRegKey(hkBundle);
253
- ReleaseNullStr(sczUninstallSubKey);
254
- ReleaseNullStr(sczUninstallSubKeyPath);
244
}
245
246
LExit:
258
- ReleaseStr(sczValue);
247
ReleaseStr(sczUninstallSubKey);
248
ReleaseStr(sczUninstallSubKeyPath);
249
ReleaseRegKey(hkBundle);
250
ReleaseRegKey(hkUninstall);
263
- ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
251
252
return FAILED(hr) ? hr : fUpgradeCodeFound ? S_OK : S_FALSE;
253
}
@@ -370,6 +357,379 @@ LExit:
357
return hr;
358
}
359
360
+DAPI_(HRESULT) BundleQueryRelatedBundles(
361
+ __in BUNDLE_INSTALL_CONTEXT installContext,
362
+ __in_z_opt LPCWSTR* rgwzDetectCodes,
363
+ __in DWORD cDetectCodes,
364
+ __in_z_opt LPCWSTR* rgwzUpgradeCodes,
365
+ __in DWORD cUpgradeCodes,
366
+ __in_z_opt LPCWSTR* rgwzAddonCodes,
367
+ __in DWORD cAddonCodes,
368
+ __in_z_opt LPCWSTR* rgwzPatchCodes,
369
+ __in DWORD cPatchCodes,
370
+ __in PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback,
371
+ __in_opt LPVOID pvContext
372
+ )
373
+{
374
+ HRESULT hr = S_OK;
375
+ BUNDLE_QUERY_CONTEXT queryContext = { };
376
+
377
+ queryContext.installContext = installContext;
378
+ queryContext.rgwzDetectCodes = rgwzDetectCodes;
379
+ queryContext.cDetectCodes = cDetectCodes;
380
+ queryContext.rgwzUpgradeCodes = rgwzUpgradeCodes;
381
+ queryContext.cUpgradeCodes = cUpgradeCodes;
382
+ queryContext.rgwzAddonCodes = rgwzAddonCodes;
383
+ queryContext.cAddonCodes = cAddonCodes;
384
+ queryContext.rgwzPatchCodes = rgwzPatchCodes;
385
+ queryContext.cPatchCodes = cPatchCodes;
386
+ queryContext.pfnCallback = pfnCallback;
387
+ queryContext.pvContext = pvContext;
388
+
389
+ queryContext.regBitness = REG_KEY_32BIT;
390
+
391
+ hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
392
+ ExitOnFailure(hr, "Failed to query 32-bit related bundles.");
393
+
394
+ queryContext.regBitness = REG_KEY_64BIT;
395
+
396
+ hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
397
+ ExitOnFailure(hr, "Failed to query 64-bit related bundles.");
398
+
399
+LExit:
400
+ return hr;
401
+}
402
+
403
+static HRESULT QueryRelatedBundlesForScopeAndBitness(
404
+ __in BUNDLE_QUERY_CONTEXT* pQueryContext
405
+ )
406
+{
407
+ HRESULT hr = S_OK;
408
+ HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == pQueryContext->installContext ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
409
+ HKEY hkUninstallKey = NULL;
410
+ LPWSTR sczRelatedBundleId = NULL;
411
+ BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
412
+
413
+ hr = RegOpenEx(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, pQueryContext->regBitness, &hkUninstallKey);
414
+ if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
415
+ {
416
+ ExitFunction1(hr = S_OK);
417
+ }
418
+ ExitOnFailure(hr, "Failed to open uninstall registry key.");
419
+
420
+ for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
421
+ {
422
+ hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
423
+ if (E_NOMOREITEMS == hr)
424
+ {
425
+ hr = S_OK;
426
+ break;
427
+ }
428
+ ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
429
+
430
+ // Ignore failures here since we'll often find products that aren't actually
431
+ // related bundles (or even bundles at all).
432
+ HRESULT hrRelatedBundle = QueryPotentialRelatedBundle(pQueryContext, hkUninstallKey, sczRelatedBundleId, &result);
433
+ if (SUCCEEDED(hrRelatedBundle) && BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE != result)
434
+ {
435
+ ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
436
+ }
437
+ }
438
+
439
+LExit:
440
+ ReleaseStr(sczRelatedBundleId);
441
+ ReleaseRegKey(hkUninstallKey);
442
+
443
+ return hr;
444
+}
445
+
446
+static HRESULT QueryPotentialRelatedBundle(
447
+ __in BUNDLE_QUERY_CONTEXT* pQueryContext,
448
+ __in HKEY hkUninstallKey,
449
+ __in_z LPCWSTR wzRelatedBundleId,
450
+ __inout BUNDLE_QUERY_CALLBACK_RESULT* pResult
451
+ )
452
+{
453
+ HRESULT hr = S_OK;
454
+ HKEY hkBundleId = NULL;
455
+ BUNDLE_RELATION_TYPE relationType = BUNDLE_RELATION_NONE;
456
+ BUNDLE_QUERY_RELATED_BUNDLE_RESULT bundle = { };
457
+
458
+ hr = RegOpenEx(hkUninstallKey, wzRelatedBundleId, KEY_READ, pQueryContext->regBitness, &hkBundleId);
459
+ ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", wzRelatedBundleId);
460
+
461
+ hr = DetermineRelationType(pQueryContext, hkBundleId, &relationType);
462
+ if (FAILED(hr))
463
+ {
464
+ ExitFunction();
465
+ }
466
+
467
+ bundle.installContext = pQueryContext->installContext;
468
+ bundle.regBitness = pQueryContext->regBitness;
469
+ bundle.wzBundleId = wzRelatedBundleId;
470
+ bundle.relationType = relationType;
471
+ bundle.hkBundle = hkBundleId;
472
+
473
+ *pResult = pQueryContext->pfnCallback(&bundle, pQueryContext->pvContext);
474
+
475
+LExit:
476
+ ReleaseRegKey(hkBundleId);
477
+
478
+ return hr;
479
+}
480
+
481
+static HRESULT DetermineRelationType(
482
+ __in BUNDLE_QUERY_CONTEXT* pQueryContext,
483
+ __in HKEY hkBundleId,
484
+ __out BUNDLE_RELATION_TYPE* pRelationType
485
+ )
486
+{
487
+ HRESULT hr = S_OK;
488
+ LPWSTR* rgsczUpgradeCodes = NULL;
489
+ DWORD cUpgradeCodes = 0;
490
+ STRINGDICT_HANDLE sdUpgradeCodes = NULL;
491
+ LPWSTR* rgsczAddonCodes = NULL;
492
+ DWORD cAddonCodes = 0;
493
+ STRINGDICT_HANDLE sdAddonCodes = NULL;
494
+ LPWSTR* rgsczDetectCodes = NULL;
495
+ DWORD cDetectCodes = 0;
496
+ STRINGDICT_HANDLE sdDetectCodes = NULL;
497
+ LPWSTR* rgsczPatchCodes = NULL;
498
+ DWORD cPatchCodes = 0;
499
+ STRINGDICT_HANDLE sdPatchCodes = NULL;
500
+
501
+ *pRelationType = BUNDLE_RELATION_NONE;
502
+
503
+ hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
504
+ if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
505
+ {
506
+ TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
507
+
508
+ rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
509
+ ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
510
+
511
+ hr = RegReadString(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
512
+ if (SUCCEEDED(hr))
513
+ {
514
+ cUpgradeCodes = 1;
515
+ }
516
+ }
517
+
518
+ // Compare upgrade codes.
519
+ if (SUCCEEDED(hr))
520
+ {
521
+ hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
522
+ ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
523
+
524
+ // Upgrade relationship: when their upgrade codes match our upgrade codes.
525
+ hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
526
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
527
+ {
528
+ hr = S_OK;
529
+ }
530
+ else
531
+ {
532
+ ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
533
+
534
+ *pRelationType = BUNDLE_RELATION_UPGRADE;
535
+ ExitFunction();
536
+ }
537
+
538
+ // Detect relationship: when their upgrade codes match our detect codes.
539
+ hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
540
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
541
+ {
542
+ hr = S_OK;
543
+ }
544
+ else
545
+ {
546
+ ExitOnFailure(hr, "Failed to do array search for detect code match.");
547
+
548
+ *pRelationType = BUNDLE_RELATION_DETECT;
549
+ ExitFunction();
550
+ }
551
+
552
+ // Dependent relationship: when their upgrade codes match our addon codes.
553
+ hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzAddonCodes), pQueryContext->cAddonCodes);
554
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
555
+ {
556
+ hr = S_OK;
557
+ }
558
+ else
559
+ {
560
+ ExitOnFailure(hr, "Failed to do array search for addon code match.");
561
+
562
+ *pRelationType = BUNDLE_RELATION_DEPENDENT;
563
+ ExitFunction();
564
+ }
565
+
566
+ // Dependent relationship: when their upgrade codes match our patch codes.
567
+ hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzPatchCodes), pQueryContext->cPatchCodes);
568
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
569
+ {
570
+ hr = S_OK;
571
+ }
572
+ else
573
+ {
574
+ ExitOnFailure(hr, "Failed to do array search for addon code match.");
575
+
576
+ *pRelationType = BUNDLE_RELATION_DEPENDENT;
577
+ ExitFunction();
578
+ }
579
+
580
+ ReleaseNullDict(sdUpgradeCodes);
581
+ ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
582
+ }
583
+
584
+ // Compare addon codes.
585
+ hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
586
+ if (SUCCEEDED(hr))
587
+ {
588
+ hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
589
+ ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
590
+
591
+ // Addon relationship: when their addon codes match our detect codes.
592
+ hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
593
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
594
+ {
595
+ hr = S_OK;
596
+ }
597
+ else
598
+ {
599
+ ExitOnFailure(hr, "Failed to do array search for addon code match.");
600
+
601
+ *pRelationType = BUNDLE_RELATION_ADDON;
602
+ ExitFunction();
603
+ }
604
+
605
+ // Addon relationship: when their addon codes match our upgrade codes.
606
+ hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
607
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
608
+ {
609
+ hr = S_OK;
610
+ }
611
+ else
612
+ {
613
+ ExitOnFailure(hr, "Failed to do array search for addon code match.");
614
+
615
+ *pRelationType = BUNDLE_RELATION_ADDON;
616
+ ExitFunction();
617
+ }
618
+
619
+ ReleaseNullDict(sdAddonCodes);
620
+ ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
621
+ }
622
+
623
+ // Compare patch codes.
624
+ hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
625
+ if (SUCCEEDED(hr))
626
+ {
627
+ hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
628
+ ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
629
+
630
+ // Patch relationship: when their patch codes match our detect codes.
631
+ hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
632
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
633
+ {
634
+ hr = S_OK;
635
+ }
636
+ else
637
+ {
638
+ ExitOnFailure(hr, "Failed to do array search for patch code match.");
639
+
640
+ *pRelationType = BUNDLE_RELATION_PATCH;
641
+ ExitFunction();
642
+ }
643
+
644
+ // Patch relationship: when their patch codes match our upgrade codes.
645
+ hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
646
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
647
+ {
648
+ hr = S_OK;
649
+ }
650
+ else
651
+ {
652
+ ExitOnFailure(hr, "Failed to do array search for patch code match.");
653
+
654
+ *pRelationType = BUNDLE_RELATION_PATCH;
655
+ ExitFunction();
656
+ }
657
+
658
+ ReleaseNullDict(sdPatchCodes);
659
+ ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
660
+ }
661
+
662
+ // Compare detect codes.
663
+ hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
664
+ if (SUCCEEDED(hr))
665
+ {
666
+ hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
667
+ ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
668
+
669
+ // Detect relationship: when their detect codes match our detect codes.
670
+ hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
671
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
672
+ {
673
+ hr = S_OK;
674
+ }
675
+ else
676
+ {
677
+ ExitOnFailure(hr, "Failed to do array search for detect code match.");
678
+
679
+ *pRelationType = BUNDLE_RELATION_DETECT;
680
+ ExitFunction();
681
+ }
682
+
683
+ // Dependent relationship: when their detect codes match our addon codes.
684
+ hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzAddonCodes), pQueryContext->cAddonCodes);
685
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
686
+ {
687
+ hr = S_OK;
688
+ }
689
+ else
690
+ {
691
+ ExitOnFailure(hr, "Failed to do array search for addon code match.");
692
+
693
+ *pRelationType = BUNDLE_RELATION_DEPENDENT;
694
+ ExitFunction();
695
+ }
696
+
697
+ // Dependent relationship: when their detect codes match our patch codes.
698
+ hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzPatchCodes), pQueryContext->cPatchCodes);
699
+ if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
700
+ {
701
+ hr = S_OK;
702
+ }
703
+ else
704
+ {
705
+ ExitOnFailure(hr, "Failed to do array search for addon code match.");
706
+
707
+ *pRelationType = BUNDLE_RELATION_DEPENDENT;
708
+ ExitFunction();
709
+ }
710
+
711
+ ReleaseNullDict(sdDetectCodes);
712
+ ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
713
+ }
714
+
715
+LExit:
716
+ if (SUCCEEDED(hr) && BUNDLE_RELATION_NONE == *pRelationType)
717
+ {
718
+ hr = E_NOTFOUND;
719
+ }
720
+
721
+ ReleaseDict(sdUpgradeCodes);
722
+ ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
723
+ ReleaseDict(sdAddonCodes);
724
+ ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
725
+ ReleaseDict(sdDetectCodes);
726
+ ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
727
+ ReleaseDict(sdPatchCodes);
728
+ ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
729
+
730
+ return hr;
731
+}
732
+
733
static HRESULT LocateAndQueryBundleValue(
734
__in_z LPCWSTR wzBundleId,
735
__in_opt LPCWSTR wzSubKey,
src/libs/dutil/WixToolset.DUtil/inc/butil.h
+49
@@ -12,6 +12,37 @@ typedef enum _BUNDLE_INSTALL_CONTEXT
12
BUNDLE_INSTALL_CONTEXT_USER,
13
} BUNDLE_INSTALL_CONTEXT;
14
15
+typedef enum _BUNDLE_QUERY_CALLBACK_RESULT
16
+{
17
+ BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE,
18
+ BUNDLE_QUERY_CALLBACK_RESULT_CANCEL,
19
+} BUNDLE_QUERY_CALLBACK_RESULT;
20
+
21
+typedef enum _BUNDLE_RELATION_TYPE
22
+{
23
+ BUNDLE_RELATION_NONE,
24
+ BUNDLE_RELATION_DETECT,
25
+ BUNDLE_RELATION_UPGRADE,
26
+ BUNDLE_RELATION_ADDON,
27
+ BUNDLE_RELATION_PATCH,
28
+ BUNDLE_RELATION_DEPENDENT,
29
+ BUNDLE_RELATION_UPDATE,
30
+} BUNDLE_RELATION_TYPE;
31
+
32
+typedef struct _BUNDLE_QUERY_RELATED_BUNDLE_RESULT
33
+{
34
+ LPCWSTR wzBundleId;
35
+ BUNDLE_INSTALL_CONTEXT installContext;
36
+ REG_KEY_BITNESS regBitness;
37
+ HKEY hkBundle;
38
+ BUNDLE_RELATION_TYPE relationType;
39
+} BUNDLE_QUERY_RELATED_BUNDLE_RESULT;
40
+
41
+typedef BUNDLE_QUERY_CALLBACK_RESULT(CALLBACK *PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK)(
42
+ __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
43
+ __in_opt LPVOID pvContext
44
+ );
45
+
46
47
/********************************************************************
48
BundleGetBundleInfo - Queries the bundle installation metadata for a given property,
@@ -155,6 +186,24 @@ HRESULT DAPI BundleGetBundleVariableFixed(
186
__inout SIZE_T* pcchValue
187
);
188
189
+/********************************************************************
190
+BundleQueryRelatedBundles - Queries the bundle installation metadata for installs with the given detect, upgrade, addon, and patch codes.
191
+ Passes each related bundle to the callback function.
192
+********************************************************************/
193
+HRESULT BundleQueryRelatedBundles(
194
+ __in BUNDLE_INSTALL_CONTEXT installContext,
195
+ __in_z_opt LPCWSTR* rgwzDetectCodes,
196
+ __in DWORD cDetectCodes,
197
+ __in_z_opt LPCWSTR* rgwzUpgradeCodes,
198
+ __in DWORD cUpgradeCodes,
199
+ __in_z_opt LPCWSTR* rgwzAddonCodes,
200
+ __in DWORD cAddonCodes,
201
+ __in_z_opt LPCWSTR* rgwzPatchCodes,
202
+ __in DWORD cPatchCodes,
203
+ __in PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback,
204
+ __in_opt LPVOID pvContext
205
+ );
206
+
207
208
#ifdef __cplusplus
209
}