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