aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine')
-rw-r--r--src/burn/engine/precomp.h1
-rw-r--r--src/burn/engine/relatedbundle.cpp390
2 files changed, 54 insertions, 337 deletions
diff --git a/src/burn/engine/precomp.h b/src/burn/engine/precomp.h
index 26adf44c..c83c1e74 100644
--- a/src/burn/engine/precomp.h
+++ b/src/burn/engine/precomp.h
@@ -55,6 +55,7 @@
55#include <atomutil.h> 55#include <atomutil.h>
56#include <apuputil.h> 56#include <apuputil.h>
57#include <dpiutil.h> 57#include <dpiutil.h>
58#include <butil.h>
58 59
59#include "BootstrapperEngine.h" 60#include "BootstrapperEngine.h"
60#include "BootstrapperApplication.h" 61#include "BootstrapperApplication.h"
diff --git a/src/burn/engine/relatedbundle.cpp b/src/burn/engine/relatedbundle.cpp
index 3e0bc799..e6633131 100644
--- a/src/burn/engine/relatedbundle.cpp
+++ b/src/burn/engine/relatedbundle.cpp
@@ -2,6 +2,12 @@
2 2
3#include "precomp.h" 3#include "precomp.h"
4 4
5typedef struct _BUNDLE_QUERY_CONTEXT
6{
7 BURN_REGISTRATION* pRegistration;
8 BURN_RELATED_BUNDLES* pRelatedBundles;
9} BUNDLE_QUERY_CONTEXT;
10
5// internal function declarations 11// internal function declarations
6 12
7static __callback int __cdecl CompareRelatedBundles( 13static __callback int __cdecl CompareRelatedBundles(
@@ -9,25 +15,15 @@ static __callback int __cdecl CompareRelatedBundles(
9 __in const void* pvLeft, 15 __in const void* pvLeft,
10 __in const void* pvRight 16 __in const void* pvRight
11); 17);
12static HRESULT InitializeForScopeAndBitness( 18static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
13 __in BOOL fPerMachine, 19 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
14 __in REG_KEY_BITNESS regBitness, 20 __in_opt LPVOID pvContext
15 __in BURN_REGISTRATION* pRegistration,
16 __in BURN_RELATED_BUNDLES* pRelatedBundles
17 ); 21 );
18static HRESULT LoadIfRelatedBundle( 22static HRESULT LoadIfRelatedBundle(
19 __in BOOL fPerMachine, 23 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
20 __in REG_KEY_BITNESS regBitness,
21 __in HKEY hkUninstallKey,
22 __in_z LPCWSTR sczRelatedBundleId,
23 __in BURN_REGISTRATION* pRegistration, 24 __in BURN_REGISTRATION* pRegistration,
24 __in BURN_RELATED_BUNDLES* pRelatedBundles 25 __in BURN_RELATED_BUNDLES* pRelatedBundles
25 ); 26 );
26static HRESULT DetermineRelationType(
27 __in HKEY hkBundleId,
28 __in BURN_REGISTRATION* pRegistration,
29 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
30 );
31static HRESULT LoadRelatedBundleFromKey( 27static HRESULT LoadRelatedBundleFromKey(
32 __in_z LPCWSTR wzRelatedBundleId, 28 __in_z LPCWSTR wzRelatedBundleId,
33 __in HKEY hkBundleId, 29 __in HKEY hkBundleId,
@@ -46,12 +42,25 @@ extern "C" HRESULT RelatedBundlesInitializeForScope(
46 ) 42 )
47{ 43{
48 HRESULT hr = S_OK; 44 HRESULT hr = S_OK;
49 45 BUNDLE_INSTALL_CONTEXT installContext = fPerMachine ? BUNDLE_INSTALL_CONTEXT_MACHINE : BUNDLE_INSTALL_CONTEXT_USER;
50 hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_32BIT, pRegistration, pRelatedBundles); 46 BUNDLE_QUERY_CONTEXT queryContext = { };
51 ExitOnFailure(hr, "Failed to open 32-bit uninstall registry key."); 47
52 48 queryContext.pRegistration = pRegistration;
53 hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_64BIT, pRegistration, pRelatedBundles); 49 queryContext.pRelatedBundles = pRelatedBundles;
54 ExitOnFailure(hr, "Failed to open 64-bit uninstall registry key."); 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.");
55 64
56LExit: 65LExit:
57 return hr; 66 return hr;
@@ -166,346 +175,53 @@ static __callback int __cdecl CompareRelatedBundles(
166 return ret; 175 return ret;
167} 176}
168 177
169static HRESULT InitializeForScopeAndBitness( 178static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
170 __in BOOL fPerMachine, 179 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
171 __in REG_KEY_BITNESS regBitness, 180 __in_opt LPVOID pvContext
172 __in BURN_REGISTRATION * pRegistration, 181 )
173 __in BURN_RELATED_BUNDLES * pRelatedBundles
174)
175{ 182{
176 HRESULT hr = S_OK; 183 HRESULT hr = S_OK;
177 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 184 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
178 HKEY hkUninstallKey = NULL; 185 BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext);
179 LPWSTR sczRelatedBundleId = NULL;
180 186
181 hr = RegOpenEx(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, regBitness, &hkUninstallKey); 187 hr = LoadIfRelatedBundle(pBundle, pContext->pRegistration, pContext->pRelatedBundles);
182 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) 188 ExitOnFailure(hr, "Failed to load related bundle: %ls", pBundle->wzBundleId);
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 }
207 189
208LExit: 190LExit:
209 ReleaseStr(sczRelatedBundleId); 191 return result;
210 ReleaseRegKey(hkUninstallKey);
211
212 return hr;
213} 192}
214 193
215static HRESULT LoadIfRelatedBundle( 194static HRESULT LoadIfRelatedBundle(
216 __in BOOL fPerMachine, 195 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
217 __in REG_KEY_BITNESS regBitness,
218 __in HKEY hkUninstallKey,
219 __in_z LPCWSTR sczRelatedBundleId,
220 __in BURN_REGISTRATION* pRegistration, 196 __in BURN_REGISTRATION* pRegistration,
221 __in BURN_RELATED_BUNDLES* pRelatedBundles 197 __in BURN_RELATED_BUNDLES* pRelatedBundles
222 ) 198 )
223{ 199{
224 HRESULT hr = S_OK; 200 HRESULT hr = S_OK;
225 HKEY hkBundleId = NULL; 201 BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
226 BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE; 202 BOOTSTRAPPER_RELATION_TYPE relationType = (BOOTSTRAPPER_RELATION_TYPE)pBundle->relationType;
227 203 BURN_RELATED_BUNDLE* pRelatedBundle = NULL;
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
253LExit:
254 ReleaseRegKey(hkBundleId);
255
256 return hr;
257}
258
259static 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 }
401 204
402 // Compare patch codes. 205 // If we found our bundle id, it's not a related bundle.
403 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes); 206 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pRegistration->sczId, -1))
404 if (SUCCEEDED(hr))
405 { 207 {
406 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE); 208 ExitFunction1(hr = S_FALSE);
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);
439 } 209 }
440 210
441 // Compare detect codes. 211 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
442 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes); 212 ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
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");
447 213
448 // Detect relationship: when their detect codes match our detect codes. 214 pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
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.");
457 215
458 *pRelationType = BOOTSTRAPPER_RELATION_DETECT; 216 hr = LoadRelatedBundleFromKey(pBundle->wzBundleId, pBundle->hkBundle, fPerMachine, relationType, pRelatedBundle);
459 ExitFunction(); 217 ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", pBundle->wzBundleId);
460 }
461 218
462 // Dependent relationship: when their detect codes match our addon codes. 219 hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration);
463 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); 220 ExitOnFailure(hr, "Failed to detect dependencies for related bundle.");
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.");
485 221
486 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; 222 ++pRelatedBundles->cRelatedBundles;
487 ExitFunction();
488 }
489
490 ReleaseNullDict(sdDetectCodes);
491 ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
492 }
493 223
494LExit: 224LExit:
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
509 return hr; 225 return hr;
510} 226}
511 227