aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/search.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/search.cpp')
-rw-r--r--src/burn/engine/search.cpp1303
1 files changed, 1303 insertions, 0 deletions
diff --git a/src/burn/engine/search.cpp b/src/burn/engine/search.cpp
new file mode 100644
index 00000000..6d5f8d49
--- /dev/null
+++ b/src/burn/engine/search.cpp
@@ -0,0 +1,1303 @@
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// internal function declarations
7
8static HRESULT DirectorySearchExists(
9 __in BURN_SEARCH* pSearch,
10 __in BURN_VARIABLES* pVariables
11 );
12static HRESULT DirectorySearchPath(
13 __in BURN_SEARCH* pSearch,
14 __in BURN_VARIABLES* pVariables
15 );
16static HRESULT FileSearchExists(
17 __in BURN_SEARCH* pSearch,
18 __in BURN_VARIABLES* pVariables
19 );
20static HRESULT FileSearchVersion(
21 __in BURN_SEARCH* pSearch,
22 __in BURN_VARIABLES* pVariables
23 );
24static HRESULT FileSearchPath(
25 __in BURN_SEARCH* pSearch,
26 __in BURN_VARIABLES* pVariables
27 );
28static HRESULT RegistrySearchExists(
29 __in BURN_SEARCH* pSearch,
30 __in BURN_VARIABLES* pVariables
31 );
32static HRESULT RegistrySearchValue(
33 __in BURN_SEARCH* pSearch,
34 __in BURN_VARIABLES* pVariables
35 );
36static HRESULT MsiComponentSearch(
37 __in BURN_SEARCH* pSearch,
38 __in BURN_VARIABLES* pVariables
39 );
40static HRESULT MsiProductSearch(
41 __in BURN_SEARCH* pSearch,
42 __in BURN_VARIABLES* pVariables
43 );
44static HRESULT MsiFeatureSearch(
45 __in BURN_SEARCH* pSearch,
46 __in BURN_VARIABLES* pVariables
47 );
48static HRESULT PerformExtensionSearch(
49 __in BURN_SEARCH* pSearch
50 );
51static HRESULT PerformSetVariable(
52 __in BURN_SEARCH* pSearch,
53 __in BURN_VARIABLES* pVariables
54);
55
56
57// function definitions
58
59extern "C" HRESULT SearchesParseFromXml(
60 __in BURN_SEARCHES* pSearches,
61 __in BURN_EXTENSIONS* pBurnExtensions,
62 __in IXMLDOMNode* pixnBundle
63 )
64{
65 HRESULT hr = S_OK;
66 IXMLDOMNodeList* pixnNodes = NULL;
67 IXMLDOMNode* pixnNode = NULL;
68 DWORD cNodes = 0;
69 BSTR bstrNodeName = NULL;
70 LPWSTR scz = NULL;
71 BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE;
72
73 // select search nodes
74 hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch|SetVariable", &pixnNodes);
75 ExitOnFailure(hr, "Failed to select search nodes.");
76
77 // get search node count
78 hr = pixnNodes->get_length((long*)&cNodes);
79 ExitOnFailure(hr, "Failed to get search node count.");
80
81 if (!cNodes)
82 {
83 ExitFunction();
84 }
85
86 // allocate memory for searches
87 pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE);
88 ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs.");
89
90 pSearches->cSearches = cNodes;
91
92 // parse search elements
93 for (DWORD i = 0; i < cNodes; ++i)
94 {
95 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
96
97 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
98 ExitOnFailure(hr, "Failed to get next node.");
99
100 // @Id
101 hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey);
102 ExitOnFailure(hr, "Failed to get @Id.");
103
104 // @Variable
105 hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable);
106 ExitOnFailure(hr, "Failed to get @Variable.");
107
108 // @Condition
109 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition);
110 if (E_NOTFOUND != hr)
111 {
112 ExitOnFailure(hr, "Failed to get @Condition.");
113 }
114
115 // read type specific attributes
116 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1))
117 {
118 pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY;
119
120 // @Path
121 hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath);
122 ExitOnFailure(hr, "Failed to get @Path.");
123
124 // @Type
125 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
126 ExitOnFailure(hr, "Failed to get @Type.");
127
128 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
129 {
130 pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS;
131 }
132 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
133 {
134 pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH;
135 }
136 else
137 {
138 hr = E_INVALIDARG;
139 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
140 }
141 }
142 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1))
143 {
144 pSearch->Type = BURN_SEARCH_TYPE_FILE;
145
146 // @Path
147 hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath);
148 ExitOnFailure(hr, "Failed to get @Path.");
149
150 // @Type
151 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
152 ExitOnFailure(hr, "Failed to get @Type.");
153
154 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
155 {
156 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS;
157 }
158 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
159 {
160 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION;
161 }
162 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
163 {
164 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH;
165 }
166 else
167 {
168 hr = E_INVALIDARG;
169 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
170 }
171 }
172 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1))
173 {
174 pSearch->Type = BURN_SEARCH_TYPE_REGISTRY;
175
176 // @Root
177 hr = XmlGetAttributeEx(pixnNode, L"Root", &scz);
178 ExitOnFailure(hr, "Failed to get @Root.");
179
180 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1))
181 {
182 pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT;
183 }
184 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1))
185 {
186 pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER;
187 }
188 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1))
189 {
190 pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE;
191 }
192 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1))
193 {
194 pSearch->RegistrySearch.hRoot = HKEY_USERS;
195 }
196 else
197 {
198 hr = E_INVALIDARG;
199 ExitOnFailure(hr, "Invalid value for @Root: %ls", scz);
200 }
201
202 // @Key
203 hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey);
204 ExitOnFailure(hr, "Failed to get Key attribute.");
205
206 // @Value
207 hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue);
208 if (E_NOTFOUND != hr)
209 {
210 ExitOnFailure(hr, "Failed to get Value attribute.");
211 }
212
213 // @Type
214 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
215 ExitOnFailure(hr, "Failed to get @Type.");
216
217 hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64);
218 if (E_NOTFOUND != hr)
219 {
220 ExitOnFailure(hr, "Failed to get Win64 attribute.");
221 }
222
223 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
224 {
225 pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS;
226 }
227 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1))
228 {
229 pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE;
230
231 // @ExpandEnvironment
232 hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment);
233 if (E_NOTFOUND != hr)
234 {
235 ExitOnFailure(hr, "Failed to get @ExpandEnvironment.");
236 }
237
238 // @VariableType
239 hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz);
240 ExitOnFailure(hr, "Failed to get @VariableType.");
241
242 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
243 {
244 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_FORMATTED;
245 }
246 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
247 {
248 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC;
249 }
250 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
251 {
252 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING;
253 }
254 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
255 {
256 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION;
257 }
258 else
259 {
260 hr = E_INVALIDARG;
261 ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz);
262 }
263 }
264 else
265 {
266 hr = E_INVALIDARG;
267 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
268 }
269 }
270 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1))
271 {
272 pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT;
273
274 // @ProductCode
275 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode);
276 if (E_NOTFOUND != hr)
277 {
278 ExitOnFailure(hr, "Failed to get @ProductCode.");
279 }
280
281 // @ComponentId
282 hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId);
283 ExitOnFailure(hr, "Failed to get @ComponentId.");
284
285 // @Type
286 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
287 ExitOnFailure(hr, "Failed to get @Type.");
288
289 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1))
290 {
291 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH;
292 }
293 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
294 {
295 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE;
296 }
297 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1))
298 {
299 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY;
300 }
301 else
302 {
303 hr = E_INVALIDARG;
304 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
305 }
306 }
307 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1))
308 {
309 pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT;
310 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE;
311
312 // @ProductCode (if we don't find a product code then look for an upgrade code)
313 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid);
314 if (E_NOTFOUND != hr)
315 {
316 ExitOnFailure(hr, "Failed to get @ProductCode.");
317 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE;
318 }
319 else
320 {
321 // @UpgradeCode
322 hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid);
323 if (E_NOTFOUND != hr)
324 {
325 ExitOnFailure(hr, "Failed to get @UpgradeCode.");
326 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE;
327 }
328 }
329
330 // make sure we found either a product or upgrade code
331 if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType)
332 {
333 hr = E_NOTFOUND;
334 ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode.");
335 }
336
337 // @Type
338 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
339 ExitOnFailure(hr, "Failed to get @Type.");
340
341 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
342 {
343 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION;
344 }
345 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1))
346 {
347 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE;
348 }
349 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
350 {
351 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE;
352 }
353 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1))
354 {
355 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT;
356 }
357 else
358 {
359 hr = E_INVALIDARG;
360 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
361 }
362 }
363 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1))
364 {
365 pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE;
366
367 // @ProductCode
368 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode);
369 ExitOnFailure(hr, "Failed to get @ProductCode.");
370
371 // @FeatureId
372 hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId);
373 ExitOnFailure(hr, "Failed to get @FeatureId.");
374
375 // @Type
376 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
377 ExitOnFailure(hr, "Failed to get @Type.");
378
379 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
380 {
381 pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE;
382 }
383 else
384 {
385 hr = E_INVALIDARG;
386 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
387 }
388 }
389 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExtensionSearch", -1))
390 {
391 pSearch->Type = BURN_SEARCH_TYPE_EXTENSION;
392
393 // @ExtensionId
394 hr = XmlGetAttributeEx(pixnNode, L"ExtensionId", &scz);
395 ExitOnFailure(hr, "Failed to get @ExtensionId.");
396
397 hr = BurnExtensionFindById(pBurnExtensions, scz, &pSearch->ExtensionSearch.pExtension);
398 ExitOnFailure(hr, "Failed to find extension '%ls' for search '%ls'", scz, pSearch->sczKey);
399 }
400 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"SetVariable", -1))
401 {
402 pSearch->Type = BURN_SEARCH_TYPE_SET_VARIABLE;
403
404 // @Value
405 hr = XmlGetAttributeEx(pixnNode, L"Value", &scz);
406 if (E_NOTFOUND != hr)
407 {
408 ExitOnFailure(hr, "Failed to get @Value.");
409
410 hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0, FALSE);
411 ExitOnFailure(hr, "Failed to set variant value.");
412
413 // @Type
414 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
415 ExitOnFailure(hr, "Failed to get @Type.");
416
417 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
418 {
419 valueType = BURN_VARIANT_TYPE_FORMATTED;
420 }
421 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
422 {
423 valueType = BURN_VARIANT_TYPE_NUMERIC;
424 }
425 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
426 {
427 valueType = BURN_VARIANT_TYPE_STRING;
428 }
429 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
430 {
431 valueType = BURN_VARIANT_TYPE_VERSION;
432 }
433 else
434 {
435 hr = E_INVALIDARG;
436 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
437 }
438 }
439 else
440 {
441 valueType = BURN_VARIANT_TYPE_NONE;
442 }
443
444 // change value variant to correct type
445 hr = BVariantChangeType(&pSearch->SetVariable.value, valueType);
446 ExitOnFailure(hr, "Failed to change variant type.");
447 }
448 else
449 {
450 hr = E_UNEXPECTED;
451 ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName);
452 }
453
454 // prepare next iteration
455 ReleaseNullObject(pixnNode);
456 ReleaseNullBSTR(bstrNodeName);
457 }
458
459 hr = S_OK;
460
461LExit:
462 ReleaseObject(pixnNodes);
463 ReleaseObject(pixnNode);
464 ReleaseBSTR(bstrNodeName);
465 ReleaseStr(scz);
466 return hr;
467}
468
469extern "C" HRESULT SearchesExecute(
470 __in BURN_SEARCHES* pSearches,
471 __in BURN_VARIABLES* pVariables
472 )
473{
474 HRESULT hr = S_OK;
475 BOOL f = FALSE;
476
477 for (DWORD i = 0; i < pSearches->cSearches; ++i)
478 {
479 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
480
481 // evaluate condition
482 if (pSearch->sczCondition && *pSearch->sczCondition)
483 {
484 hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f);
485 if (E_INVALIDDATA == hr)
486 {
487 TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
488 hr = S_OK;
489 continue;
490 }
491 ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
492
493 if (!f)
494 {
495 continue; // condition evaluated to false, skip
496 }
497 }
498
499 switch (pSearch->Type)
500 {
501 case BURN_SEARCH_TYPE_DIRECTORY:
502 switch (pSearch->DirectorySearch.Type)
503 {
504 case BURN_DIRECTORY_SEARCH_TYPE_EXISTS:
505 hr = DirectorySearchExists(pSearch, pVariables);
506 break;
507 case BURN_DIRECTORY_SEARCH_TYPE_PATH:
508 hr = DirectorySearchPath(pSearch, pVariables);
509 break;
510 default:
511 hr = E_UNEXPECTED;
512 }
513 break;
514 case BURN_SEARCH_TYPE_FILE:
515 switch (pSearch->FileSearch.Type)
516 {
517 case BURN_FILE_SEARCH_TYPE_EXISTS:
518 hr = FileSearchExists(pSearch, pVariables);
519 break;
520 case BURN_FILE_SEARCH_TYPE_VERSION:
521 hr = FileSearchVersion(pSearch, pVariables);
522 break;
523 case BURN_FILE_SEARCH_TYPE_PATH:
524 hr = FileSearchPath(pSearch, pVariables);
525 break;
526 default:
527 hr = E_UNEXPECTED;
528 }
529 break;
530 case BURN_SEARCH_TYPE_REGISTRY:
531 switch (pSearch->RegistrySearch.Type)
532 {
533 case BURN_REGISTRY_SEARCH_TYPE_EXISTS:
534 hr = RegistrySearchExists(pSearch, pVariables);
535 break;
536 case BURN_REGISTRY_SEARCH_TYPE_VALUE:
537 hr = RegistrySearchValue(pSearch, pVariables);
538 break;
539 default:
540 hr = E_UNEXPECTED;
541 }
542 break;
543 case BURN_SEARCH_TYPE_MSI_COMPONENT:
544 hr = MsiComponentSearch(pSearch, pVariables);
545 break;
546 case BURN_SEARCH_TYPE_MSI_PRODUCT:
547 hr = MsiProductSearch(pSearch, pVariables);
548 break;
549 case BURN_SEARCH_TYPE_MSI_FEATURE:
550 hr = MsiFeatureSearch(pSearch, pVariables);
551 break;
552 case BURN_SEARCH_TYPE_EXTENSION:
553 hr = PerformExtensionSearch(pSearch);
554 break;
555 case BURN_SEARCH_TYPE_SET_VARIABLE:
556 hr = PerformSetVariable(pSearch, pVariables);
557 break;
558 default:
559 hr = E_UNEXPECTED;
560 }
561
562 if (FAILED(hr))
563 {
564 TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey);
565 continue;
566 }
567 }
568
569 hr = S_OK;
570
571LExit:
572 return hr;
573}
574
575extern "C" void SearchesUninitialize(
576 __in BURN_SEARCHES* pSearches
577 )
578{
579 if (pSearches->rgSearches)
580 {
581 for (DWORD i = 0; i < pSearches->cSearches; ++i)
582 {
583 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
584
585 ReleaseStr(pSearch->sczKey);
586 ReleaseStr(pSearch->sczVariable);
587 ReleaseStr(pSearch->sczCondition);
588
589 switch (pSearch->Type)
590 {
591 case BURN_SEARCH_TYPE_DIRECTORY:
592 ReleaseStr(pSearch->DirectorySearch.sczPath);
593 break;
594 case BURN_SEARCH_TYPE_FILE:
595 ReleaseStr(pSearch->FileSearch.sczPath);
596 break;
597 case BURN_SEARCH_TYPE_REGISTRY:
598 ReleaseStr(pSearch->RegistrySearch.sczKey);
599 ReleaseStr(pSearch->RegistrySearch.sczValue);
600 break;
601 case BURN_SEARCH_TYPE_MSI_COMPONENT:
602 ReleaseStr(pSearch->MsiComponentSearch.sczProductCode);
603 ReleaseStr(pSearch->MsiComponentSearch.sczComponentId);
604 break;
605 case BURN_SEARCH_TYPE_MSI_PRODUCT:
606 ReleaseStr(pSearch->MsiProductSearch.sczGuid);
607 break;
608 case BURN_SEARCH_TYPE_MSI_FEATURE:
609 ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode);
610 ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId);
611 break;
612 case BURN_SEARCH_TYPE_SET_VARIABLE:
613 BVariantUninitialize(&pSearch->SetVariable.value);
614 break;
615 }
616 }
617 MemFree(pSearches->rgSearches);
618 }
619}
620
621
622// internal function definitions
623
624static HRESULT DirectorySearchExists(
625 __in BURN_SEARCH* pSearch,
626 __in BURN_VARIABLES* pVariables
627 )
628{
629 HRESULT hr = S_OK;
630 LPWSTR sczPath = NULL;
631 BOOL fExists = FALSE;
632
633 // format path
634 hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
635 ExitOnFailure(hr, "Failed to format variable string.");
636
637 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
638 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
639 {
640 hr = HRESULT_FROM_WIN32(::GetLastError());
641 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
642 {
643 hr = S_OK; // didn't find file, fExists still is false.
644 }
645 }
646 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
647 {
648 fExists = TRUE;
649 }
650
651 // else must have found a file.
652 // What if there is a hidden variable in sczPath?
653 ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
654
655 // set variable
656 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
657 ExitOnFailure(hr, "Failed to set variable.");
658
659LExit:
660 StrSecureZeroFreeString(sczPath);
661
662 return hr;
663}
664
665static HRESULT DirectorySearchPath(
666 __in BURN_SEARCH* pSearch,
667 __in BURN_VARIABLES* pVariables
668 )
669{
670 HRESULT hr = S_OK;
671 LPWSTR sczPath = NULL;
672
673 // format path
674 hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
675 ExitOnFailure(hr, "Failed to format variable string.");
676
677 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
678 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
679 {
680 hr = HRESULT_FROM_WIN32(::GetLastError());
681 }
682 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
683 {
684 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
685 ExitOnFailure(hr, "Failed to set directory search path variable.");
686 }
687 else // must have found a file.
688 {
689 hr = E_PATHNOTFOUND;
690 }
691
692 // What if there is a hidden variable in sczPath?
693 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
694 {
695 LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr);
696 ExitFunction1(hr = S_OK);
697 }
698 ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
699
700LExit:
701 StrSecureZeroFreeString(sczPath);
702
703 return hr;
704}
705
706static HRESULT FileSearchExists(
707 __in BURN_SEARCH* pSearch,
708 __in BURN_VARIABLES* pVariables
709 )
710{
711 HRESULT hr = S_OK;
712 DWORD er = ERROR_SUCCESS;
713 LPWSTR sczPath = NULL;
714 BOOL fExists = FALSE;
715
716 // format path
717 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
718 ExitOnFailure(hr, "Failed to format variable string.");
719
720 // find file
721 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
722 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
723 {
724 er = ::GetLastError();
725 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er)
726 {
727 // What if there is a hidden variable in sczPath?
728 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
729 }
730 else
731 {
732 ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath);
733 }
734 }
735 else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
736 {
737 fExists = TRUE;
738 }
739
740 // set variable
741 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
742 ExitOnFailure(hr, "Failed to set variable.");
743
744LExit:
745 StrSecureZeroFreeString(sczPath);
746 return hr;
747}
748
749static HRESULT FileSearchVersion(
750 __in BURN_SEARCH* pSearch,
751 __in BURN_VARIABLES* pVariables
752 )
753{
754 HRESULT hr = S_OK;
755 ULARGE_INTEGER uliVersion = { };
756 LPWSTR sczPath = NULL;
757 VERUTIL_VERSION* pVersion = NULL;
758
759 // format path
760 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
761 ExitOnFailure(hr, "Failed to format path string.");
762
763 // get file version
764 hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart);
765 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
766 {
767 // What if there is a hidden variable in sczPath?
768 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
769 ExitFunction1(hr = S_OK);
770 }
771 ExitOnFailure(hr, "Failed to get file version.");
772
773 hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion);
774 ExitOnFailure(hr, "Failed to create version from file version.");
775
776 // set variable
777 hr = VariableSetVersion(pVariables, pSearch->sczVariable, pVersion, FALSE);
778 ExitOnFailure(hr, "Failed to set variable.");
779
780LExit:
781 StrSecureZeroFreeString(sczPath);
782 ReleaseVerutilVersion(pVersion);
783 return hr;
784}
785
786static HRESULT FileSearchPath(
787 __in BURN_SEARCH* pSearch,
788 __in BURN_VARIABLES* pVariables
789 )
790{
791 HRESULT hr = S_OK;
792 LPWSTR sczPath = NULL;
793
794 // format path
795 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
796 ExitOnFailure(hr, "Failed to format variable string.");
797
798 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
799 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
800 {
801 hr = HRESULT_FROM_WIN32(::GetLastError());
802 }
803 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory.
804 {
805 hr = E_FILENOTFOUND;
806 }
807 else // found our file.
808 {
809 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
810 ExitOnFailure(hr, "Failed to set variable to file search path.");
811 }
812
813 // What if there is a hidden variable in sczPath?
814 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
815 {
816 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
817 ExitFunction1(hr = S_OK);
818 }
819 ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath);
820
821LExit:
822 StrSecureZeroFreeString(sczPath);
823
824 return hr;
825}
826
827static HRESULT RegistrySearchExists(
828 __in BURN_SEARCH* pSearch,
829 __in BURN_VARIABLES* pVariables
830 )
831{
832 HRESULT hr = S_OK;
833 DWORD er = ERROR_SUCCESS;
834 LPWSTR sczKey = NULL;
835 LPWSTR sczValue = NULL;
836 HKEY hKey = NULL;
837 DWORD dwType = 0;
838 BOOL fExists = FALSE;
839 REGSAM samDesired = KEY_QUERY_VALUE;
840
841 if (pSearch->RegistrySearch.fWin64)
842 {
843 samDesired = samDesired | KEY_WOW64_64KEY;
844 }
845
846 // format key string
847 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
848 ExitOnFailure(hr, "Failed to format key string.");
849
850 // open key
851 hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
852 if (SUCCEEDED(hr))
853 {
854 fExists = TRUE;
855 }
856 else if (E_FILENOTFOUND == hr)
857 {
858 // What if there is a hidden variable in sczKey?
859 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
860 fExists = FALSE;
861 hr = S_OK;
862 }
863 else
864 {
865 // What if there is a hidden variable in sczKey?
866 ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey);
867 }
868
869 if (fExists && pSearch->RegistrySearch.sczValue)
870 {
871 // format value string
872 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
873 ExitOnFailure(hr, "Failed to format value string.");
874
875 // query value
876 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL);
877 switch (er)
878 {
879 case ERROR_SUCCESS:
880 fExists = TRUE;
881 break;
882 case ERROR_FILE_NOT_FOUND:
883 // What if there is a hidden variable in sczKey or sczValue?
884 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
885 fExists = FALSE;
886 break;
887 default:
888 ExitOnWin32Error(er, hr, "Failed to query registry key value.");
889 }
890 }
891
892 // set variable
893 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
894 ExitOnFailure(hr, "Failed to set variable.");
895
896LExit:
897 if (FAILED(hr))
898 {
899 // What if there is a hidden variable in sczKey?
900 LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
901 }
902
903 StrSecureZeroFreeString(sczKey);
904 StrSecureZeroFreeString(sczValue);
905 ReleaseRegKey(hKey);
906
907 return hr;
908}
909
910static HRESULT RegistrySearchValue(
911 __in BURN_SEARCH* pSearch,
912 __in BURN_VARIABLES* pVariables
913 )
914{
915 HRESULT hr = S_OK;
916 DWORD er = ERROR_SUCCESS;
917 LPWSTR sczKey = NULL;
918 LPWSTR sczValue = NULL;
919 HKEY hKey = NULL;
920 DWORD dwType = 0;
921 DWORD cbData = 0;
922 LPBYTE pData = NULL;
923 DWORD cch = 0;
924 BURN_VARIANT value = { };
925 REGSAM samDesired = KEY_QUERY_VALUE;
926
927 if (pSearch->RegistrySearch.fWin64)
928 {
929 samDesired = samDesired | KEY_WOW64_64KEY;
930 }
931
932 // format key string
933 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
934 ExitOnFailure(hr, "Failed to format key string.");
935
936 // format value string
937 if (pSearch->RegistrySearch.sczValue)
938 {
939 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
940 ExitOnFailure(hr, "Failed to format value string.");
941 }
942
943 // open key
944 hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
945 if (E_FILENOTFOUND == hr)
946 {
947 // What if there is a hidden variable in sczKey?
948 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
949
950 ExitFunction1(hr = S_OK);
951 }
952 ExitOnFailure(hr, "Failed to open registry key.");
953
954 // get value
955 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData);
956 if (ERROR_FILE_NOT_FOUND == er)
957 {
958 // What if there is a hidden variable in sczKey or sczValue?
959 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
960
961 ExitFunction1(hr = S_OK);
962 }
963 ExitOnWin32Error(er, hr, "Failed to query registry key value size.");
964
965 pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ
966 ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value.");
967
968 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData);
969 ExitOnWin32Error(er, hr, "Failed to query registry key value.");
970
971 switch (dwType)
972 {
973 case REG_DWORD:
974 if (sizeof(LONG) != cbData)
975 {
976 ExitFunction1(hr = E_UNEXPECTED);
977 }
978 hr = BVariantSetNumeric(&value, *((LONG*)pData));
979 break;
980 case REG_QWORD:
981 if (sizeof(LONGLONG) != cbData)
982 {
983 ExitFunction1(hr = E_UNEXPECTED);
984 }
985 hr = BVariantSetNumeric(&value, *((LONGLONG*)pData));
986 break;
987 case REG_EXPAND_SZ:
988 if (pSearch->RegistrySearch.fExpandEnvironment)
989 {
990 hr = StrAlloc(&value.sczValue, cbData);
991 ExitOnFailure(hr, "Failed to allocate string buffer.");
992 value.Type = BURN_VARIANT_TYPE_STRING;
993
994 cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData);
995 if (cch > cbData)
996 {
997 hr = StrAlloc(&value.sczValue, cch);
998 ExitOnFailure(hr, "Failed to allocate string buffer.");
999
1000 if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch))
1001 {
1002 ExitWithLastError(hr, "Failed to get expand environment string.");
1003 }
1004 }
1005 break;
1006 }
1007 __fallthrough;
1008 case REG_SZ:
1009 hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE);
1010 break;
1011 default:
1012 ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType);
1013 }
1014 ExitOnFailure(hr, "Failed to read registry value.");
1015
1016 // change value to requested type
1017 hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType);
1018 ExitOnFailure(hr, "Failed to change value type.");
1019
1020 // Set variable.
1021 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
1022 ExitOnFailure(hr, "Failed to set variable.");
1023
1024LExit:
1025 if (FAILED(hr))
1026 {
1027 // What if there is a hidden variable in sczKey?
1028 LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
1029 }
1030
1031 StrSecureZeroFreeString(sczKey);
1032 StrSecureZeroFreeString(sczValue);
1033 ReleaseRegKey(hKey);
1034 ReleaseMem(pData);
1035 BVariantUninitialize(&value);
1036
1037 return hr;
1038}
1039
1040static HRESULT MsiComponentSearch(
1041 __in BURN_SEARCH* pSearch,
1042 __in BURN_VARIABLES* pVariables
1043 )
1044{
1045 HRESULT hr = S_OK;
1046 INSTALLSTATE is = INSTALLSTATE_BROKEN;
1047 LPWSTR sczComponentId = NULL;
1048 LPWSTR sczProductCode = NULL;
1049 LPWSTR sczPath = NULL;
1050
1051 // format component id string
1052 hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL);
1053 ExitOnFailure(hr, "Failed to format component id string.");
1054
1055 if (pSearch->MsiComponentSearch.sczProductCode)
1056 {
1057 // format product code string
1058 hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL);
1059 ExitOnFailure(hr, "Failed to format product code string.");
1060 }
1061
1062 if (sczProductCode)
1063 {
1064 hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath);
1065 }
1066 else
1067 {
1068 hr = WiuLocateComponent(sczComponentId, &is, &sczPath);
1069 }
1070
1071 if (INSTALLSTATE_SOURCEABSENT == is)
1072 {
1073 is = INSTALLSTATE_SOURCE;
1074 }
1075 else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is)
1076 {
1077 is = INSTALLSTATE_ABSENT;
1078 }
1079 else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is)
1080 {
1081 hr = E_INVALIDARG;
1082 ExitOnFailure(hr, "Failed to get component path: %d", is);
1083 }
1084
1085 // set variable
1086 switch (pSearch->MsiComponentSearch.Type)
1087 {
1088 case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH:
1089 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1090 {
1091 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
1092 }
1093 break;
1094 case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE:
1095 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE);
1096 break;
1097 case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY:
1098 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1099 {
1100 // remove file part from path, if any
1101 LPWSTR wz = wcsrchr(sczPath, L'\\');
1102 if (wz)
1103 {
1104 wz[1] = L'\0';
1105 }
1106
1107 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
1108 }
1109 break;
1110 }
1111 ExitOnFailure(hr, "Failed to set variable.");
1112
1113LExit:
1114 if (FAILED(hr))
1115 {
1116 LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1117 }
1118
1119 StrSecureZeroFreeString(sczComponentId);
1120 StrSecureZeroFreeString(sczProductCode);
1121 ReleaseStr(sczPath);
1122 return hr;
1123}
1124
1125static HRESULT MsiProductSearch(
1126 __in BURN_SEARCH* pSearch,
1127 __in BURN_VARIABLES* pVariables
1128 )
1129{
1130 HRESULT hr = S_OK;
1131 LPWSTR sczGuid = NULL;
1132 LPCWSTR wzProperty = NULL;
1133 LPWSTR *rgsczRelatedProductCodes = NULL;
1134 DWORD dwRelatedProducts = 0;
1135 BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE;
1136 BURN_VARIANT value = { };
1137
1138 switch (pSearch->MsiProductSearch.Type)
1139 {
1140 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1141 wzProperty = INSTALLPROPERTY_VERSIONSTRING;
1142 break;
1143 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1144 wzProperty = INSTALLPROPERTY_LANGUAGE;
1145 break;
1146 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1147 wzProperty = INSTALLPROPERTY_PRODUCTSTATE;
1148 break;
1149 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1150 wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE;
1151 break;
1152 default:
1153 ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type);
1154 }
1155
1156 // format guid string
1157 hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL);
1158 ExitOnFailure(hr, "Failed to format GUID string.");
1159
1160 // get product info
1161 value.Type = BURN_VARIANT_TYPE_STRING;
1162
1163 // if this is an upgrade code then get the product code of the highest versioned related product
1164 if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType)
1165 {
1166 // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there?
1167 hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE);
1168 ExitOnFailure(hr, "Failed to enumerate related products for upgrade code.");
1169
1170 // if we actually found a related product then use its upgrade code for the rest of the search
1171 if (1 == dwRelatedProducts)
1172 {
1173 hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0);
1174 ExitOnFailure(hr, "Failed to copy upgrade code.");
1175 }
1176 else
1177 {
1178 // set this here so we have a way of knowing that we don't need to bother
1179 // querying for the product information below
1180 hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT);
1181 }
1182 }
1183
1184 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr)
1185 {
1186 hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue);
1187 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr)
1188 {
1189 // product state is available only through MsiGetProductInfoEx
1190 // What if there is a hidden variable in sczGuid?
1191 LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1192 hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue);
1193
1194 // if not in per-machine context, try per-user (unmanaged)
1195 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1196 {
1197 // What if there is a hidden variable in sczGuid?
1198 LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1199 hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue);
1200 }
1201 }
1202 }
1203
1204 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1205 {
1206 // What if there is a hidden variable in sczGuid?
1207 LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid);
1208
1209 // set value to indicate absent
1210 switch (pSearch->MsiProductSearch.Type)
1211 {
1212 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough;
1213 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1214 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1215 value.llValue = 0;
1216 break;
1217 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1218 // is supposed to remain empty
1219 break;
1220 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1221 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1222 value.llValue = INSTALLSTATE_ABSENT;
1223 break;
1224 }
1225
1226 hr = S_OK;
1227 }
1228 ExitOnFailure(hr, "Failed to get product info.");
1229
1230 // change value type
1231 switch (pSearch->MsiProductSearch.Type)
1232 {
1233 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1234 type = BURN_VARIANT_TYPE_VERSION;
1235 break;
1236 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1237 type = BURN_VARIANT_TYPE_STRING;
1238 break;
1239 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough;
1240 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1241 type = BURN_VARIANT_TYPE_NUMERIC;
1242 break;
1243 }
1244 hr = BVariantChangeType(&value, type);
1245 ExitOnFailure(hr, "Failed to change value type.");
1246
1247 // Set variable.
1248 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
1249 ExitOnFailure(hr, "Failed to set variable.");
1250
1251LExit:
1252 if (FAILED(hr))
1253 {
1254 LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1255 }
1256
1257 StrSecureZeroFreeString(sczGuid);
1258 ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts);
1259 BVariantUninitialize(&value);
1260
1261 return hr;
1262}
1263
1264static HRESULT MsiFeatureSearch(
1265 __in BURN_SEARCH* pSearch,
1266 __in BURN_VARIABLES* /*pVariables*/
1267 )
1268{
1269 HRESULT hr = E_NOTIMPL;
1270
1271//LExit:
1272 if (FAILED(hr))
1273 {
1274 LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1275 }
1276
1277 return hr;
1278}
1279
1280static HRESULT PerformExtensionSearch(
1281 __in BURN_SEARCH* pSearch
1282 )
1283{
1284 HRESULT hr = S_OK;
1285
1286 hr = BurnExtensionPerformSearch(pSearch->ExtensionSearch.pExtension, pSearch->sczKey, pSearch->sczVariable);
1287
1288 return hr;
1289}
1290
1291static HRESULT PerformSetVariable(
1292 __in BURN_SEARCH* pSearch,
1293 __in BURN_VARIABLES* pVariables
1294 )
1295{
1296 HRESULT hr = S_OK;
1297
1298 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &pSearch->SetVariable.value);
1299 ExitOnFailure(hr, "Failed to set variable: %ls", pSearch->sczVariable);
1300
1301LExit:
1302 return hr;
1303}