main
cpp 811 lines 27.4 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 // Exit macros
6 #define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
7 #define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
8 #define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
9 #define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
10 #define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
11 #define ButilExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_BUTIL, x, e, s, __VA_ARGS__)
12 #define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
13 #define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__)
14 #define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__)
15 #define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__)
16 #define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__)
17 #define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__)
18 #define ButilExitOnPathFailure(x, b, s, ...) ExitOnPathFailureSource(DUTIL_SOURCE_BUTIL, x, b, s, __VA_ARGS__)
19
20 // constants
21 // From engine/registration.h
22 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
23 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode";
24 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode";
25 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode";
26 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
27 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
28 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
29
30 enum INTERNAL_BUNDLE_STATUS
31 {
32 INTERNAL_BUNDLE_STATUS_SUCCESS,
33 INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE,
34 INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY,
35 };
36
37 typedef struct _BUNDLE_QUERY_CONTEXT
38 {
39 BUNDLE_INSTALL_CONTEXT installContext;
40 REG_KEY_BITNESS regBitness;
41 PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback;
42 LPVOID pvContext;
43
44 LPCWSTR* rgwzDetectCodes;
45 DWORD cDetectCodes;
46
47 LPCWSTR* rgwzUpgradeCodes;
48 DWORD cUpgradeCodes;
49
50 LPCWSTR* rgwzAddonCodes;
51 DWORD cAddonCodes;
52
53 LPCWSTR* rgwzPatchCodes;
54 DWORD cPatchCodes;
55 } BUNDLE_QUERY_CONTEXT;
56
57 // Forward declarations.
58 static HRESULT QueryRelatedBundlesForScopeAndBitness(
59 __in BUNDLE_QUERY_CONTEXT* pQueryContext
60 );
61 static HRESULT QueryPotentialRelatedBundle(
62 __in BUNDLE_QUERY_CONTEXT* pQueryContext,
63 __in HKEY hkUninstallKey,
64 __in_z LPCWSTR wzRelatedBundleId,
65 __inout BUNDLE_QUERY_CALLBACK_RESULT* pResult
66 );
67 static HRESULT DetermineRelationType(
68 __in BUNDLE_QUERY_CONTEXT* pQueryContext,
69 __in HKEY hkBundleId,
70 __out BUNDLE_RELATION_TYPE* pRelationType
71 );
72 /********************************************************************
73 LocateAndQueryBundleValue - Locates the requested key for the bundle,
74 then queries the registry type for requested value.
75
76 NOTE: caller is responsible for closing key
77 ********************************************************************/
78 static HRESULT LocateAndQueryBundleValue(
79 __in_z LPCWSTR wzBundleId,
80 __in_opt LPCWSTR wzSubKey,
81 __in LPCWSTR wzValueName,
82 __inout HKEY* phKey,
83 __inout DWORD* pdwType,
84 __out INTERNAL_BUNDLE_STATUS* pStatus
85 );
86 static HRESULT CopyStringToBuffer(
87 __in_z LPWSTR wzValue,
88 __in_z_opt LPWSTR wzBuffer,
89 __inout SIZE_T* pcchBuffer
90 );
91
92
93 DAPI_(HRESULT) BundleGetBundleInfo(
94 __in_z LPCWSTR wzBundleId,
95 __in_z LPCWSTR wzAttribute,
96 __deref_out_z LPWSTR* psczValue
97 )
98 {
99 HRESULT hr = S_OK;
100 HKEY hkBundle = NULL;
101 INTERNAL_BUNDLE_STATUS status = INTERNAL_BUNDLE_STATUS_SUCCESS;
102 DWORD dwType = 0;
103 DWORD dwValue = 0;
104
105 if (!wzBundleId || !wzAttribute || !psczValue)
106 {
107 ButilExitWithRootFailure(hr, E_INVALIDARG, "An invalid parameter was passed to the function.");
108 }
109
110 hr = LocateAndQueryBundleValue(wzBundleId, NULL, wzAttribute, &hkBundle, &dwType, &status);
111 ButilExitOnFailure(hr, "Failed to locate and query bundle attribute.");
112
113 switch (status)
114 {
115 case INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE:
116 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT));
117 case INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY:
118 // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY
119 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY));
120 }
121
122 switch (dwType)
123 {
124 case REG_SZ:
125 hr = RegReadString(hkBundle, wzAttribute, psczValue);
126 ButilExitOnFailure(hr, "Failed to read string property.");
127 break;
128 case REG_DWORD:
129 hr = RegReadNumber(hkBundle, wzAttribute, &dwValue);
130 ButilExitOnFailure(hr, "Failed to read dword property.");
131
132 hr = StrAllocFormatted(psczValue, L"%d", dwValue);
133 ButilExitOnFailure(hr, "Failed to format dword property as string.");
134 break;
135 default:
136 ButilExitWithRootFailure(hr, E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType);
137 }
138
139 LExit:
140 ReleaseRegKey(hkBundle);
141
142 return hr;
143 }
144
145
146 DAPI_(HRESULT) BundleGetBundleInfoFixed(
147 __in_z LPCWSTR wzBundleId,
148 __in_z LPCWSTR wzAttribute,
149 __out_ecount_opt(*pcchValue) LPWSTR wzValue,
150 __inout SIZE_T* pcchValue
151 )
152 {
153 HRESULT hr = S_OK;
154 LPWSTR sczValue = NULL;
155
156 if (!pcchValue)
157 {
158 ButilExitWithRootFailure(hr, E_INVALIDARG, "An invalid parameter was passed to the function.");
159 }
160
161 hr = BundleGetBundleInfo(wzBundleId, wzAttribute, &sczValue);
162 if (SUCCEEDED(hr))
163 {
164 hr = CopyStringToBuffer(sczValue, wzValue, pcchValue);
165 }
166
167 LExit:
168 ReleaseStr(sczValue);
169
170 return hr;
171 }
172
173
174 DAPI_(HRESULT) BundleEnumRelatedBundle(
175 __in_z LPCWSTR wzUpgradeCode,
176 __in BUNDLE_INSTALL_CONTEXT context,
177 __in REG_KEY_BITNESS kbKeyBitness,
178 __inout PDWORD pdwStartIndex,
179 __deref_out_z LPWSTR* psczBundleId
180 )
181 {
182 HRESULT hr = S_OK;
183 BOOL fUpgradeCodeFound = FALSE;
184 HKEY hkUninstall = NULL;
185 HKEY hkBundle = NULL;
186 LPWSTR sczUninstallSubKey = NULL;
187 LPWSTR sczUninstallSubKeyPath = NULL;
188 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
189 BUNDLE_QUERY_CONTEXT queryContext = { };
190 BUNDLE_RELATION_TYPE relationType = BUNDLE_RELATION_NONE;
191
192 queryContext.installContext = context;
193 queryContext.rgwzUpgradeCodes = &wzUpgradeCode;
194 queryContext.cUpgradeCodes = 1;
195
196 if (!wzUpgradeCode || !pdwStartIndex)
197 {
198 ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
199 }
200
201 hr = RegOpenEx(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, kbKeyBitness, &hkUninstall);
202 ButilExitOnFailure(hr, "Failed to open bundle uninstall key path.");
203
204 for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++)
205 {
206 hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey);
207 ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path.");
208
209 hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey);
210 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
211
212 hr = RegOpenEx(hkRoot, sczUninstallSubKeyPath, KEY_READ, kbKeyBitness, &hkBundle);
213 ButilExitOnFailure(hr, "Failed to open uninstall key path.");
214
215 hr = DetermineRelationType(&queryContext, hkBundle, &relationType);
216 if (SUCCEEDED(hr) && BUNDLE_RELATION_UPGRADE == relationType)
217 {
218 fUpgradeCodeFound = TRUE;
219 *pdwStartIndex = dwIndex;
220
221 if (psczBundleId)
222 {
223 *psczBundleId = sczUninstallSubKey;
224 sczUninstallSubKey = NULL;
225 }
226
227 break;
228 }
229
230 // Cleanup before next iteration
231 ReleaseRegKey(hkBundle);
232 }
233
234 LExit:
235 ReleaseStr(sczUninstallSubKey);
236 ReleaseStr(sczUninstallSubKeyPath);
237 ReleaseRegKey(hkBundle);
238 ReleaseRegKey(hkUninstall);
239
240 return FAILED(hr) ? hr : fUpgradeCodeFound ? S_OK : S_FALSE;
241 }
242
243
244 DAPI_(HRESULT) BundleEnumRelatedBundleFixed(
245 __in_z LPCWSTR wzUpgradeCode,
246 __in BUNDLE_INSTALL_CONTEXT context,
247 __in REG_KEY_BITNESS kbKeyBitness,
248 __inout PDWORD pdwStartIndex,
249 __out_ecount(MAX_GUID_CHARS+1) LPWSTR wzBundleId
250 )
251 {
252 HRESULT hr = S_OK;
253 LPWSTR sczValue = NULL;
254 size_t cchValue = 0;
255
256 hr = BundleEnumRelatedBundle(wzUpgradeCode, context, kbKeyBitness, pdwStartIndex, &sczValue);
257 if (S_OK == hr && wzBundleId)
258 {
259 hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchValue);
260 ButilExitOnRootFailure(hr, "Failed to calculate length of string.");
261
262 hr = ::StringCchCopyNExW(wzBundleId, MAX_GUID_CHARS + 1, sczValue, cchValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
263 ButilExitOnRootFailure(hr, "Failed to copy the property value to the output buffer.");
264 }
265
266 LExit:
267 ReleaseStr(sczValue);
268
269 return hr;
270 }
271
272
273 DAPI_(HRESULT) BundleGetBundleVariable(
274 __in_z LPCWSTR wzBundleId,
275 __in_z LPCWSTR wzVariable,
276 __deref_out_z LPWSTR* psczValue
277 )
278 {
279 HRESULT hr = S_OK;
280 HKEY hkBundle = NULL;
281 INTERNAL_BUNDLE_STATUS status = INTERNAL_BUNDLE_STATUS_SUCCESS;
282 DWORD dwType = 0;
283
284 if (!wzBundleId || !wzVariable || !psczValue)
285 {
286 ButilExitWithRootFailure(hr, E_INVALIDARG, "An invalid parameter was passed to the function.");
287 }
288
289 hr = LocateAndQueryBundleValue(wzBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY, wzVariable, &hkBundle, &dwType, &status);
290 ButilExitOnFailure(hr, "Failed to locate and query bundle variable.");
291
292 switch (status)
293 {
294 case INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE:
295 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT));
296 case INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY:
297 // If the bundle doesn't have the shared variable defined, return ERROR_UNKNOWN_PROPERTY
298 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY));
299 }
300
301 switch (dwType)
302 {
303 case REG_SZ:
304 hr = RegReadString(hkBundle, wzVariable, psczValue);
305 ButilExitOnFailure(hr, "Failed to read string shared variable.");
306 break;
307 case REG_NONE:
308 hr = S_OK;
309 break;
310 default:
311 ButilExitWithRootFailure(hr, E_NOTIMPL, "Reading bundle variable of type 0x%x not implemented.", dwType);
312 }
313
314 LExit:
315 ReleaseRegKey(hkBundle);
316
317 return hr;
318 }
319
320
321 DAPI_(HRESULT) BundleGetBundleVariableFixed(
322 __in_z LPCWSTR wzBundleId,
323 __in_z LPCWSTR wzVariable,
324 __out_ecount_opt(*pcchValue) LPWSTR wzValue,
325 __inout SIZE_T* pcchValue
326 )
327 {
328 HRESULT hr = S_OK;
329 LPWSTR sczValue = NULL;
330
331 if (!pcchValue)
332 {
333 ButilExitWithRootFailure(hr, E_INVALIDARG, "An invalid parameter was passed to the function.");
334 }
335
336 hr = BundleGetBundleVariable(wzBundleId, wzVariable, &sczValue);
337 if (SUCCEEDED(hr))
338 {
339 hr = CopyStringToBuffer(sczValue, wzValue, pcchValue);
340 }
341
342 LExit:
343 ReleaseStr(sczValue);
344
345 return hr;
346 }
347
348 DAPI_(HRESULT) BundleQueryRelatedBundles(
349 __in BUNDLE_INSTALL_CONTEXT installContext,
350 __in_z_opt LPCWSTR* rgwzDetectCodes,
351 __in DWORD cDetectCodes,
352 __in_z_opt LPCWSTR* rgwzUpgradeCodes,
353 __in DWORD cUpgradeCodes,
354 __in_z_opt LPCWSTR* rgwzAddonCodes,
355 __in DWORD cAddonCodes,
356 __in_z_opt LPCWSTR* rgwzPatchCodes,
357 __in DWORD cPatchCodes,
358 __in PFNBUNDLE_QUERY_RELATED_BUNDLE_CALLBACK pfnCallback,
359 __in_opt LPVOID pvContext
360 )
361 {
362 HRESULT hr = S_OK;
363 BUNDLE_QUERY_CONTEXT queryContext = { };
364 BOOL fSearch64 = TRUE;
365
366 #if !defined(_WIN64)
367 // On 32-bit OS's, the requested bitness of the key is ignored so need to avoid searching the same place twice.
368 ProcWow64(::GetCurrentProcess(), &fSearch64);
369 #endif
370
371 queryContext.installContext = installContext;
372 queryContext.rgwzDetectCodes = rgwzDetectCodes;
373 queryContext.cDetectCodes = cDetectCodes;
374 queryContext.rgwzUpgradeCodes = rgwzUpgradeCodes;
375 queryContext.cUpgradeCodes = cUpgradeCodes;
376 queryContext.rgwzAddonCodes = rgwzAddonCodes;
377 queryContext.cAddonCodes = cAddonCodes;
378 queryContext.rgwzPatchCodes = rgwzPatchCodes;
379 queryContext.cPatchCodes = cPatchCodes;
380 queryContext.pfnCallback = pfnCallback;
381 queryContext.pvContext = pvContext;
382
383 queryContext.regBitness = REG_KEY_32BIT;
384
385 hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
386 ButilExitOnFailure(hr, "Failed to query 32-bit related bundles.");
387
388 if (fSearch64)
389 {
390 queryContext.regBitness = REG_KEY_64BIT;
391
392 hr = QueryRelatedBundlesForScopeAndBitness(&queryContext);
393 ButilExitOnFailure(hr, "Failed to query 64-bit related bundles.");
394 }
395
396 LExit:
397 return hr;
398 }
399
400 static HRESULT QueryRelatedBundlesForScopeAndBitness(
401 __in BUNDLE_QUERY_CONTEXT* pQueryContext
402 )
403 {
404 HRESULT hr = S_OK;
405 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == pQueryContext->installContext ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
406 HKEY hkUninstallKey = NULL;
407 BOOL fExists = FALSE;
408 LPWSTR sczRelatedBundleId = NULL;
409 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
410
411 hr = RegOpenEx(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, pQueryContext->regBitness, &hkUninstallKey);
412 ButilExitOnPathFailure(hr, fExists, "Failed to open uninstall registry key.");
413
414 if (!fExists)
415 {
416 ExitFunction1(hr = S_OK);
417 }
418
419 for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
420 {
421 hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
422 if (E_NOMOREITEMS == hr)
423 {
424 hr = S_OK;
425 break;
426 }
427 ButilExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
428
429 // Ignore failures here since we'll often find products that aren't actually
430 // related bundles (or even bundles at all).
431 HRESULT hrRelatedBundle = QueryPotentialRelatedBundle(pQueryContext, hkUninstallKey, sczRelatedBundleId, &result);
432 if (SUCCEEDED(hrRelatedBundle) && BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE != result)
433 {
434 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
435 }
436 }
437
438 LExit:
439 ReleaseStr(sczRelatedBundleId);
440 ReleaseRegKey(hkUninstallKey);
441
442 return hr;
443 }
444
445 static HRESULT QueryPotentialRelatedBundle(
446 __in BUNDLE_QUERY_CONTEXT* pQueryContext,
447 __in HKEY hkUninstallKey,
448 __in_z LPCWSTR wzRelatedBundleId,
449 __inout BUNDLE_QUERY_CALLBACK_RESULT* pResult
450 )
451 {
452 HRESULT hr = S_OK;
453 HKEY hkBundleId = NULL;
454 BUNDLE_RELATION_TYPE relationType = BUNDLE_RELATION_NONE;
455 BUNDLE_QUERY_RELATED_BUNDLE_RESULT bundle = { };
456
457 hr = RegOpenEx(hkUninstallKey, wzRelatedBundleId, KEY_READ, pQueryContext->regBitness, &hkBundleId);
458 ButilExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", wzRelatedBundleId);
459
460 hr = DetermineRelationType(pQueryContext, hkBundleId, &relationType);
461 if (FAILED(hr))
462 {
463 ExitFunction();
464 }
465
466 bundle.installContext = pQueryContext->installContext;
467 bundle.regBitness = pQueryContext->regBitness;
468 bundle.wzBundleId = wzRelatedBundleId;
469 bundle.relationType = relationType;
470 bundle.hkBundle = hkBundleId;
471
472 *pResult = pQueryContext->pfnCallback(&bundle, pQueryContext->pvContext);
473
474 LExit:
475 ReleaseRegKey(hkBundleId);
476
477 return hr;
478 }
479
480 static HRESULT DetermineRelationType(
481 __in BUNDLE_QUERY_CONTEXT* pQueryContext,
482 __in HKEY hkBundleId,
483 __out BUNDLE_RELATION_TYPE* pRelationType
484 )
485 {
486 HRESULT hr = S_OK;
487 LPWSTR* rgsczUpgradeCodes = NULL;
488 DWORD cUpgradeCodes = 0;
489 STRINGDICT_HANDLE sdUpgradeCodes = NULL;
490 LPWSTR* rgsczAddonCodes = NULL;
491 DWORD cAddonCodes = 0;
492 STRINGDICT_HANDLE sdAddonCodes = NULL;
493 LPWSTR* rgsczDetectCodes = NULL;
494 DWORD cDetectCodes = 0;
495 STRINGDICT_HANDLE sdDetectCodes = NULL;
496 LPWSTR* rgsczPatchCodes = NULL;
497 DWORD cPatchCodes = 0;
498 STRINGDICT_HANDLE sdPatchCodes = NULL;
499
500 *pRelationType = BUNDLE_RELATION_NONE;
501
502 hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
503 if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
504 {
505 TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
506
507 rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
508 ButilExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
509
510 hr = RegReadString(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
511 if (SUCCEEDED(hr))
512 {
513 cUpgradeCodes = 1;
514 }
515 }
516
517 // Compare upgrade codes.
518 if (SUCCEEDED(hr))
519 {
520 hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
521 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
522
523 // Upgrade relationship: when their upgrade codes match our upgrade codes.
524 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
525 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
526 {
527 hr = S_OK;
528 }
529 else
530 {
531 ButilExitOnFailure(hr, "Failed to do array search for upgrade code match.");
532
533 *pRelationType = BUNDLE_RELATION_UPGRADE;
534 ExitFunction();
535 }
536
537 // Detect relationship: when their upgrade codes match our detect codes.
538 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
539 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
540 {
541 hr = S_OK;
542 }
543 else
544 {
545 ButilExitOnFailure(hr, "Failed to do array search for detect code match.");
546
547 *pRelationType = BUNDLE_RELATION_DETECT;
548 ExitFunction();
549 }
550
551 // Dependent relationship: when their upgrade codes match our addon codes.
552 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzAddonCodes), pQueryContext->cAddonCodes);
553 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
554 {
555 hr = S_OK;
556 }
557 else
558 {
559 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
560
561 *pRelationType = BUNDLE_RELATION_DEPENDENT_ADDON;
562 ExitFunction();
563 }
564
565 // Dependent relationship: when their upgrade codes match our patch codes.
566 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzPatchCodes), pQueryContext->cPatchCodes);
567 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
568 {
569 hr = S_OK;
570 }
571 else
572 {
573 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
574
575 *pRelationType = BUNDLE_RELATION_DEPENDENT_PATCH;
576 ExitFunction();
577 }
578
579 ReleaseNullDict(sdUpgradeCodes);
580 ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
581 }
582
583 // Compare addon codes.
584 hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
585 if (SUCCEEDED(hr))
586 {
587 hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
588 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
589
590 // Addon relationship: when their addon codes match our detect codes.
591 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
592 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
593 {
594 hr = S_OK;
595 }
596 else
597 {
598 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
599
600 *pRelationType = BUNDLE_RELATION_ADDON;
601 ExitFunction();
602 }
603
604 // Addon relationship: when their addon codes match our upgrade codes.
605 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
606 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
607 {
608 hr = S_OK;
609 }
610 else
611 {
612 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
613
614 *pRelationType = BUNDLE_RELATION_ADDON;
615 ExitFunction();
616 }
617
618 ReleaseNullDict(sdAddonCodes);
619 ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
620 }
621
622 // Compare patch codes.
623 hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
624 if (SUCCEEDED(hr))
625 {
626 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
627 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
628
629 // Patch relationship: when their patch codes match our detect codes.
630 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
631 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
632 {
633 hr = S_OK;
634 }
635 else
636 {
637 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
638
639 *pRelationType = BUNDLE_RELATION_PATCH;
640 ExitFunction();
641 }
642
643 // Patch relationship: when their patch codes match our upgrade codes.
644 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzUpgradeCodes), pQueryContext->cUpgradeCodes);
645 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
646 {
647 hr = S_OK;
648 }
649 else
650 {
651 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
652
653 *pRelationType = BUNDLE_RELATION_PATCH;
654 ExitFunction();
655 }
656
657 ReleaseNullDict(sdPatchCodes);
658 ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
659 }
660
661 // Compare detect codes.
662 hr = RegReadStringArray(hkBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
663 if (SUCCEEDED(hr))
664 {
665 hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
666 ButilExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
667
668 // Detect relationship: when their detect codes match our detect codes.
669 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzDetectCodes), pQueryContext->cDetectCodes);
670 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
671 {
672 hr = S_OK;
673 }
674 else
675 {
676 ButilExitOnFailure(hr, "Failed to do array search for detect code match.");
677
678 *pRelationType = BUNDLE_RELATION_DETECT;
679 ExitFunction();
680 }
681
682 // Dependent relationship: when their detect codes match our addon codes.
683 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzAddonCodes), pQueryContext->cAddonCodes);
684 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
685 {
686 hr = S_OK;
687 }
688 else
689 {
690 ButilExitOnFailure(hr, "Failed to do array search for addon code match.");
691
692 *pRelationType = BUNDLE_RELATION_DEPENDENT_ADDON;
693 ExitFunction();
694 }
695
696 // Dependent relationship: when their detect codes match our patch codes.
697 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pQueryContext->rgwzPatchCodes), pQueryContext->cPatchCodes);
698 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
699 {
700 hr = S_OK;
701 }
702 else
703 {
704 ButilExitOnFailure(hr, "Failed to do array search for patch code match.");
705
706 *pRelationType = BUNDLE_RELATION_DEPENDENT_PATCH;
707 ExitFunction();
708 }
709
710 ReleaseNullDict(sdDetectCodes);
711 ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
712 }
713
714 LExit:
715 if (SUCCEEDED(hr) && BUNDLE_RELATION_NONE == *pRelationType)
716 {
717 hr = E_NOTFOUND;
718 }
719
720 ReleaseDict(sdUpgradeCodes);
721 ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
722 ReleaseDict(sdAddonCodes);
723 ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
724 ReleaseDict(sdDetectCodes);
725 ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
726 ReleaseDict(sdPatchCodes);
727 ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
728
729 return hr;
730 }
731
732 static HRESULT LocateAndQueryBundleValue(
733 __in_z LPCWSTR wzBundleId,
734 __in_opt LPCWSTR wzSubKey,
735 __in LPCWSTR wzValueName,
736 __inout HKEY* phKey,
737 __inout DWORD* pdwType,
738 __out INTERNAL_BUNDLE_STATUS* pStatus
739 )
740 {
741 HRESULT hr = S_OK;
742 LPWSTR sczKeypath = NULL;
743 BOOL fExists = TRUE;
744
745 *pStatus = INTERNAL_BUNDLE_STATUS_SUCCESS;
746
747 if (wzSubKey)
748 {
749 hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId, wzSubKey);
750 }
751 else
752 {
753 hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId);
754 }
755 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
756
757 if (FAILED(hr = RegOpenEx(HKEY_LOCAL_MACHINE, sczKeypath, KEY_READ, REG_KEY_32BIT, phKey)) &&
758 FAILED(hr = RegOpenEx(HKEY_LOCAL_MACHINE, sczKeypath, KEY_READ, REG_KEY_64BIT, phKey)) &&
759 FAILED(hr = RegOpenEx(HKEY_CURRENT_USER, sczKeypath, KEY_READ, REG_KEY_DEFAULT, phKey)))
760 {
761 ButilExitOnPathFailure(hr, fExists, "Failed to open bundle key.");
762
763 *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE;
764 ExitFunction1(hr = S_OK);
765 }
766
767 hr = RegGetType(*phKey, wzValueName, pdwType);
768 ButilExitOnPathFailure(hr, fExists, "Failed to read bundle value.");
769
770 if (!fExists)
771 {
772 *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY;
773 ExitFunction1(hr = S_OK);
774 }
775
776 LExit:
777 ReleaseStr(sczKeypath);
778
779 return hr;
780 }
781
782 static HRESULT CopyStringToBuffer(
783 __in_z LPWSTR wzValue,
784 __in_z_opt LPWSTR wzBuffer,
785 __inout SIZE_T* pcchBuffer
786 )
787 {
788 HRESULT hr = S_OK;
789 BOOL fTooSmall = !wzBuffer;
790
791 if (!fTooSmall)
792 {
793 hr = ::StringCchCopyExW(wzBuffer, *pcchBuffer, wzValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
794 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
795 {
796 fTooSmall = TRUE;
797 }
798 }
799
800 if (fTooSmall)
801 {
802 hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(pcchBuffer));
803 if (SUCCEEDED(hr))
804 {
805 hr = E_MOREDATA;
806 *pcchBuffer += 1; // null terminator.
807 }
808 }
809
810 return hr;
811 }