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