From af10c45d7b3a44af0b461a557847fe03263dcc10 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 17:06:54 -0700 Subject: Move burn into burn --- src/burn/engine/search.cpp | 1303 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1303 insertions(+) create mode 100644 src/burn/engine/search.cpp (limited to 'src/burn/engine/search.cpp') 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 @@ +// 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. + +#include "precomp.h" + + +// internal function declarations + +static HRESULT DirectorySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT DirectorySearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchVersion( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT FileSearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RegistrySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT RegistrySearchValue( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiComponentSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiProductSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT MsiFeatureSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ); +static HRESULT PerformExtensionSearch( + __in BURN_SEARCH* pSearch + ); +static HRESULT PerformSetVariable( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables +); + + +// function definitions + +extern "C" HRESULT SearchesParseFromXml( + __in BURN_SEARCHES* pSearches, + __in BURN_EXTENSIONS* pBurnExtensions, + __in IXMLDOMNode* pixnBundle + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeName = NULL; + LPWSTR scz = NULL; + BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; + + // select search nodes + hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch|ExtensionSearch|SetVariable", &pixnNodes); + ExitOnFailure(hr, "Failed to select search nodes."); + + // get search node count + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get search node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // allocate memory for searches + pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE); + ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs."); + + pSearches->cSearches = cNodes; + + // parse search elements + for (DWORD i = 0; i < cNodes; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey); + ExitOnFailure(hr, "Failed to get @Id."); + + // @Variable + hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable); + ExitOnFailure(hr, "Failed to get @Variable."); + + // @Condition + hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Condition."); + } + + // read type specific attributes + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY; + + // @Path + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) + { + pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_FILE; + + // @Path + hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath); + ExitOnFailure(hr, "Failed to get @Path."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) + { + pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_REGISTRY; + + // @Root + hr = XmlGetAttributeEx(pixnNode, L"Root", &scz); + ExitOnFailure(hr, "Failed to get @Root."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1)) + { + pSearch->RegistrySearch.hRoot = HKEY_USERS; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Root: %ls", scz); + } + + // @Key + hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey); + ExitOnFailure(hr, "Failed to get Key attribute."); + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Value attribute."); + } + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get Win64 attribute."); + } + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) + { + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1)) + { + pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE; + + // @ExpandEnvironment + hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ExpandEnvironment."); + } + + // @VariableType + hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz); + ExitOnFailure(hr, "Failed to get @VariableType."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz); + } + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT; + + // @ProductCode + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductCode."); + } + + // @ComponentId + hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId); + ExitOnFailure(hr, "Failed to get @ComponentId."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1)) + { + pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT; + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE; + + // @ProductCode (if we don't find a product code then look for an upgrade code) + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @ProductCode."); + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE; + } + else + { + // @UpgradeCode + hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @UpgradeCode."); + pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE; + } + } + + // make sure we found either a product or upgrade code + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType) + { + hr = E_NOTFOUND; + ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode."); + } + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1)) + { + pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE; + + // @ProductCode + hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode); + ExitOnFailure(hr, "Failed to get @ProductCode."); + + // @FeatureId + hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId); + ExitOnFailure(hr, "Failed to get @FeatureId."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) + { + pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExtensionSearch", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_EXTENSION; + + // @ExtensionId + hr = XmlGetAttributeEx(pixnNode, L"ExtensionId", &scz); + ExitOnFailure(hr, "Failed to get @ExtensionId."); + + hr = BurnExtensionFindById(pBurnExtensions, scz, &pSearch->ExtensionSearch.pExtension); + ExitOnFailure(hr, "Failed to find extension '%ls' for search '%ls'", scz, pSearch->sczKey); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"SetVariable", -1)) + { + pSearch->Type = BURN_SEARCH_TYPE_SET_VARIABLE; + + // @Value + hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @Value."); + + hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0, FALSE); + ExitOnFailure(hr, "Failed to set variant value."); + + // @Type + hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); + ExitOnFailure(hr, "Failed to get @Type."); + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) + { + valueType = BURN_VARIANT_TYPE_FORMATTED; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) + { + valueType = BURN_VARIANT_TYPE_NUMERIC; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) + { + valueType = BURN_VARIANT_TYPE_STRING; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) + { + valueType = BURN_VARIANT_TYPE_VERSION; + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); + } + } + else + { + valueType = BURN_VARIANT_TYPE_NONE; + } + + // change value variant to correct type + hr = BVariantChangeType(&pSearch->SetVariable.value, valueType); + ExitOnFailure(hr, "Failed to change variant type."); + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + + hr = S_OK; + +LExit: + ReleaseObject(pixnNodes); + ReleaseObject(pixnNode); + ReleaseBSTR(bstrNodeName); + ReleaseStr(scz); + return hr; +} + +extern "C" HRESULT SearchesExecute( + __in BURN_SEARCHES* pSearches, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + BOOL f = FALSE; + + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + // evaluate condition + if (pSearch->sczCondition && *pSearch->sczCondition) + { + hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f); + if (E_INVALIDDATA == hr) + { + TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); + hr = S_OK; + continue; + } + ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); + + if (!f) + { + continue; // condition evaluated to false, skip + } + } + + switch (pSearch->Type) + { + case BURN_SEARCH_TYPE_DIRECTORY: + switch (pSearch->DirectorySearch.Type) + { + case BURN_DIRECTORY_SEARCH_TYPE_EXISTS: + hr = DirectorySearchExists(pSearch, pVariables); + break; + case BURN_DIRECTORY_SEARCH_TYPE_PATH: + hr = DirectorySearchPath(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_FILE: + switch (pSearch->FileSearch.Type) + { + case BURN_FILE_SEARCH_TYPE_EXISTS: + hr = FileSearchExists(pSearch, pVariables); + break; + case BURN_FILE_SEARCH_TYPE_VERSION: + hr = FileSearchVersion(pSearch, pVariables); + break; + case BURN_FILE_SEARCH_TYPE_PATH: + hr = FileSearchPath(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_REGISTRY: + switch (pSearch->RegistrySearch.Type) + { + case BURN_REGISTRY_SEARCH_TYPE_EXISTS: + hr = RegistrySearchExists(pSearch, pVariables); + break; + case BURN_REGISTRY_SEARCH_TYPE_VALUE: + hr = RegistrySearchValue(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + break; + case BURN_SEARCH_TYPE_MSI_COMPONENT: + hr = MsiComponentSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_MSI_PRODUCT: + hr = MsiProductSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_MSI_FEATURE: + hr = MsiFeatureSearch(pSearch, pVariables); + break; + case BURN_SEARCH_TYPE_EXTENSION: + hr = PerformExtensionSearch(pSearch); + break; + case BURN_SEARCH_TYPE_SET_VARIABLE: + hr = PerformSetVariable(pSearch, pVariables); + break; + default: + hr = E_UNEXPECTED; + } + + if (FAILED(hr)) + { + TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey); + continue; + } + } + + hr = S_OK; + +LExit: + return hr; +} + +extern "C" void SearchesUninitialize( + __in BURN_SEARCHES* pSearches + ) +{ + if (pSearches->rgSearches) + { + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; + + ReleaseStr(pSearch->sczKey); + ReleaseStr(pSearch->sczVariable); + ReleaseStr(pSearch->sczCondition); + + switch (pSearch->Type) + { + case BURN_SEARCH_TYPE_DIRECTORY: + ReleaseStr(pSearch->DirectorySearch.sczPath); + break; + case BURN_SEARCH_TYPE_FILE: + ReleaseStr(pSearch->FileSearch.sczPath); + break; + case BURN_SEARCH_TYPE_REGISTRY: + ReleaseStr(pSearch->RegistrySearch.sczKey); + ReleaseStr(pSearch->RegistrySearch.sczValue); + break; + case BURN_SEARCH_TYPE_MSI_COMPONENT: + ReleaseStr(pSearch->MsiComponentSearch.sczProductCode); + ReleaseStr(pSearch->MsiComponentSearch.sczComponentId); + break; + case BURN_SEARCH_TYPE_MSI_PRODUCT: + ReleaseStr(pSearch->MsiProductSearch.sczGuid); + break; + case BURN_SEARCH_TYPE_MSI_FEATURE: + ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode); + ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId); + break; + case BURN_SEARCH_TYPE_SET_VARIABLE: + BVariantUninitialize(&pSearch->SetVariable.value); + break; + } + } + MemFree(pSearches->rgSearches); + } +} + + +// internal function definitions + +static HRESULT DirectorySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + BOOL fExists = FALSE; + + // format path + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; // didn't find file, fExists still is false. + } + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + fExists = TRUE; + } + + // else must have found a file. + // What if there is a hidden variable in sczPath? + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT DirectorySearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set directory search path variable."); + } + else // must have found a file. + { + hr = E_PATHNOTFOUND; + } + + // What if there is a hidden variable in sczPath? + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT FileSearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczPath = NULL; + BOOL fExists = FALSE; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + // find file + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + er = ::GetLastError(); + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) + { + // What if there is a hidden variable in sczPath? + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + } + else + { + ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath); + } + } + else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + fExists = TRUE; + } + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + return hr; +} + +static HRESULT FileSearchVersion( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + ULARGE_INTEGER uliVersion = { }; + LPWSTR sczPath = NULL; + VERUTIL_VERSION* pVersion = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format path string."); + + // get file version + hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart); + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + // What if there is a hidden variable in sczPath? + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to get file version."); + + hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); + ExitOnFailure(hr, "Failed to create version from file version."); + + // set variable + hr = VariableSetVersion(pVariables, pSearch->sczVariable, pVersion, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + StrSecureZeroFreeString(sczPath); + ReleaseVerutilVersion(pVersion); + return hr; +} + +static HRESULT FileSearchPath( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + // format path + hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); + ExitOnFailure(hr, "Failed to format variable string."); + + DWORD dwAttributes = ::GetFileAttributesW(sczPath); + if (INVALID_FILE_ATTRIBUTES == dwAttributes) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + } + else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory. + { + hr = E_FILENOTFOUND; + } + else // found our file. + { + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + ExitOnFailure(hr, "Failed to set variable to file search path."); + } + + // What if there is a hidden variable in sczPath? + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath); + +LExit: + StrSecureZeroFreeString(sczPath); + + return hr; +} + +static HRESULT RegistrySearchExists( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczKey = NULL; + LPWSTR sczValue = NULL; + HKEY hKey = NULL; + DWORD dwType = 0; + BOOL fExists = FALSE; + REGSAM samDesired = KEY_QUERY_VALUE; + + if (pSearch->RegistrySearch.fWin64) + { + samDesired = samDesired | KEY_WOW64_64KEY; + } + + // format key string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); + ExitOnFailure(hr, "Failed to format key string."); + + // open key + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); + if (SUCCEEDED(hr)) + { + fExists = TRUE; + } + else if (E_FILENOTFOUND == hr) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); + fExists = FALSE; + hr = S_OK; + } + else + { + // What if there is a hidden variable in sczKey? + ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey); + } + + if (fExists && pSearch->RegistrySearch.sczValue) + { + // format value string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format value string."); + + // query value + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL); + switch (er) + { + case ERROR_SUCCESS: + fExists = TRUE; + break; + case ERROR_FILE_NOT_FOUND: + // What if there is a hidden variable in sczKey or sczValue? + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); + fExists = FALSE; + break; + default: + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + } + } + + // set variable + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr); + } + + StrSecureZeroFreeString(sczKey); + StrSecureZeroFreeString(sczValue); + ReleaseRegKey(hKey); + + return hr; +} + +static HRESULT RegistrySearchValue( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + LPWSTR sczKey = NULL; + LPWSTR sczValue = NULL; + HKEY hKey = NULL; + DWORD dwType = 0; + DWORD cbData = 0; + LPBYTE pData = NULL; + DWORD cch = 0; + BURN_VARIANT value = { }; + REGSAM samDesired = KEY_QUERY_VALUE; + + if (pSearch->RegistrySearch.fWin64) + { + samDesired = samDesired | KEY_WOW64_64KEY; + } + + // format key string + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); + ExitOnFailure(hr, "Failed to format key string."); + + // format value string + if (pSearch->RegistrySearch.sczValue) + { + hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); + ExitOnFailure(hr, "Failed to format value string."); + } + + // open key + hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); + if (E_FILENOTFOUND == hr) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); + + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to open registry key."); + + // get value + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData); + if (ERROR_FILE_NOT_FOUND == er) + { + // What if there is a hidden variable in sczKey or sczValue? + LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); + + ExitFunction1(hr = S_OK); + } + ExitOnWin32Error(er, hr, "Failed to query registry key value size."); + + pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ + ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value."); + + er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData); + ExitOnWin32Error(er, hr, "Failed to query registry key value."); + + switch (dwType) + { + case REG_DWORD: + if (sizeof(LONG) != cbData) + { + ExitFunction1(hr = E_UNEXPECTED); + } + hr = BVariantSetNumeric(&value, *((LONG*)pData)); + break; + case REG_QWORD: + if (sizeof(LONGLONG) != cbData) + { + ExitFunction1(hr = E_UNEXPECTED); + } + hr = BVariantSetNumeric(&value, *((LONGLONG*)pData)); + break; + case REG_EXPAND_SZ: + if (pSearch->RegistrySearch.fExpandEnvironment) + { + hr = StrAlloc(&value.sczValue, cbData); + ExitOnFailure(hr, "Failed to allocate string buffer."); + value.Type = BURN_VARIANT_TYPE_STRING; + + cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData); + if (cch > cbData) + { + hr = StrAlloc(&value.sczValue, cch); + ExitOnFailure(hr, "Failed to allocate string buffer."); + + if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch)) + { + ExitWithLastError(hr, "Failed to get expand environment string."); + } + } + break; + } + __fallthrough; + case REG_SZ: + hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE); + break; + default: + ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); + } + ExitOnFailure(hr, "Failed to read registry value."); + + // change value to requested type + hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType); + ExitOnFailure(hr, "Failed to change value type."); + + // Set variable. + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + // What if there is a hidden variable in sczKey? + LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr); + } + + StrSecureZeroFreeString(sczKey); + StrSecureZeroFreeString(sczValue); + ReleaseRegKey(hKey); + ReleaseMem(pData); + BVariantUninitialize(&value); + + return hr; +} + +static HRESULT MsiComponentSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + INSTALLSTATE is = INSTALLSTATE_BROKEN; + LPWSTR sczComponentId = NULL; + LPWSTR sczProductCode = NULL; + LPWSTR sczPath = NULL; + + // format component id string + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL); + ExitOnFailure(hr, "Failed to format component id string."); + + if (pSearch->MsiComponentSearch.sczProductCode) + { + // format product code string + hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL); + ExitOnFailure(hr, "Failed to format product code string."); + } + + if (sczProductCode) + { + hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath); + } + else + { + hr = WiuLocateComponent(sczComponentId, &is, &sczPath); + } + + if (INSTALLSTATE_SOURCEABSENT == is) + { + is = INSTALLSTATE_SOURCE; + } + else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is) + { + is = INSTALLSTATE_ABSENT; + } + else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to get component path: %d", is); + } + + // set variable + switch (pSearch->MsiComponentSearch.Type) + { + case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH: + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) + { + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + } + break; + case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE: + hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE); + break; + case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY: + if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) + { + // remove file part from path, if any + LPWSTR wz = wcsrchr(sczPath, L'\\'); + if (wz) + { + wz[1] = L'\0'; + } + + hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE); + } + break; + } + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + StrSecureZeroFreeString(sczComponentId); + StrSecureZeroFreeString(sczProductCode); + ReleaseStr(sczPath); + return hr; +} + +static HRESULT MsiProductSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + LPWSTR sczGuid = NULL; + LPCWSTR wzProperty = NULL; + LPWSTR *rgsczRelatedProductCodes = NULL; + DWORD dwRelatedProducts = 0; + BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE; + BURN_VARIANT value = { }; + + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + wzProperty = INSTALLPROPERTY_VERSIONSTRING; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + wzProperty = INSTALLPROPERTY_LANGUAGE; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: + wzProperty = INSTALLPROPERTY_PRODUCTSTATE; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: + wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE; + break; + default: + ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type); + } + + // format guid string + hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL); + ExitOnFailure(hr, "Failed to format GUID string."); + + // get product info + value.Type = BURN_VARIANT_TYPE_STRING; + + // if this is an upgrade code then get the product code of the highest versioned related product + if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType) + { + // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there? + hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE); + ExitOnFailure(hr, "Failed to enumerate related products for upgrade code."); + + // if we actually found a related product then use its upgrade code for the rest of the search + if (1 == dwRelatedProducts) + { + hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0); + ExitOnFailure(hr, "Failed to copy upgrade code."); + } + else + { + // set this here so we have a way of knowing that we don't need to bother + // querying for the product information below + hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT); + } + } + + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr) + { + hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue); + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) + { + // product state is available only through MsiGetProductInfoEx + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid); + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue); + + // if not in per-machine context, try per-user (unmanaged) + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid); + hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue); + } + } + } + + if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) + { + // What if there is a hidden variable in sczGuid? + LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid); + + // set value to indicate absent + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough; + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + value.Type = BURN_VARIANT_TYPE_NUMERIC; + value.llValue = 0; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + // is supposed to remain empty + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: + value.Type = BURN_VARIANT_TYPE_NUMERIC; + value.llValue = INSTALLSTATE_ABSENT; + break; + } + + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get product info."); + + // change value type + switch (pSearch->MsiProductSearch.Type) + { + case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: + type = BURN_VARIANT_TYPE_VERSION; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: + type = BURN_VARIANT_TYPE_STRING; + break; + case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough; + case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: + type = BURN_VARIANT_TYPE_NUMERIC; + break; + } + hr = BVariantChangeType(&value, type); + ExitOnFailure(hr, "Failed to change value type."); + + // Set variable. + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value); + ExitOnFailure(hr, "Failed to set variable."); + +LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + StrSecureZeroFreeString(sczGuid); + ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts); + BVariantUninitialize(&value); + + return hr; +} + +static HRESULT MsiFeatureSearch( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* /*pVariables*/ + ) +{ + HRESULT hr = E_NOTIMPL; + +//LExit: + if (FAILED(hr)) + { + LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); + } + + return hr; +} + +static HRESULT PerformExtensionSearch( + __in BURN_SEARCH* pSearch + ) +{ + HRESULT hr = S_OK; + + hr = BurnExtensionPerformSearch(pSearch->ExtensionSearch.pExtension, pSearch->sczKey, pSearch->sczVariable); + + return hr; +} + +static HRESULT PerformSetVariable( + __in BURN_SEARCH* pSearch, + __in BURN_VARIABLES* pVariables + ) +{ + HRESULT hr = S_OK; + + hr = VariableSetVariant(pVariables, pSearch->sczVariable, &pSearch->SetVariable.value); + ExitOnFailure(hr, "Failed to set variable: %ls", pSearch->sczVariable); + +LExit: + return hr; +} -- cgit v1.2.3-55-g6feb