aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2020-07-31 17:06:35 -0600
committerSean Hall <r.sean.hall@gmail.com>2020-08-09 10:33:49 -0600
commitd21eed76c48960707561c45c492c10a6a23c052e (patch)
treeebab09d099a699a24801fcfb4d7152cf82cc4a06
parentad80d6dc10f9bd6cb6a084857a8fb546cc55e756 (diff)
downloadwix-d21eed76c48960707561c45c492c10a6a23c052e.tar.gz
wix-d21eed76c48960707561c45c492c10a6a23c052e.tar.bz2
wix-d21eed76c48960707561c45c492c10a6a23c052e.zip
WIXFEAT:4763 Change "string" variable type to literal and add "formatted".
-rw-r--r--src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h1
-rw-r--r--src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h14
-rw-r--r--src/engine/EngineForApplication.cpp2
-rw-r--r--src/engine/EngineForExtension.cpp30
-rw-r--r--src/engine/apply.cpp4
-rw-r--r--src/engine/cache.cpp6
-rw-r--r--src/engine/condition.cpp11
-rw-r--r--src/engine/core.cpp8
-rw-r--r--src/engine/engine.cpp2
-rw-r--r--src/engine/exeengine.cpp6
-rw-r--r--src/engine/logging.cpp4
-rw-r--r--src/engine/msiengine.cpp6
-rw-r--r--src/engine/mspengine.cpp6
-rw-r--r--src/engine/msuengine.cpp4
-rw-r--r--src/engine/registration.cpp10
-rw-r--r--src/engine/search.cpp36
-rw-r--r--src/engine/variable.cpp102
-rw-r--r--src/engine/variable.h17
-rw-r--r--src/engine/variant.cpp34
-rw-r--r--src/engine/variant.h6
-rw-r--r--src/test/BurnUnitTest/SearchTest.cpp87
-rw-r--r--src/test/BurnUnitTest/VariableHelpers.cpp4
-rw-r--r--src/test/BurnUnitTest/VariableHelpers.h2
-rw-r--r--src/test/BurnUnitTest/VariableTest.cpp69
-rw-r--r--src/test/BurnUnitTest/VariantTest.cpp56
25 files changed, 313 insertions, 214 deletions
diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h
index 0dcaba75..e3792177 100644
--- a/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h
+++ b/src/WixToolset.BootstrapperCore.Native/inc/BootstrapperEngine.h
@@ -398,6 +398,7 @@ typedef struct _BAENGINE_SETVARIABLESTRING_ARGS
398 DWORD cbSize; 398 DWORD cbSize;
399 LPCWSTR wzVariable; 399 LPCWSTR wzVariable;
400 LPCWSTR wzValue; 400 LPCWSTR wzValue;
401 BOOL fFormatted;
401} BAENGINE_SETVARIABLESTRING_ARGS; 402} BAENGINE_SETVARIABLESTRING_ARGS;
402 403
403typedef struct _BAENGINE_SETVARIABLESTRING_RESULTS 404typedef struct _BAENGINE_SETVARIABLESTRING_RESULTS
diff --git a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h
index 03c4f206..61a55693 100644
--- a/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h
+++ b/src/WixToolset.BootstrapperCore.Native/inc/BundleExtensionEngine.h
@@ -24,7 +24,6 @@ enum BUNDLE_EXTENSION_ENGINE_MESSAGE
24 BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING, 24 BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLESTRING,
25 BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION, 25 BUNDLE_EXTENSION_ENGINE_MESSAGE_GETVARIABLEVERSION,
26 BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG, 26 BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG,
27 BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLELITERALSTRING,
28 BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC, 27 BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC,
29 BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING, 28 BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLESTRING,
30 BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION, 29 BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLEVERSION,
@@ -124,18 +123,6 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_LOG_RESULTS
124 DWORD cbSize; 123 DWORD cbSize;
125} BUNDLE_EXTENSION_ENGINE_LOG_RESULTS; 124} BUNDLE_EXTENSION_ENGINE_LOG_RESULTS;
126 125
127typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS
128{
129 DWORD cbSize;
130 LPCWSTR wzVariable;
131 LPCWSTR wzValue;
132} BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS;
133
134typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS
135{
136 DWORD cbSize;
137} BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS;
138
139typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS 126typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS
140{ 127{
141 DWORD cbSize; 128 DWORD cbSize;
@@ -153,6 +140,7 @@ typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS
153 DWORD cbSize; 140 DWORD cbSize;
154 LPCWSTR wzVariable; 141 LPCWSTR wzVariable;
155 LPCWSTR wzValue; 142 LPCWSTR wzValue;
143 BOOL fFormatted;
156} BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS; 144} BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_ARGS;
157 145
158typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS 146typedef struct _BUNDLE_EXTENSION_ENGINE_SETVARIABLESTRING_RESULTS
diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp
index e559b438..81eec2fc 100644
--- a/src/engine/EngineForApplication.cpp
+++ b/src/engine/EngineForApplication.cpp
@@ -592,7 +592,7 @@ static HRESULT BAEngineSetVariableString(
592 592
593 if (wzVariable && *wzVariable) 593 if (wzVariable && *wzVariable)
594 { 594 {
595 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); 595 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE, pArgs->fFormatted);
596 ExitOnFailure(hr, "Failed to set string variable."); 596 ExitOnFailure(hr, "Failed to set string variable.");
597 } 597 }
598 else 598 else
diff --git a/src/engine/EngineForExtension.cpp b/src/engine/EngineForExtension.cpp
index 9667dd18..fdfa59b1 100644
--- a/src/engine/EngineForExtension.cpp
+++ b/src/engine/EngineForExtension.cpp
@@ -245,31 +245,6 @@ LExit:
245 return hr; 245 return hr;
246} 246}
247 247
248static HRESULT BEEngineSetVariableLiteralString(
249 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
250 __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS* pArgs,
251 __in BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS* /*pResults*/
252 )
253{
254 HRESULT hr = S_OK;
255 LPCWSTR wzVariable = pArgs->wzVariable;
256 LPCWSTR wzValue = pArgs->wzValue;
257
258 if (wzVariable && *wzVariable)
259 {
260 hr = VariableSetLiteralString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE);
261 ExitOnFailure(hr, "Failed to set literal string variable.");
262 }
263 else
264 {
265 hr = E_INVALIDARG;
266 ExitOnFailure(hr, "Bundle Extension did not provide variable name.");
267 }
268
269LExit:
270 return hr;
271}
272
273static HRESULT BEEngineSetVariableNumeric( 248static HRESULT BEEngineSetVariableNumeric(
274 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext, 249 __in BURN_EXTENSION_ENGINE_CONTEXT* pContext,
275 __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS* pArgs, 250 __in const BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS* pArgs,
@@ -307,7 +282,7 @@ static HRESULT BEEngineSetVariableString(
307 282
308 if (wzVariable && *wzVariable) 283 if (wzVariable && *wzVariable)
309 { 284 {
310 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); 285 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE, pArgs->fFormatted);
311 ExitOnFailure(hr, "Failed to set string variable."); 286 ExitOnFailure(hr, "Failed to set string variable.");
312 } 287 }
313 else 288 else
@@ -383,9 +358,6 @@ HRESULT WINAPI EngineForExtensionProc(
383 case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG: 358 case BUNDLE_EXTENSION_ENGINE_MESSAGE_LOG:
384 hr = BEEngineLog(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_LOG_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_LOG_RESULTS*>(pvResults)); 359 hr = BEEngineLog(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_LOG_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_LOG_RESULTS*>(pvResults));
385 break; 360 break;
386 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLELITERALSTRING:
387 hr = BEEngineSetVariableLiteralString(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLELITERALSTRING_RESULTS*>(pvResults));
388 break;
389 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC: 361 case BUNDLE_EXTENSION_ENGINE_MESSAGE_SETVARIABLENUMERIC:
390 hr = BEEngineSetVariableNumeric(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS*>(pvResults)); 362 hr = BEEngineSetVariableNumeric(pContext, reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BUNDLE_EXTENSION_ENGINE_SETVARIABLENUMERIC_RESULTS*>(pvResults));
391 break; 363 break;
diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp
index eae7c681..9a0f64e1 100644
--- a/src/engine/apply.cpp
+++ b/src/engine/apply.cpp
@@ -272,7 +272,7 @@ extern "C" HRESULT ApplySetVariables(
272{ 272{
273 HRESULT hr = S_OK; 273 HRESULT hr = S_OK;
274 274
275 hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE); 275 hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE, FALSE);
276 ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); 276 ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable.");
277 277
278LExit: 278LExit:
@@ -2493,7 +2493,7 @@ static HRESULT ExecutePackageComplete(
2493 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) 2493 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
2494 { 2494 {
2495 // Best effort to set the forced restart package variable. 2495 // Best effort to set the forced restart package variable.
2496 VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE); 2496 VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE, FALSE);
2497 } 2497 }
2498 2498
2499 // If we're retrying, leave a message in the log file and say everything is okay. 2499 // If we're retrying, leave a message in the log file and say everything is okay.
diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp
index 9338426d..315281bc 100644
--- a/src/engine/cache.cpp
+++ b/src/engine/cache.cpp
@@ -157,7 +157,7 @@ extern "C" HRESULT CacheInitialize(
157 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); 157 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
158 if (E_NOTFOUND == hr) 158 if (E_NOTFOUND == hr)
159 { 159 {
160 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE); 160 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE, FALSE);
161 ExitOnFailure(hr, "Failed to set original source variable."); 161 ExitOnFailure(hr, "Failed to set original source variable.");
162 162
163 hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0); 163 hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0);
@@ -170,7 +170,7 @@ extern "C" HRESULT CacheInitialize(
170 hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder); 170 hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder);
171 ExitOnFailure(hr, "Failed to get directory from original source path."); 171 ExitOnFailure(hr, "Failed to get directory from original source path.");
172 172
173 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE); 173 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE, FALSE);
174 ExitOnFailure(hr, "Failed to set original source directory variable."); 174 ExitOnFailure(hr, "Failed to set original source directory variable.");
175 } 175 }
176 } 176 }
@@ -549,7 +549,7 @@ extern "C" HRESULT CacheSetLastUsedSource(
549 549
550 if (CSTR_EQUAL != nCompare) 550 if (CSTR_EQUAL != nCompare)
551 { 551 {
552 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE); 552 hr = VariableSetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE, FALSE);
553 ExitOnFailure(hr, "Failed to set last source."); 553 ExitOnFailure(hr, "Failed to set last source.");
554 } 554 }
555 } 555 }
diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp
index 28391d2d..cd346680 100644
--- a/src/engine/condition.cpp
+++ b/src/engine/condition.cpp
@@ -439,6 +439,13 @@ static HRESULT ParseValue(
439 { 439 {
440 ExitOnRootFailure(hr, "Failed to find variable."); 440 ExitOnRootFailure(hr, "Failed to find variable.");
441 } 441 }
442
443 if (BURN_VARIANT_TYPE_FORMATTED == pValue->Type)
444 {
445 // TODO: actually format the value?
446 hr = BVariantChangeType(pValue, BURN_VARIANT_TYPE_STRING);
447 ExitOnRootFailure(hr, "Failed to change variable '%ls' type for condition '%ls'", pContext->NextSymbol.Value.sczValue, pContext->wzCondition);
448 }
442 break; 449 break;
443 450
444 case BURN_SYMBOL_TYPE_NUMBER: __fallthrough; 451 case BURN_SYMBOL_TYPE_NUMBER: __fallthrough;
@@ -642,7 +649,7 @@ static HRESULT NextSymbol(
642 ++n; // terminating '"' 649 ++n; // terminating '"'
643 650
644 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL; 651 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL;
645 hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2); 652 hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2, FALSE);
646 ExitOnFailure(hr, "Failed to set symbol value."); 653 ExitOnFailure(hr, "Failed to set symbol value.");
647 break; 654 break;
648 default: 655 default:
@@ -746,7 +753,7 @@ static HRESULT NextSymbol(
746 { 753 {
747 // identifier 754 // identifier
748 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER; 755 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER;
749 hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n); 756 hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n, FALSE);
750 ExitOnFailure(hr, "Failed to set symbol value."); 757 ExitOnFailure(hr, "Failed to set symbol value.");
751 } 758 }
752 } 759 }
diff --git a/src/engine/core.cpp b/src/engine/core.cpp
index 26e74588..c34024fd 100644
--- a/src/engine/core.cpp
+++ b/src/engine/core.cpp
@@ -113,13 +113,13 @@ extern "C" HRESULT CoreInitialize(
113 113
114 if (sczSourceProcessPath) 114 if (sczSourceProcessPath)
115 { 115 {
116 hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE); 116 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE, FALSE);
117 ExitOnFailure(hr, "Failed to set source process path variable."); 117 ExitOnFailure(hr, "Failed to set source process path variable.");
118 118
119 hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder); 119 hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder);
120 ExitOnFailure(hr, "Failed to get source process folder from path."); 120 ExitOnFailure(hr, "Failed to get source process folder from path.");
121 121
122 hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE); 122 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE, FALSE);
123 ExitOnFailure(hr, "Failed to set source process folder variable."); 123 ExitOnFailure(hr, "Failed to set source process folder variable.");
124 } 124 }
125 125
@@ -127,7 +127,7 @@ extern "C" HRESULT CoreInitialize(
127 // Needs to be done after ManifestLoadXmlFromBuffer. 127 // Needs to be done after ManifestLoadXmlFromBuffer.
128 if (sczOriginalSource) 128 if (sczOriginalSource)
129 { 129 {
130 hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE); 130 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE, FALSE);
131 ExitOnFailure(hr, "Failed to set original source variable."); 131 ExitOnFailure(hr, "Failed to set original source variable.");
132 } 132 }
133 133
@@ -258,7 +258,7 @@ extern "C" HRESULT CoreDetect(
258 } 258 }
259 else 259 else
260 { 260 {
261 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE); 261 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE, FALSE);
262 ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable."); 262 ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable.");
263 } 263 }
264 264
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 36c58b49..71c37138 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -538,7 +538,7 @@ static HRESULT RunNormal(
538 // If a layout directory was specified on the command-line, set it as a well-known variable. 538 // If a layout directory was specified on the command-line, set it as a well-known variable.
539 if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) 539 if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory)
540 { 540 {
541 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE); 541 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE, FALSE);
542 ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); 542 ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line.");
543 } 543 }
544 544
diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp
index 71540d5d..8d6cd7fd 100644
--- a/src/engine/exeengine.cpp
+++ b/src/engine/exeengine.cpp
@@ -399,7 +399,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
399 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); 399 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId);
400 400
401 // Best effort to set the execute package cache folder and action variables. 401 // Best effort to set the execute package cache folder and action variables.
402 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); 402 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
403 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE); 403 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE);
404 404
405 hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); 405 hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath);
@@ -591,8 +591,8 @@ LExit:
591 ReleaseFileHandle(hExecutableFile); 591 ReleaseFileHandle(hExecutableFile);
592 592
593 // Best effort to clear the execute package cache folder and action variables. 593 // Best effort to clear the execute package cache folder and action variables.
594 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); 594 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
595 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); 595 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
596 596
597 return hr; 597 return hr;
598} 598}
diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp
index 818532ad..55b65336 100644
--- a/src/engine/logging.cpp
+++ b/src/engine/logging.cpp
@@ -149,7 +149,7 @@ extern "C" HRESULT LoggingOpen(
149 149
150 if (pLog->sczPathVariable && *pLog->sczPathVariable) 150 if (pLog->sczPathVariable && *pLog->sczPathVariable)
151 { 151 {
152 VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE); // Ignore failure. 152 VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE, FALSE); // Ignore failure.
153 } 153 }
154 } 154 }
155 155
@@ -220,7 +220,7 @@ extern "C" HRESULT LoggingSetPackageVariable(
220 hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension); 220 hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension);
221 ExitOnFailure(hr, "Failed to allocate path for package log."); 221 ExitOnFailure(hr, "Failed to allocate path for package log.");
222 222
223 hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE); 223 hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE, FALSE);
224 ExitOnFailure(hr, "Failed to set log path into variable."); 224 ExitOnFailure(hr, "Failed to set log path into variable.");
225 225
226 if (psczLogPath) 226 if (psczLogPath)
diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp
index 8b8121c1..e7cffd62 100644
--- a/src/engine/msiengine.cpp
+++ b/src/engine/msiengine.cpp
@@ -1156,7 +1156,7 @@ extern "C" HRESULT MsiEngineExecutePackage(
1156 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId); 1156 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId);
1157 1157
1158 // Best effort to set the execute package cache folder variable. 1158 // Best effort to set the execute package cache folder variable.
1159 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); 1159 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
1160 1160
1161 hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath); 1161 hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath);
1162 ExitOnFailure(hr, "Failed to build MSI path."); 1162 ExitOnFailure(hr, "Failed to build MSI path.");
@@ -1313,8 +1313,8 @@ LExit:
1313 } 1313 }
1314 1314
1315 // Best effort to clear the execute package cache folder and action variables. 1315 // Best effort to clear the execute package cache folder and action variables.
1316 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); 1316 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
1317 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); 1317 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
1318 1318
1319 return hr; 1319 return hr;
1320} 1320}
diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp
index 57321d75..0854862b 100644
--- a/src/engine/mspengine.cpp
+++ b/src/engine/mspengine.cpp
@@ -488,7 +488,7 @@ extern "C" HRESULT MspEngineExecutePackage(
488 488
489 // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only 489 // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only
490 // Best effort to set the execute package cache folder variable. 490 // Best effort to set the execute package cache folder variable.
491 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); 491 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
492 492
493 hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); 493 hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath);
494 ExitOnFailure(hr, "Failed to build MSP path."); 494 ExitOnFailure(hr, "Failed to build MSP path.");
@@ -609,8 +609,8 @@ LExit:
609 } 609 }
610 610
611 // Best effort to clear the execute package cache folder and action variables. 611 // Best effort to clear the execute package cache folder and action variables.
612 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); 612 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
613 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); 613 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE);
614 614
615 return hr; 615 return hr;
616} 616}
diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp
index 3818e932..0c81873e 100644
--- a/src/engine/msuengine.cpp
+++ b/src/engine/msuengine.cpp
@@ -310,7 +310,7 @@ extern "C" HRESULT MsuEngineExecutePackage(
310 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); 310 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId);
311 311
312 // Best effort to set the execute package cache folder variable. 312 // Best effort to set the execute package cache folder variable.
313 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); 313 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE);
314 314
315 hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); 315 hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath);
316 ExitOnFailure(hr, "Failed to build MSU path."); 316 ExitOnFailure(hr, "Failed to build MSU path.");
@@ -419,7 +419,7 @@ LExit:
419 } 419 }
420 420
421 // Best effort to clear the execute package cache folder variable. 421 // Best effort to clear the execute package cache folder variable.
422 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); 422 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE);
423 423
424 return hr; 424 return hr;
425} 425}
diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp
index bbde92b3..eace62ce 100644
--- a/src/engine/registration.cpp
+++ b/src/engine/registration.cpp
@@ -426,14 +426,14 @@ extern "C" HRESULT RegistrationSetVariables(
426 426
427 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) 427 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
428 { 428 {
429 hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE); 429 hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE, FALSE);
430 ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable."); 430 ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable.");
431 } 431 }
432 432
433 hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE); 433 hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE, FALSE);
434 ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable."); 434 ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable.");
435 435
436 hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE); 436 hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE, FALSE);
437 ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); 437 ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable.");
438 438
439 hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE); 439 hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE);
@@ -1129,7 +1129,7 @@ static HRESULT GetBundleManufacturer(
1129 hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer); 1129 hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer);
1130 if (E_NOTFOUND == hr) 1130 if (E_NOTFOUND == hr)
1131 { 1131 {
1132 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE); 1132 hr = VariableSetString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE, FALSE);
1133 ExitOnFailure(hr, "Failed to set bundle manufacturer."); 1133 ExitOnFailure(hr, "Failed to set bundle manufacturer.");
1134 1134
1135 hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0); 1135 hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0);
@@ -1151,7 +1151,7 @@ static HRESULT GetBundleName(
1151 hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName); 1151 hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName);
1152 if (E_NOTFOUND == hr) 1152 if (E_NOTFOUND == hr)
1153 { 1153 {
1154 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE); 1154 hr = VariableSetString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE, FALSE);
1155 ExitOnFailure(hr, "Failed to set bundle name."); 1155 ExitOnFailure(hr, "Failed to set bundle name.");
1156 1156
1157 hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0); 1157 hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0);
diff --git a/src/engine/search.cpp b/src/engine/search.cpp
index 16f8e459..2978edd3 100644
--- a/src/engine/search.cpp
+++ b/src/engine/search.cpp
@@ -239,7 +239,11 @@ extern "C" HRESULT SearchesParseFromXml(
239 hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz); 239 hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz);
240 ExitOnFailure(hr, "Failed to get @VariableType."); 240 ExitOnFailure(hr, "Failed to get @VariableType.");
241 241
242 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) 242 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
243 {
244 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_FORMATTED;
245 }
246 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
243 { 247 {
244 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC; 248 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC;
245 } 249 }
@@ -403,14 +407,18 @@ extern "C" HRESULT SearchesParseFromXml(
403 { 407 {
404 ExitOnFailure(hr, "Failed to get @Value."); 408 ExitOnFailure(hr, "Failed to get @Value.");
405 409
406 hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0); 410 hr = BVariantSetString(&pSearch->SetVariable.value, scz, 0, FALSE);
407 ExitOnFailure(hr, "Failed to set variant value."); 411 ExitOnFailure(hr, "Failed to set variant value.");
408 412
409 // @Type 413 // @Type
410 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); 414 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
411 ExitOnFailure(hr, "Failed to get @Type."); 415 ExitOnFailure(hr, "Failed to get @Type.");
412 416
413 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) 417 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
418 {
419 valueType = BURN_VARIANT_TYPE_FORMATTED;
420 }
421 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
414 { 422 {
415 valueType = BURN_VARIANT_TYPE_NUMERIC; 423 valueType = BURN_VARIANT_TYPE_NUMERIC;
416 } 424 }
@@ -673,7 +681,7 @@ static HRESULT DirectorySearchPath(
673 } 681 }
674 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) 682 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
675 { 683 {
676 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); 684 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
677 ExitOnFailure(hr, "Failed to set directory search path variable."); 685 ExitOnFailure(hr, "Failed to set directory search path variable.");
678 } 686 }
679 else // must have found a file. 687 else // must have found a file.
@@ -793,7 +801,7 @@ static HRESULT FileSearchPath(
793 } 801 }
794 else // found our file. 802 else // found our file.
795 { 803 {
796 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); 804 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
797 ExitOnFailure(hr, "Failed to set variable to file search path."); 805 ExitOnFailure(hr, "Failed to set variable to file search path.");
798 } 806 }
799 807
@@ -933,7 +941,7 @@ static HRESULT RegistrySearchValue(
933 { 941 {
934 // What if there is a hidden variable in sczKey? 942 // What if there is a hidden variable in sczKey?
935 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); 943 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
936 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); 944 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
937 ExitOnFailure(hr, "Failed to clear variable."); 945 ExitOnFailure(hr, "Failed to clear variable.");
938 ExitFunction1(hr = S_OK); 946 ExitFunction1(hr = S_OK);
939 } 947 }
@@ -945,7 +953,7 @@ static HRESULT RegistrySearchValue(
945 { 953 {
946 // What if there is a hidden variable in sczKey or sczValue? 954 // What if there is a hidden variable in sczKey or sczValue?
947 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); 955 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
948 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); 956 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
949 ExitOnFailure(hr, "Failed to clear variable."); 957 ExitOnFailure(hr, "Failed to clear variable.");
950 ExitFunction1(hr = S_OK); 958 ExitFunction1(hr = S_OK);
951 } 959 }
@@ -995,7 +1003,7 @@ static HRESULT RegistrySearchValue(
995 } 1003 }
996 __fallthrough; 1004 __fallthrough;
997 case REG_SZ: 1005 case REG_SZ:
998 hr = BVariantSetString(&value, (LPCWSTR)pData, 0); 1006 hr = BVariantSetString(&value, (LPCWSTR)pData, 0, FALSE);
999 break; 1007 break;
1000 default: 1008 default:
1001 ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); 1009 ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType);
@@ -1006,8 +1014,8 @@ static HRESULT RegistrySearchValue(
1006 hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType); 1014 hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType);
1007 ExitOnFailure(hr, "Failed to change value type."); 1015 ExitOnFailure(hr, "Failed to change value type.");
1008 1016
1009 // Set variable as a literal. 1017 // Set variable.
1010 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); 1018 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
1011 ExitOnFailure(hr, "Failed to set variable."); 1019 ExitOnFailure(hr, "Failed to set variable.");
1012 1020
1013LExit: 1021LExit:
@@ -1077,7 +1085,7 @@ static HRESULT MsiComponentSearch(
1077 case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH: 1085 case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH:
1078 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) 1086 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1079 { 1087 {
1080 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); 1088 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
1081 } 1089 }
1082 break; 1090 break;
1083 case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE: 1091 case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE:
@@ -1093,7 +1101,7 @@ static HRESULT MsiComponentSearch(
1093 wz[1] = L'\0'; 1101 wz[1] = L'\0';
1094 } 1102 }
1095 1103
1096 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); 1104 hr = VariableSetString(pVariables, pSearch->sczVariable, sczPath, FALSE, FALSE);
1097 } 1105 }
1098 break; 1106 break;
1099 } 1107 }
@@ -1234,8 +1242,8 @@ static HRESULT MsiProductSearch(
1234 hr = BVariantChangeType(&value, type); 1242 hr = BVariantChangeType(&value, type);
1235 ExitOnFailure(hr, "Failed to change value type."); 1243 ExitOnFailure(hr, "Failed to change value type.");
1236 1244
1237 // Set variable as a literal. 1245 // Set variable.
1238 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); 1246 hr = VariableSetVariant(pVariables, pSearch->sczVariable, &value);
1239 ExitOnFailure(hr, "Failed to set variable."); 1247 ExitOnFailure(hr, "Failed to set variable.");
1240 1248
1241LExit: 1249LExit:
diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp
index 6322942e..fb4b74e2 100644
--- a/src/engine/variable.cpp
+++ b/src/engine/variable.cpp
@@ -83,7 +83,6 @@ static HRESULT SetVariableValue(
83 __in BURN_VARIABLES* pVariables, 83 __in BURN_VARIABLES* pVariables,
84 __in_z LPCWSTR wzVariable, 84 __in_z LPCWSTR wzVariable,
85 __in BURN_VARIANT* pVariant, 85 __in BURN_VARIANT* pVariant,
86 __in BOOL fLiteral,
87 __in SET_VARIABLE setBuiltin, 86 __in SET_VARIABLE setBuiltin,
88 __in BOOL fLog 87 __in BOOL fLog
89 ); 88 );
@@ -335,14 +334,22 @@ extern "C" HRESULT VariablesParseFromXml(
335 { 334 {
336 ExitOnFailure(hr, "Failed to get @Value."); 335 ExitOnFailure(hr, "Failed to get @Value.");
337 336
338 hr = BVariantSetString(&value, scz, 0); 337 hr = BVariantSetString(&value, scz, 0, FALSE);
339 ExitOnFailure(hr, "Failed to set variant value."); 338 ExitOnFailure(hr, "Failed to set variant value.");
340 339
341 // @Type 340 // @Type
342 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); 341 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
343 ExitOnFailure(hr, "Failed to get @Type."); 342 ExitOnFailure(hr, "Failed to get @Type.");
344 343
345 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) 344 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1))
345 {
346 if (!fHidden)
347 {
348 LogStringLine(REPORT_STANDARD, "Initializing formatted variable '%ls' to value '%ls'", sczId, value.sczValue);
349 }
350 valueType = BURN_VARIANT_TYPE_FORMATTED;
351 }
352 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
346 { 353 {
347 if (!fHidden) 354 if (!fHidden)
348 { 355 {
@@ -637,10 +644,7 @@ extern "C" HRESULT VariableGetFormatted(
637 } 644 }
638 ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); 645 ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable);
639 646
640 // Strings need to get expanded unless they're built-in or literal because they're guaranteed not to have embedded variables. 647 if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type)
641 if (BURN_VARIANT_TYPE_STRING == pVariable->Value.Type &&
642 BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariable->internalType &&
643 !pVariable->fLiteral)
644 { 648 {
645 hr = BVariantGetString(&pVariable->Value, &scz); 649 hr = BVariantGetString(&pVariable->Value, &scz);
646 ExitOnFailure(hr, "Failed to get unformatted string."); 650 ExitOnFailure(hr, "Failed to get unformatted string.");
@@ -674,39 +678,24 @@ extern "C" HRESULT VariableSetNumeric(
674 variant.llValue = llValue; 678 variant.llValue = llValue;
675 variant.Type = BURN_VARIANT_TYPE_NUMERIC; 679 variant.Type = BURN_VARIANT_TYPE_NUMERIC;
676 680
677 return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); 681 return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
678}
679
680extern "C" HRESULT VariableSetLiteralString(
681 __in BURN_VARIABLES* pVariables,
682 __in_z LPCWSTR wzVariable,
683 __in_z_opt LPCWSTR wzValue,
684 __in BOOL fOverwriteBuiltIn
685 )
686{
687 BURN_VARIANT variant = { };
688
689 // We're not going to encrypt this value, so can access the value directly.
690 variant.sczValue = (LPWSTR)wzValue;
691 variant.Type = BURN_VARIANT_TYPE_STRING;
692
693 return SetVariableValue(pVariables, wzVariable, &variant, TRUE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
694} 682}
695 683
696extern "C" HRESULT VariableSetString( 684extern "C" HRESULT VariableSetString(
697 __in BURN_VARIABLES* pVariables, 685 __in BURN_VARIABLES* pVariables,
698 __in_z LPCWSTR wzVariable, 686 __in_z LPCWSTR wzVariable,
699 __in_z_opt LPCWSTR wzValue, 687 __in_z_opt LPCWSTR wzValue,
700 __in BOOL fOverwriteBuiltIn 688 __in BOOL fOverwriteBuiltIn,
689 __in BOOL fFormatted
701 ) 690 )
702{ 691{
703 BURN_VARIANT variant = { }; 692 BURN_VARIANT variant = { };
704 693
705 // We're not going to encrypt this value, so can access the value directly. 694 // We're not going to encrypt this value, so can access the value directly.
706 variant.sczValue = (LPWSTR)wzValue; 695 variant.sczValue = (LPWSTR)wzValue;
707 variant.Type = BURN_VARIANT_TYPE_STRING; 696 variant.Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING;
708 697
709 return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); 698 return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
710} 699}
711 700
712extern "C" HRESULT VariableSetVersion( 701extern "C" HRESULT VariableSetVersion(
@@ -722,16 +711,7 @@ extern "C" HRESULT VariableSetVersion(
722 variant.qwValue = qwValue; 711 variant.qwValue = qwValue;
723 variant.Type = BURN_VARIANT_TYPE_VERSION; 712 variant.Type = BURN_VARIANT_TYPE_VERSION;
724 713
725 return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); 714 return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
726}
727
728extern "C" HRESULT VariableSetLiteralVariant(
729 __in BURN_VARIABLES* pVariables,
730 __in_z LPCWSTR wzVariable,
731 __in BURN_VARIANT* pVariant
732 )
733{
734 return SetVariableValue(pVariables, wzVariable, pVariant, TRUE, SET_VARIABLE_NOT_BUILTIN, TRUE);
735} 715}
736 716
737extern "C" HRESULT VariableSetVariant( 717extern "C" HRESULT VariableSetVariant(
@@ -740,7 +720,7 @@ extern "C" HRESULT VariableSetVariant(
740 __in BURN_VARIANT * pVariant 720 __in BURN_VARIANT * pVariant
741 ) 721 )
742{ 722{
743 return SetVariableValue(pVariables, wzVariable, pVariant, FALSE, SET_VARIABLE_NOT_BUILTIN, TRUE); 723 return SetVariableValue(pVariables, wzVariable, pVariant, SET_VARIABLE_NOT_BUILTIN, TRUE);
744} 724}
745 725
746// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree 726// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree
@@ -888,6 +868,7 @@ extern "C" HRESULT VariableSerialize(
888 868
889 SecureZeroMemory(&qw, sizeof(qw)); 869 SecureZeroMemory(&qw, sizeof(qw));
890 break; 870 break;
871 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
891 case BURN_VARIANT_TYPE_STRING: 872 case BURN_VARIANT_TYPE_STRING:
892 hr = BVariantGetString(&pVariable->Value, &scz); 873 hr = BVariantGetString(&pVariable->Value, &scz);
893 ExitOnFailure(hr, "Failed to get string."); 874 ExitOnFailure(hr, "Failed to get string.");
@@ -901,10 +882,6 @@ extern "C" HRESULT VariableSerialize(
901 hr = E_INVALIDARG; 882 hr = E_INVALIDARG;
902 ExitOnFailure(hr, "Unsupported variable type."); 883 ExitOnFailure(hr, "Unsupported variable type.");
903 } 884 }
904
905 // Write literal flag.
906 hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->fLiteral);
907 ExitOnFailure(hr, "Failed to write literal flag.");
908 } 885 }
909 886
910LExit: 887LExit:
@@ -928,7 +905,6 @@ extern "C" HRESULT VariableDeserialize(
928 DWORD cVariables = 0; 905 DWORD cVariables = 0;
929 LPWSTR sczName = NULL; 906 LPWSTR sczName = NULL;
930 BOOL fIncluded = FALSE; 907 BOOL fIncluded = FALSE;
931 BOOL fLiteral = FALSE;
932 BURN_VARIANT value = { }; 908 BURN_VARIANT value = { };
933 LPWSTR scz = NULL; 909 LPWSTR scz = NULL;
934 DWORD64 qw = 0; 910 DWORD64 qw = 0;
@@ -982,11 +958,12 @@ extern "C" HRESULT VariableDeserialize(
982 958
983 SecureZeroMemory(&qw, sizeof(qw)); 959 SecureZeroMemory(&qw, sizeof(qw));
984 break; 960 break;
961 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
985 case BURN_VARIANT_TYPE_STRING: 962 case BURN_VARIANT_TYPE_STRING:
986 hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); 963 hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz);
987 ExitOnFailure(hr, "Failed to read variable value as string."); 964 ExitOnFailure(hr, "Failed to read variable value as string.");
988 965
989 hr = BVariantSetString(&value, scz, NULL); 966 hr = BVariantSetString(&value, scz, NULL, BURN_VARIANT_TYPE_FORMATTED == value.Type);
990 ExitOnFailure(hr, "Failed to set variable value."); 967 ExitOnFailure(hr, "Failed to set variable value.");
991 968
992 ReleaseNullStrSecure(scz); 969 ReleaseNullStrSecure(scz);
@@ -996,12 +973,8 @@ extern "C" HRESULT VariableDeserialize(
996 ExitOnFailure(hr, "Unsupported variable type."); 973 ExitOnFailure(hr, "Unsupported variable type.");
997 } 974 }
998 975
999 // Read variable literal flag.
1000 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fLiteral);
1001 ExitOnFailure(hr, "Failed to read variable literal flag.");
1002
1003 // Set variable. 976 // Set variable.
1004 hr = SetVariableValue(pVariables, sczName, &value, fLiteral, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); 977 hr = SetVariableValue(pVariables, sczName, &value, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE);
1005 ExitOnFailure(hr, "Failed to set variable."); 978 ExitOnFailure(hr, "Failed to set variable.");
1006 979
1007 // Clean up. 980 // Clean up.
@@ -1525,7 +1498,6 @@ static HRESULT SetVariableValue(
1525 __in BURN_VARIABLES* pVariables, 1498 __in BURN_VARIABLES* pVariables,
1526 __in_z LPCWSTR wzVariable, 1499 __in_z LPCWSTR wzVariable,
1527 __in BURN_VARIANT* pVariant, 1500 __in BURN_VARIANT* pVariant,
1528 __in BOOL fLiteral,
1529 __in SET_VARIABLE setBuiltin, 1501 __in SET_VARIABLE setBuiltin,
1530 __in BOOL fLog 1502 __in BOOL fLog
1531 ) 1503 )
@@ -1587,6 +1559,7 @@ static HRESULT SetVariableValue(
1587 LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue); 1559 LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue);
1588 break; 1560 break;
1589 1561
1562 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
1590 case BURN_VARIANT_TYPE_STRING: 1563 case BURN_VARIANT_TYPE_STRING:
1591 if (!pVariant->sczValue) 1564 if (!pVariant->sczValue)
1592 { 1565 {
@@ -1594,7 +1567,7 @@ static HRESULT SetVariableValue(
1594 } 1567 }
1595 else 1568 else
1596 { 1569 {
1597 LogStringLine(REPORT_STANDARD, "Setting string variable '%ls' to value '%ls'", wzVariable, pVariant->sczValue); 1570 LogStringLine(REPORT_STANDARD, "Setting %ls variable '%ls' to value '%ls'", BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ? L"formatted" : L"string", wzVariable, pVariant->sczValue);
1598 } 1571 }
1599 break; 1572 break;
1600 1573
@@ -1613,9 +1586,6 @@ static HRESULT SetVariableValue(
1613 hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant); 1586 hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant);
1614 ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable); 1587 ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable);
1615 1588
1616 // Update variable literal flag.
1617 pVariables->rgVariables[iVariable].fLiteral = fLiteral;
1618
1619LExit: 1589LExit:
1620 ::LeaveCriticalSection(&pVariables->csAccess); 1590 ::LeaveCriticalSection(&pVariables->csAccess);
1621 1591
@@ -1827,7 +1797,7 @@ static HRESULT InitializeVariableComputerName(
1827 } 1797 }
1828 1798
1829 // set value 1799 // set value
1830 hr = BVariantSetString(pValue, wzComputerName, 0); 1800 hr = BVariantSetString(pValue, wzComputerName, 0, FALSE);
1831 ExitOnFailure(hr, "Failed to set variant value."); 1801 ExitOnFailure(hr, "Failed to set variant value.");
1832 1802
1833LExit: 1803LExit:
@@ -1875,7 +1845,7 @@ static HRESULT InitializeVariableCsidlFolder(
1875 ExitOnRootFailure(hr, "Failed to get shell folder."); 1845 ExitOnRootFailure(hr, "Failed to get shell folder.");
1876 1846
1877 // set value 1847 // set value
1878 hr = BVariantSetString(pValue, sczPath, 0); 1848 hr = BVariantSetString(pValue, sczPath, 0, FALSE);
1879 ExitOnFailure(hr, "Failed to set variant value."); 1849 ExitOnFailure(hr, "Failed to set variant value.");
1880 1850
1881LExit: 1851LExit:
@@ -1901,7 +1871,7 @@ static HRESULT InitializeVariableTempFolder(
1901 } 1871 }
1902 1872
1903 // set value 1873 // set value
1904 hr = BVariantSetString(pValue, wzPath, 0); 1874 hr = BVariantSetString(pValue, wzPath, 0, FALSE);
1905 ExitOnFailure(hr, "Failed to set variant value."); 1875 ExitOnFailure(hr, "Failed to set variant value.");
1906 1876
1907LExit: 1877LExit:
@@ -1972,7 +1942,7 @@ static HRESULT InitializeVariableSystemFolder(
1972 } 1942 }
1973 1943
1974 // set value 1944 // set value
1975 hr = BVariantSetString(pValue, wzSystemFolder, 0); 1945 hr = BVariantSetString(pValue, wzSystemFolder, 0, FALSE);
1976 ExitOnFailure(hr, "Failed to set system folder variant value."); 1946 ExitOnFailure(hr, "Failed to set system folder variant value.");
1977 1947
1978LExit: 1948LExit:
@@ -2003,7 +1973,7 @@ static HRESULT InitializeVariableWindowsVolumeFolder(
2003 } 1973 }
2004 1974
2005 // set value 1975 // set value
2006 hr = BVariantSetString(pValue, wzVolumePath, 0); 1976 hr = BVariantSetString(pValue, wzVolumePath, 0, FALSE);
2007 ExitOnFailure(hr, "Failed to set variant value."); 1977 ExitOnFailure(hr, "Failed to set variant value.");
2008 1978
2009LExit: 1979LExit:
@@ -2130,7 +2100,7 @@ static HRESULT InitializeVariableString(
2130 LPCWSTR wzValue = (LPCWSTR)dwpData; 2100 LPCWSTR wzValue = (LPCWSTR)dwpData;
2131 2101
2132 // set value 2102 // set value
2133 hr = BVariantSetString(pValue, wzValue, 0); 2103 hr = BVariantSetString(pValue, wzValue, 0, FALSE);
2134 ExitOnFailure(hr, "Failed to set variant value."); 2104 ExitOnFailure(hr, "Failed to set variant value.");
2135 2105
2136LExit: 2106LExit:
@@ -2176,7 +2146,7 @@ static HRESULT InitializeVariableRegistryFolder(
2176 ExitOnFailure(hr, "Failed to get 64-bit folder."); 2146 ExitOnFailure(hr, "Failed to get 64-bit folder.");
2177 2147
2178 // set value 2148 // set value
2179 hr = BVariantSetString(pValue, sczPath, 0); 2149 hr = BVariantSetString(pValue, sczPath, 0, FALSE);
2180 ExitOnFailure(hr, "Failed to set variant value."); 2150 ExitOnFailure(hr, "Failed to set variant value.");
2181 2151
2182LExit: 2152LExit:
@@ -2212,7 +2182,7 @@ static HRESULT InitializeVariable6432Folder(
2212 } 2182 }
2213 2183
2214 // set value 2184 // set value
2215 hr = BVariantSetString(pValue, sczPath, 0); 2185 hr = BVariantSetString(pValue, sczPath, 0, FALSE);
2216 ExitOnFailure(hr, "Failed to set variant value."); 2186 ExitOnFailure(hr, "Failed to set variant value.");
2217 2187
2218LExit: 2188LExit:
@@ -2249,7 +2219,7 @@ static HRESULT InitializeVariableDate(
2249 } 2219 }
2250 2220
2251 // set value 2221 // set value
2252 hr = BVariantSetString(pValue, sczDate, cchDate); 2222 hr = BVariantSetString(pValue, sczDate, cchDate, FALSE);
2253 ExitOnFailure(hr, "Failed to set variant value."); 2223 ExitOnFailure(hr, "Failed to set variant value.");
2254 2224
2255LExit: 2225LExit:
@@ -2266,7 +2236,7 @@ static HRESULT InitializeVariableInstallerName(
2266 HRESULT hr = S_OK; 2236 HRESULT hr = S_OK;
2267 2237
2268 // set value 2238 // set value
2269 hr = BVariantSetString(pValue, L"WiX Burn", 0); 2239 hr = BVariantSetString(pValue, L"WiX Burn", 0, FALSE);
2270 ExitOnFailure(hr, "Failed to set variant value."); 2240 ExitOnFailure(hr, "Failed to set variant value.");
2271 2241
2272LExit: 2242LExit:
@@ -2285,7 +2255,7 @@ static HRESULT InitializeVariableInstallerVersion(
2285 ExitOnFailure(hr, "Failed to copy the engine version."); 2255 ExitOnFailure(hr, "Failed to copy the engine version.");
2286 2256
2287 // set value 2257 // set value
2288 hr = BVariantSetString(pValue, sczVersion, 0); 2258 hr = BVariantSetString(pValue, sczVersion, 0, FALSE);
2289 ExitOnFailure(hr, "Failed to set variant value."); 2259 ExitOnFailure(hr, "Failed to set variant value.");
2290 2260
2291LExit: 2261LExit:
@@ -2325,7 +2295,7 @@ static HRESULT InitializeVariableLogonUser(
2325 } 2295 }
2326 2296
2327 // set value 2297 // set value
2328 hr = BVariantSetString(pValue, wzUserName, 0); 2298 hr = BVariantSetString(pValue, wzUserName, 0, FALSE);
2329 ExitOnFailure(hr, "Failed to set variant value."); 2299 ExitOnFailure(hr, "Failed to set variant value.");
2330 2300
2331LExit: 2301LExit:
diff --git a/src/engine/variable.h b/src/engine/variable.h
index 63f12cdf..648c5daf 100644
--- a/src/engine/variable.h
+++ b/src/engine/variable.h
@@ -39,8 +39,7 @@ typedef struct _BURN_VARIABLE
39{ 39{
40 LPWSTR sczName; 40 LPWSTR sczName;
41 BURN_VARIANT Value; 41 BURN_VARIANT Value;
42 BOOL fHidden; 42 BOOL fHidden;
43 BOOL fLiteral; // if fLiteral, then when formatting this variable its value should be used as is (don't continue recursively formatting).
44 BOOL fPersisted; 43 BOOL fPersisted;
45 44
46 // used for late initialization of built-in variables 45 // used for late initialization of built-in variables
@@ -104,17 +103,12 @@ HRESULT VariableSetNumeric(
104 __in LONGLONG llValue, 103 __in LONGLONG llValue,
105 __in BOOL fOverwriteBuiltIn 104 __in BOOL fOverwriteBuiltIn
106 ); 105 );
107HRESULT VariableSetLiteralString(
108 __in BURN_VARIABLES* pVariables,
109 __in_z LPCWSTR wzVariable,
110 __in_z_opt LPCWSTR wzValue,
111 __in BOOL fOverwriteBuiltIn
112 );
113HRESULT VariableSetString( 106HRESULT VariableSetString(
114 __in BURN_VARIABLES* pVariables, 107 __in BURN_VARIABLES* pVariables,
115 __in_z LPCWSTR wzVariable, 108 __in_z LPCWSTR wzVariable,
116 __in_z_opt LPCWSTR wzValue, 109 __in_z_opt LPCWSTR wzValue,
117 __in BOOL fOverwriteBuiltIn 110 __in BOOL fOverwriteBuiltIn,
111 __in BOOL fFormatted
118 ); 112 );
119HRESULT VariableSetVersion( 113HRESULT VariableSetVersion(
120 __in BURN_VARIABLES* pVariables, 114 __in BURN_VARIABLES* pVariables,
@@ -122,11 +116,6 @@ HRESULT VariableSetVersion(
122 __in DWORD64 qwValue, 116 __in DWORD64 qwValue,
123 __in BOOL fOverwriteBuiltIn 117 __in BOOL fOverwriteBuiltIn
124 ); 118 );
125HRESULT VariableSetLiteralVariant(
126 __in BURN_VARIABLES* pVariables,
127 __in_z LPCWSTR wzVariable,
128 __in BURN_VARIANT* pVariant
129 );
130HRESULT VariableSetVariant( 119HRESULT VariableSetVariant(
131 __in BURN_VARIABLES* pVariables, 120 __in BURN_VARIABLES* pVariables,
132 __in_z LPCWSTR wzVariable, 121 __in_z LPCWSTR wzVariable,
diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp
index 5e9bd29a..43bc19c4 100644
--- a/src/engine/variant.cpp
+++ b/src/engine/variant.cpp
@@ -32,7 +32,8 @@ extern "C" void BVariantUninitialize(
32 __in BURN_VARIANT* pVariant 32 __in BURN_VARIANT* pVariant
33 ) 33 )
34{ 34{
35 if (BURN_VARIANT_TYPE_STRING == pVariant->Type) 35 if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ||
36 BURN_VARIANT_TYPE_STRING == pVariant->Type)
36 { 37 {
37 StrSecureZeroFreeString(pVariant->sczValue); 38 StrSecureZeroFreeString(pVariant->sczValue);
38 } 39 }
@@ -53,6 +54,7 @@ extern "C" HRESULT BVariantGetNumeric(
53 case BURN_VARIANT_TYPE_NUMERIC: 54 case BURN_VARIANT_TYPE_NUMERIC:
54 BVariantRetrieveNumeric(pVariant, pllValue); 55 BVariantRetrieveNumeric(pVariant, pllValue);
55 break; 56 break;
57 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
56 case BURN_VARIANT_TYPE_STRING: 58 case BURN_VARIANT_TYPE_STRING:
57 hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); 59 hr = BVariantRetrieveDecryptedString(pVariant, &sczValue);
58 if (SUCCEEDED(hr)) 60 if (SUCCEEDED(hr))
@@ -97,6 +99,7 @@ extern "C" HRESULT BVariantGetString(
97 } 99 }
98 SecureZeroMemory(&llValue, sizeof(llValue)); 100 SecureZeroMemory(&llValue, sizeof(llValue));
99 break; 101 break;
102 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
100 case BURN_VARIANT_TYPE_STRING: 103 case BURN_VARIANT_TYPE_STRING:
101 hr = BVariantRetrieveDecryptedString(pVariant, psczValue); 104 hr = BVariantRetrieveDecryptedString(pVariant, psczValue);
102 break; 105 break;
@@ -136,6 +139,7 @@ extern "C" HRESULT BVariantGetVersion(
136 case BURN_VARIANT_TYPE_NUMERIC: 139 case BURN_VARIANT_TYPE_NUMERIC:
137 BVariantRetrieveNumeric(pVariant, (LONGLONG*)pqwValue); 140 BVariantRetrieveNumeric(pVariant, (LONGLONG*)pqwValue);
138 break; 141 break;
142 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
139 case BURN_VARIANT_TYPE_STRING: 143 case BURN_VARIANT_TYPE_STRING:
140 hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); 144 hr = BVariantRetrieveDecryptedString(pVariant, &sczValue);
141 if (SUCCEEDED(hr)) 145 if (SUCCEEDED(hr))
@@ -167,7 +171,8 @@ extern "C" HRESULT BVariantSetNumeric(
167 HRESULT hr = S_OK; 171 HRESULT hr = S_OK;
168 BOOL fEncrypt = pVariant->fEncryptString; 172 BOOL fEncrypt = pVariant->fEncryptString;
169 173
170 if (BURN_VARIANT_TYPE_STRING == pVariant->Type) 174 if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ||
175 BURN_VARIANT_TYPE_STRING == pVariant->Type)
171 { 176 {
172 StrSecureZeroFreeString(pVariant->sczValue); 177 StrSecureZeroFreeString(pVariant->sczValue);
173 } 178 }
@@ -182,7 +187,8 @@ extern "C" HRESULT BVariantSetNumeric(
182extern "C" HRESULT BVariantSetString( 187extern "C" HRESULT BVariantSetString(
183 __in BURN_VARIANT* pVariant, 188 __in BURN_VARIANT* pVariant,
184 __in_z_opt LPCWSTR wzValue, 189 __in_z_opt LPCWSTR wzValue,
185 __in DWORD_PTR cchValue 190 __in DWORD_PTR cchValue,
191 __in BOOL fFormatted
186 ) 192 )
187{ 193{
188 HRESULT hr = S_OK; 194 HRESULT hr = S_OK;
@@ -194,7 +200,8 @@ extern "C" HRESULT BVariantSetString(
194 } 200 }
195 else // assign the value. 201 else // assign the value.
196 { 202 {
197 if (BURN_VARIANT_TYPE_STRING != pVariant->Type) 203 if (BURN_VARIANT_TYPE_FORMATTED != pVariant->Type &&
204 BURN_VARIANT_TYPE_STRING != pVariant->Type)
198 { 205 {
199 memset(pVariant, 0, sizeof(BURN_VARIANT)); 206 memset(pVariant, 0, sizeof(BURN_VARIANT));
200 } 207 }
@@ -207,7 +214,7 @@ extern "C" HRESULT BVariantSetString(
207 hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue); 214 hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue);
208 ExitOnFailure(hr, "Failed to copy string."); 215 ExitOnFailure(hr, "Failed to copy string.");
209 216
210 pVariant->Type = BURN_VARIANT_TYPE_STRING; 217 pVariant->Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING;
211 } 218 }
212 219
213LExit: 220LExit:
@@ -223,7 +230,8 @@ extern "C" HRESULT BVariantSetVersion(
223 HRESULT hr = S_OK; 230 HRESULT hr = S_OK;
224 BOOL fEncryptValue = pVariant->fEncryptString; 231 BOOL fEncryptValue = pVariant->fEncryptString;
225 232
226 if (BURN_VARIANT_TYPE_STRING == pVariant->Type) 233 if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ||
234 BURN_VARIANT_TYPE_STRING == pVariant->Type)
227 { 235 {
228 StrSecureZeroFreeString(pVariant->sczValue); 236 StrSecureZeroFreeString(pVariant->sczValue);
229 } 237 }
@@ -259,11 +267,12 @@ extern "C" HRESULT BVariantSetValue(
259 } 267 }
260 SecureZeroMemory(&llValue, sizeof(llValue)); 268 SecureZeroMemory(&llValue, sizeof(llValue));
261 break; 269 break;
270 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
262 case BURN_VARIANT_TYPE_STRING: 271 case BURN_VARIANT_TYPE_STRING:
263 hr = BVariantGetString(pValue, &sczValue); 272 hr = BVariantGetString(pValue, &sczValue);
264 if (SUCCEEDED(hr)) 273 if (SUCCEEDED(hr))
265 { 274 {
266 hr = BVariantSetString(pVariant, sczValue, 0); 275 hr = BVariantSetString(pVariant, sczValue, 0, BURN_VARIANT_TYPE_FORMATTED == pValue->Type);
267 } 276 }
268 StrSecureZeroFreeString(sczValue); 277 StrSecureZeroFreeString(sczValue);
269 break; 278 break;
@@ -310,11 +319,12 @@ extern "C" HRESULT BVariantCopy(
310 } 319 }
311 SecureZeroMemory(&llValue, sizeof(llValue)); 320 SecureZeroMemory(&llValue, sizeof(llValue));
312 break; 321 break;
322 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
313 case BURN_VARIANT_TYPE_STRING: 323 case BURN_VARIANT_TYPE_STRING:
314 hr = BVariantGetString(pSource, &sczValue); 324 hr = BVariantGetString(pSource, &sczValue);
315 if (SUCCEEDED(hr)) 325 if (SUCCEEDED(hr))
316 { 326 {
317 hr = BVariantSetString(pTarget, sczValue, 0); 327 hr = BVariantSetString(pTarget, sczValue, 0, BURN_VARIANT_TYPE_FORMATTED == pSource->Type);
318 } 328 }
319 StrSecureZeroFreeString(sczValue); 329 StrSecureZeroFreeString(sczValue);
320 break; 330 break;
@@ -350,6 +360,12 @@ extern "C" HRESULT BVariantChangeType(
350 { 360 {
351 ExitFunction(); // variant already is of the requested type 361 ExitFunction(); // variant already is of the requested type
352 } 362 }
363 else if (BURN_VARIANT_TYPE_FORMATTED == pVariant->Type && BURN_VARIANT_TYPE_STRING == type ||
364 BURN_VARIANT_TYPE_STRING == pVariant->Type && BURN_VARIANT_TYPE_FORMATTED == type)
365 {
366 pVariant->Type = type;
367 ExitFunction();
368 }
353 369
354 switch (type) 370 switch (type)
355 { 371 {
@@ -359,6 +375,7 @@ extern "C" HRESULT BVariantChangeType(
359 case BURN_VARIANT_TYPE_NUMERIC: 375 case BURN_VARIANT_TYPE_NUMERIC:
360 hr = BVariantGetNumeric(pVariant, &variant.llValue); 376 hr = BVariantGetNumeric(pVariant, &variant.llValue);
361 break; 377 break;
378 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
362 case BURN_VARIANT_TYPE_STRING: 379 case BURN_VARIANT_TYPE_STRING:
363 hr = BVariantGetString(pVariant, &variant.sczValue); 380 hr = BVariantGetString(pVariant, &variant.sczValue);
364 break; 381 break;
@@ -400,6 +417,7 @@ extern "C" HRESULT BVariantSetEncryption(
400 case BURN_VARIANT_TYPE_VERSION: 417 case BURN_VARIANT_TYPE_VERSION:
401 hr = S_OK; 418 hr = S_OK;
402 break; 419 break;
420 case BURN_VARIANT_TYPE_FORMATTED: __fallthrough;
403 case BURN_VARIANT_TYPE_STRING: 421 case BURN_VARIANT_TYPE_STRING:
404 hr = BVariantEncryptString(pVariant, fEncrypt); 422 hr = BVariantEncryptString(pVariant, fEncrypt);
405 break; 423 break;
diff --git a/src/engine/variant.h b/src/engine/variant.h
index 73fbe076..35463479 100644
--- a/src/engine/variant.h
+++ b/src/engine/variant.h
@@ -12,8 +12,9 @@ extern "C" {
12enum BURN_VARIANT_TYPE 12enum BURN_VARIANT_TYPE
13{ 13{
14 BURN_VARIANT_TYPE_NONE, 14 BURN_VARIANT_TYPE_NONE,
15 BURN_VARIANT_TYPE_FORMATTED,
15 BURN_VARIANT_TYPE_NUMERIC, 16 BURN_VARIANT_TYPE_NUMERIC,
16 BURN_VARIANT_TYPE_STRING, 17 BURN_VARIANT_TYPE_STRING, // when formatting this value should be used as is (don't continue recursively formatting).
17 BURN_VARIANT_TYPE_VERSION, 18 BURN_VARIANT_TYPE_VERSION,
18}; 19};
19 20
@@ -57,7 +58,8 @@ HRESULT BVariantSetNumeric(
57HRESULT BVariantSetString( 58HRESULT BVariantSetString(
58 __in BURN_VARIANT* pVariant, 59 __in BURN_VARIANT* pVariant,
59 __in_z_opt LPCWSTR wzValue, 60 __in_z_opt LPCWSTR wzValue,
60 __in DWORD_PTR cchValue 61 __in DWORD_PTR cchValue,
62 __in BOOL fFormatted
61 ); 63 );
62HRESULT BVariantSetVersion( 64HRESULT BVariantSetVersion(
63 __in BURN_VARIANT* pVariant, 65 __in BURN_VARIANT* pVariant,
diff --git a/src/test/BurnUnitTest/SearchTest.cpp b/src/test/BurnUnitTest/SearchTest.cpp
index 48ab60aa..32107d87 100644
--- a/src/test/BurnUnitTest/SearchTest.cpp
+++ b/src/test/BurnUnitTest/SearchTest.cpp
@@ -66,8 +66,8 @@ namespace Bootstrapper
66 pin_ptr<const WCHAR> wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory); 66 pin_ptr<const WCHAR> wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory);
67 pin_ptr<const WCHAR> wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none"))); 67 pin_ptr<const WCHAR> wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none")));
68 68
69 VariableSetStringHelper(&variables, L"Directory1", wzDirectory1); 69 VariableSetStringHelper(&variables, L"Directory1", wzDirectory1, FALSE);
70 VariableSetStringHelper(&variables, L"Directory2", wzDirectory2); 70 VariableSetStringHelper(&variables, L"Directory2", wzDirectory2, FALSE);
71 71
72 LPCWSTR wzDocument = 72 LPCWSTR wzDocument =
73 L"<Bundle>" 73 L"<Bundle>"
@@ -117,8 +117,8 @@ namespace Bootstrapper
117 hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart); 117 hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart);
118 TestThrowOnFailure(hr, L"Failed to get DLL version."); 118 TestThrowOnFailure(hr, L"Failed to get DLL version.");
119 119
120 VariableSetStringHelper(&variables, L"File1", wzFile1); 120 VariableSetStringHelper(&variables, L"File1", wzFile1, FALSE);
121 VariableSetStringHelper(&variables, L"File2", wzFile2); 121 VariableSetStringHelper(&variables, L"File2", wzFile2, FALSE);
122 122
123 LPCWSTR wzDocument = 123 LPCWSTR wzDocument =
124 L"<Bundle>" 124 L"<Bundle>"
@@ -191,8 +191,8 @@ namespace Bootstrapper
191 Assert::True(SUCCEEDED(hr)); 191 Assert::True(SUCCEEDED(hr));
192 } 192 }
193 193
194 VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"); 194 VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value", FALSE);
195 VariableSetStringHelper(&variables, L"MyValue", L"String"); 195 VariableSetStringHelper(&variables, L"MyValue", L"String", FALSE);
196 196
197 LPCWSTR wzDocument = 197 LPCWSTR wzDocument =
198 L"<Bundle>" 198 L"<Bundle>"
@@ -219,8 +219,9 @@ namespace Bootstrapper
219 L" <RegistrySearch Id='Search21' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable21' VariableType='string' Win64='no' />" 219 L" <RegistrySearch Id='Search21' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable21' VariableType='string' Win64='no' />"
220 L" <RegistrySearch Id='Search22' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable22' VariableType='string' Win64='yes' />" 220 L" <RegistrySearch Id='Search22' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable22' VariableType='string' Win64='yes' />"
221 L" <RegistrySearch Id='Search23' Type='exists' Root='HKU' Key='.DEFAULT\\Environment' Variable='Variable23' />" 221 L" <RegistrySearch Id='Search23' Type='exists' Root='HKU' Key='.DEFAULT\\Environment' Variable='Variable23' />"
222 L" <RegistrySearch Id='Search23' Type='exists' Root='HKU' Key='.DEFAULT\\System\\NetworkServiceSidSubkeyDoesNotExist' Variable='Variable24' />" 222 L" <RegistrySearch Id='Search24' Type='exists' Root='HKU' Key='.DEFAULT\\System\\NetworkServiceSidSubkeyDoesNotExist' Variable='Variable24' />"
223 L" <RegistrySearch Id='Search24' Type='value' Root='HKCR' Key='.msi' Variable='Variable25' VariableType='string' />" 223 L" <RegistrySearch Id='Search25' Type='value' Root='HKCR' Key='.msi' Variable='Variable25' VariableType='string' />"
224 L" <RegistrySearch Id='Search26' Type='value' Root='HKCR' Key='.msi' Variable='Variable26' VariableType='formatted' />"
224 L"</Bundle>"; 225 L"</Bundle>";
225 226
226 // load XML document 227 // load XML document
@@ -263,6 +264,7 @@ namespace Bootstrapper
263 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23")); 264 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23"));
264 Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24")); 265 Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24"));
265 Assert::Equal<String^>(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); 266 Assert::Equal<String^>(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25"));
267 Assert::Equal<String^>(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable26"));
266 } 268 }
267 finally 269 finally
268 { 270 {
@@ -525,6 +527,75 @@ namespace Bootstrapper
525 SearchesUninitialize(&searches); 527 SearchesUninitialize(&searches);
526 } 528 }
527 } 529 }
530
531 [Fact]
532 void SetVariableSearchTest()
533 {
534 HRESULT hr = S_OK;
535 IXMLDOMElement* pixeBundle = NULL;
536 BURN_VARIABLES variables = { };
537 BURN_SEARCHES searches = { };
538 BURN_EXTENSIONS burnExtensions = { };
539 try
540 {
541 LPCWSTR wzDocument =
542 L"<Bundle>"
543 L" <SetVariable Id='Search1' Type='string' Value='VAL1' Variable='PROP1' />"
544 L" <SetVariable Id='Search2' Type='numeric' Value='2' Variable='PROP2' />"
545 L" <SetVariable Id='Search3' Type='string' Value='VAL3' Variable='PROP3' />"
546 L" <SetVariable Id='Search4' Type='string' Value='VAL4' Variable='PROP4' />"
547 L" <SetVariable Id='Search5' Type='string' Value='VAL5' Variable='PROP5' />"
548 L" <SetVariable Id='Search6' Type='string' Value='VAL6' Variable='PROP6' />"
549 L" <SetVariable Id='Search7' Type='string' Value='7' Variable='PROP7' />"
550 L" <SetVariable Id='Search8' Type='version' Value='1.1.0.0' Variable='PROP8' />"
551 L" <SetVariable Id='Search9' Type='formatted' Value='[VAL9]' Variable='PROP9' />"
552 L" <SetVariable Id='Search10' Type='numeric' Value='42' Variable='OVERWRITTEN_STRING' />"
553 L" <SetVariable Id='Search11' Type='string' Value='NEW' Variable='OVERWRITTEN_NUMBER' />"
554 L" <SetVariable Id='Search12' Variable='REMOVED_NUMBER' />"
555 L"</Bundle>";
556
557 hr = VariableInitialize(&variables);
558 TestThrowOnFailure(hr, L"Failed to initialize variables.");
559
560 // set variables
561 VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE);
562 VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5);
563 VariableSetNumericHelper(&variables, L"REMOVED_NUMBER", 22);
564
565 // load XML document
566 LoadBundleXmlHelper(wzDocument, &pixeBundle);
567
568 hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle);
569 TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
570
571 // execute searches
572 hr = SearchesExecute(&searches, &variables);
573 TestThrowOnFailure(hr, L"Failed to execute searches.");
574
575 // check variable values
576 Assert::Equal<String^>(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1"));
577 Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2"));
578 Assert::Equal<String^>(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2"));
579 Assert::Equal<String^>(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3"));
580 Assert::Equal<String^>(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4"));
581 Assert::Equal<String^>(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5"));
582 Assert::Equal<String^>(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6"));
583 Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7"));
584 Assert::Equal(MAKEQWORDVERSION(1, 1, 0, 0), VariableGetVersionHelper(&variables, L"PROP8"));
585 Assert::Equal<String^>(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8"));
586 Assert::Equal<String^>(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9"));
587
588 Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING"));
589 Assert::Equal<String^>(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER"));
590 Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"REMOVED_NUMBER"));
591 }
592 finally
593 {
594 ReleaseObject(pixeBundle);
595 VariablesUninitialize(&variables);
596 SearchesUninitialize(&searches);
597 }
598 }
528 }; 599 };
529} 600}
530} 601}
diff --git a/src/test/BurnUnitTest/VariableHelpers.cpp b/src/test/BurnUnitTest/VariableHelpers.cpp
index 9ce46a76..fdfb9191 100644
--- a/src/test/BurnUnitTest/VariableHelpers.cpp
+++ b/src/test/BurnUnitTest/VariableHelpers.cpp
@@ -17,11 +17,11 @@ namespace Test
17{ 17{
18namespace Bootstrapper 18namespace Bootstrapper
19{ 19{
20 void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) 20 void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted)
21 { 21 {
22 HRESULT hr = S_OK; 22 HRESULT hr = S_OK;
23 23
24 hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE); 24 hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE, fFormatted);
25 TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue); 25 TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue);
26 } 26 }
27 27
diff --git a/src/test/BurnUnitTest/VariableHelpers.h b/src/test/BurnUnitTest/VariableHelpers.h
index 98c52649..8c2b081a 100644
--- a/src/test/BurnUnitTest/VariableHelpers.h
+++ b/src/test/BurnUnitTest/VariableHelpers.h
@@ -14,7 +14,7 @@ namespace Bootstrapper
14{ 14{
15 15
16 16
17void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); 17void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted);
18void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); 18void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue);
19void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue); 19void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue);
20System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); 20System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp
index 7d670744..0b49a530 100644
--- a/src/test/BurnUnitTest/VariableTest.cpp
+++ b/src/test/BurnUnitTest/VariableTest.cpp
@@ -35,21 +35,22 @@ namespace Bootstrapper
35 TestThrowOnFailure(hr, L"Failed to initialize variables."); 35 TestThrowOnFailure(hr, L"Failed to initialize variables.");
36 36
37 // set variables 37 // set variables
38 VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); 38 VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE);
39 VariableSetNumericHelper(&variables, L"PROP2", 2); 39 VariableSetNumericHelper(&variables, L"PROP2", 2);
40 VariableSetStringHelper(&variables, L"PROP5", L"VAL5"); 40 VariableSetStringHelper(&variables, L"PROP5", L"VAL5", FALSE);
41 VariableSetStringHelper(&variables, L"PROP3", L"VAL3"); 41 VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE);
42 VariableSetStringHelper(&variables, L"PROP4", L"VAL4"); 42 VariableSetStringHelper(&variables, L"PROP4", L"VAL4", FALSE);
43 VariableSetStringHelper(&variables, L"PROP6", L"VAL6"); 43 VariableSetStringHelper(&variables, L"PROP6", L"VAL6", FALSE);
44 VariableSetStringHelper(&variables, L"PROP7", L"7"); 44 VariableSetStringHelper(&variables, L"PROP7", L"7", FALSE);
45 VariableSetVersionHelper(&variables, L"PROP8", MAKEQWORDVERSION(1,1,0,0)); 45 VariableSetVersionHelper(&variables, L"PROP8", MAKEQWORDVERSION(1,1,0,0));
46 VariableSetStringHelper(&variables, L"PROP9", L"[VAL9]", TRUE);
46 47
47 // set overwritten variables 48 // set overwritten variables
48 VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL"); 49 VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE);
49 VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42); 50 VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42);
50 51
51 VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); 52 VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5);
52 VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW"); 53 VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW", FALSE);
53 54
54 // get and verify variable values 55 // get and verify variable values
55 Assert::Equal<String^>(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); 56 Assert::Equal<String^>(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1"));
@@ -62,6 +63,7 @@ namespace Bootstrapper
62 Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); 63 Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7"));
63 Assert::Equal(MAKEQWORDVERSION(1,1,0,0), VariableGetVersionHelper(&variables, L"PROP8")); 64 Assert::Equal(MAKEQWORDVERSION(1,1,0,0), VariableGetVersionHelper(&variables, L"PROP8"));
64 Assert::Equal<String^>(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); 65 Assert::Equal<String^>(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8"));
66 Assert::Equal<String^>(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9"));
65 67
66 Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); 68 Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING"));
67 Assert::Equal<String^>(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); 69 Assert::Equal<String^>(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER"));
@@ -87,6 +89,7 @@ namespace Bootstrapper
87 L" <Variable Id='Var3' Type='version' Value='1.2.3.4' Hidden='no' Persisted='no' />" 89 L" <Variable Id='Var3' Type='version' Value='1.2.3.4' Hidden='no' Persisted='no' />"
88 L" <Variable Id='Var4' Hidden='no' Persisted='no' />" 90 L" <Variable Id='Var4' Hidden='no' Persisted='no' />"
89 L" <Variable Id='Var5' Type='string' Value='' Hidden='no' Persisted='no' />" 91 L" <Variable Id='Var5' Type='string' Value='' Hidden='no' Persisted='no' />"
92 L" <Variable Id='Var6' Type='formatted' Value='[Formatted]' Hidden='no' Persisted='no' />"
90 L"</Bundle>"; 93 L"</Bundle>";
91 94
92 hr = VariableInitialize(&variables); 95 hr = VariableInitialize(&variables);
@@ -103,10 +106,12 @@ namespace Bootstrapper
103 Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2")); 106 Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2"));
104 Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3")); 107 Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3"));
105 Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4")); 108 Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4"));
109 Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables, L"Var6"));
106 110
107 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); 111 Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1"));
108 Assert::Equal<String^>(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); 112 Assert::Equal<String^>(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2"));
109 Assert::Equal(MAKEQWORDVERSION(1,2,3,4), VariableGetVersionHelper(&variables, L"Var3")); 113 Assert::Equal(MAKEQWORDVERSION(1,2,3,4), VariableGetVersionHelper(&variables, L"Var3"));
114 Assert::Equal<String^>(gcnew String(L"[Formatted]"), VariableGetStringHelper(&variables, L"Var6"));
110 } 115 }
111 finally 116 finally
112 { 117 {
@@ -128,9 +133,13 @@ namespace Bootstrapper
128 TestThrowOnFailure(hr, L"Failed to initialize variables."); 133 TestThrowOnFailure(hr, L"Failed to initialize variables.");
129 134
130 // set variables 135 // set variables
131 VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); 136 VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE);
132 VariableSetStringHelper(&variables, L"PROP2", L"VAL2"); 137 VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE);
133 VariableSetNumericHelper(&variables, L"PROP3", 3); 138 VariableSetNumericHelper(&variables, L"PROP3", 3);
139 VariableSetStringHelper(&variables, L"PROP4", L"[PROP1]", FALSE);
140 VariableSetStringHelper(&variables, L"PROP5", L"[PROP2]", FALSE);
141 VariableSetStringHelper(&variables, L"PROP6", L"[PROP4]", TRUE);
142 VariableSetStringHelper(&variables, L"PROP7", L"[PROP5]", TRUE);
134 143
135 // test string formatting 144 // test string formatting
136 Assert::Equal<String^>(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); 145 Assert::Equal<String^>(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP"));
@@ -148,6 +157,10 @@ namespace Bootstrapper
148 Assert::Equal<String^>(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); 157 Assert::Equal<String^>(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE"));
149 Assert::Equal<String^>(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2")); 158 Assert::Equal<String^>(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2"));
150 Assert::Equal<String^>(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3")); 159 Assert::Equal<String^>(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3"));
160 Assert::Equal<String^>(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP4"));
161 Assert::Equal<String^>(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP5"));
162 Assert::Equal<String^>(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP6"));
163 Assert::Equal<String^>(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP7"));
151 164
152 hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); 165 hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch);
153 TestThrowOnFailure(hr, L"Failed to format string"); 166 TestThrowOnFailure(hr, L"Failed to format string");
@@ -186,15 +199,15 @@ namespace Bootstrapper
186 TestThrowOnFailure(hr, L"Failed to initialize variables."); 199 TestThrowOnFailure(hr, L"Failed to initialize variables.");
187 200
188 // set variables 201 // set variables
189 VariableSetStringHelper(&variables, L"PROP1", L"VAL1"); 202 VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE);
190 VariableSetStringHelper(&variables, L"PROP2", L"VAL2"); 203 VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE);
191 VariableSetStringHelper(&variables, L"PROP3", L"VAL3"); 204 VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE);
192 VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END"); 205 VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END", FALSE);
193 VariableSetNumericHelper(&variables, L"PROP5", 5); 206 VariableSetNumericHelper(&variables, L"PROP5", 5);
194 VariableSetNumericHelper(&variables, L"PROP6", 6); 207 VariableSetNumericHelper(&variables, L"PROP6", 6);
195 VariableSetStringHelper(&variables, L"PROP7", L""); 208 VariableSetStringHelper(&variables, L"PROP7", L"", FALSE);
196 VariableSetNumericHelper(&variables, L"PROP8", 0); 209 VariableSetNumericHelper(&variables, L"PROP8", 0);
197 VariableSetStringHelper(&variables, L"_PROP9", L"VAL9"); 210 VariableSetStringHelper(&variables, L"_PROP9", L"VAL9", FALSE);
198 VariableSetNumericHelper(&variables, L"PROP10", -10); 211 VariableSetNumericHelper(&variables, L"PROP10", -10);
199 VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll); 212 VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll);
200 VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll); 213 VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll);
@@ -208,7 +221,11 @@ namespace Bootstrapper
208 VariableSetVersionHelper(&variables, L"PROP20", MAKEQWORDVERSION(1,1,1,1)); 221 VariableSetVersionHelper(&variables, L"PROP20", MAKEQWORDVERSION(1,1,1,1));
209 VariableSetNumericHelper(&variables, L"vPROP21", 1); 222 VariableSetNumericHelper(&variables, L"vPROP21", 1);
210 VariableSetVersionHelper(&variables, L"PROP22", MAKEQWORDVERSION(65535,65535,65535,65535)); 223 VariableSetVersionHelper(&variables, L"PROP22", MAKEQWORDVERSION(65535,65535,65535,65535));
211 VariableSetStringHelper(&variables, L"PROP23", L"1.1.1"); 224 VariableSetStringHelper(&variables, L"PROP23", L"1.1.1", FALSE);
225 VariableSetStringHelper(&variables, L"PROP24", L"[PROP1]", TRUE);
226 VariableSetStringHelper(&variables, L"PROP25", L"[PROP7]", TRUE);
227 VariableSetStringHelper(&variables, L"PROP26", L"[PROP8]", TRUE);
228 VariableSetStringHelper(&variables, L"PROP27", L"[PROP16]", TRUE);
212 229
213 // test conditions 230 // test conditions
214 Assert::True(EvaluateConditionHelper(&variables, L"PROP1")); 231 Assert::True(EvaluateConditionHelper(&variables, L"PROP1"));
@@ -218,6 +235,10 @@ namespace Bootstrapper
218 Assert::True(EvaluateConditionHelper(&variables, L"_PROP9")); 235 Assert::True(EvaluateConditionHelper(&variables, L"_PROP9"));
219 Assert::False(EvaluateConditionHelper(&variables, L"PROP16")); 236 Assert::False(EvaluateConditionHelper(&variables, L"PROP16"));
220 Assert::True(EvaluateConditionHelper(&variables, L"PROP17")); 237 Assert::True(EvaluateConditionHelper(&variables, L"PROP17"));
238 Assert::True(EvaluateConditionHelper(&variables, L"PROP24"));
239 Assert::True(EvaluateConditionHelper(&variables, L"PROP25"));
240 Assert::True(EvaluateConditionHelper(&variables, L"PROP26"));
241 Assert::True(EvaluateConditionHelper(&variables, L"PROP27"));
221 242
222 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\"")); 243 Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\""));
223 Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\"")); 244 Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\""));
@@ -365,10 +386,11 @@ namespace Bootstrapper
365 hr = VariableInitialize(&variables1); 386 hr = VariableInitialize(&variables1);
366 TestThrowOnFailure(hr, L"Failed to initialize variables."); 387 TestThrowOnFailure(hr, L"Failed to initialize variables.");
367 388
368 VariableSetStringHelper(&variables1, L"PROP1", L"VAL1"); 389 VariableSetStringHelper(&variables1, L"PROP1", L"VAL1", FALSE);
369 VariableSetNumericHelper(&variables1, L"PROP2", 2); 390 VariableSetNumericHelper(&variables1, L"PROP2", 2);
370 VariableSetVersionHelper(&variables1, L"PROP3", MAKEQWORDVERSION(1,1,1,1)); 391 VariableSetVersionHelper(&variables1, L"PROP3", MAKEQWORDVERSION(1,1,1,1));
371 VariableSetStringHelper(&variables1, L"PROP4", L"VAL4"); 392 VariableSetStringHelper(&variables1, L"PROP4", L"VAL4", FALSE);
393 VariableSetStringHelper(&variables1, L"PROP5", L"[PROP1]", TRUE);
372 394
373 hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer); 395 hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer);
374 TestThrowOnFailure(hr, L"Failed to serialize variables."); 396 TestThrowOnFailure(hr, L"Failed to serialize variables.");
@@ -384,6 +406,13 @@ namespace Bootstrapper
384 Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); 406 Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2"));
385 Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables2, L"PROP3")); 407 Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables2, L"PROP3"));
386 Assert::Equal<String^>(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); 408 Assert::Equal<String^>(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4"));
409 Assert::Equal<String^>(gcnew String(L"[PROP1]"), VariableGetStringHelper(&variables2, L"PROP5"));
410
411 Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP1"));
412 Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables2, L"PROP2"));
413 Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables2, L"PROP3"));
414 Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP4"));
415 Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables2, L"PROP5"));
387 } 416 }
388 finally 417 finally
389 { 418 {
@@ -420,7 +449,7 @@ namespace Bootstrapper
420 } 449 }
421 450
422 // attempt to set a built-in property 451 // attempt to set a built-in property
423 hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE); 452 hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE, FALSE);
424 Assert::Equal(E_INVALIDARG, hr); 453 Assert::Equal(E_INVALIDARG, hr);
425 Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\"")); 454 Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\""));
426 455
diff --git a/src/test/BurnUnitTest/VariantTest.cpp b/src/test/BurnUnitTest/VariantTest.cpp
index d16ac699..c982db72 100644
--- a/src/test/BurnUnitTest/VariantTest.cpp
+++ b/src/test/BurnUnitTest/VariantTest.cpp
@@ -25,9 +25,9 @@ namespace Bootstrapper
25 [Fact] 25 [Fact]
26 void VariantBasicTest() 26 void VariantBasicTest()
27 { 27 {
28 BURN_VARIANT expectedVariants[8]; 28 BURN_VARIANT expectedVariants[10];
29 BURN_VARIANT actualVariants[8]; 29 BURN_VARIANT actualVariants[10];
30 for (DWORD i = 0; i < 8; i++) 30 for (DWORD i = 0; i < 10; i++)
31 { 31 {
32 BVariantUninitialize(expectedVariants + i); 32 BVariantUninitialize(expectedVariants + i);
33 BVariantUninitialize(actualVariants + i); 33 BVariantUninitialize(actualVariants + i);
@@ -43,6 +43,8 @@ namespace Bootstrapper
43 InitVersionValue(expectedVariants + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, L"PROP6", actualVariants + 5); 43 InitVersionValue(expectedVariants + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, L"PROP6", actualVariants + 5);
44 InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6); 44 InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6);
45 InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7); 45 InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7);
46 InitFormattedValue(expectedVariants + 8, L"VAL9", FALSE, L"PROP9", actualVariants + 8);
47 InitFormattedValue(expectedVariants + 9, L"VAL10", TRUE, L"PROP10", actualVariants + 9);
46 48
47 VerifyNumericValue(expectedVariants + 0, actualVariants + 0); 49 VerifyNumericValue(expectedVariants + 0, actualVariants + 0);
48 VerifyStringValue(expectedVariants + 1, actualVariants + 1); 50 VerifyStringValue(expectedVariants + 1, actualVariants + 1);
@@ -52,10 +54,12 @@ namespace Bootstrapper
52 VerifyVersionValue(expectedVariants + 5, actualVariants + 5); 54 VerifyVersionValue(expectedVariants + 5, actualVariants + 5);
53 VerifyStringValue(expectedVariants + 6, actualVariants + 6); 55 VerifyStringValue(expectedVariants + 6, actualVariants + 6);
54 VerifyNumericValue(expectedVariants + 7, actualVariants + 7); 56 VerifyNumericValue(expectedVariants + 7, actualVariants + 7);
57 VerifyFormattedValue(expectedVariants + 8, actualVariants + 8);
58 VerifyFormattedValue(expectedVariants + 9, actualVariants + 9);
55 } 59 }
56 finally 60 finally
57 { 61 {
58 for (DWORD i = 0; i < 8; i++) 62 for (DWORD i = 0; i < 10; i++)
59 { 63 {
60 BVariantUninitialize(expectedVariants + i); 64 BVariantUninitialize(expectedVariants + i);
61 BVariantUninitialize(actualVariants + i); 65 BVariantUninitialize(actualVariants + i);
@@ -64,6 +68,26 @@ namespace Bootstrapper
64 } 68 }
65 69
66 private: 70 private:
71 void InitFormattedValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue)
72 {
73 HRESULT hr = S_OK;
74 pValue->Type = BURN_VARIANT_TYPE_FORMATTED;
75
76 hr = StrAllocString(&pValue->sczValue, wzValue, 0);
77 NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue);
78
79 hr = BVariantCopy(pValue, pActualValue);
80 NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz);
81
82 if (fHidden)
83 {
84 hr = BVariantSetEncryption(pActualValue, TRUE);
85 NativeAssert::Succeeded(hr, "Failed to encrypt variant {0}", wz);
86
87 NativeAssert::True(pActualValue->fEncryptString);
88 }
89 }
90
67 void InitNoneValue(BURN_VARIANT* pValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue) 91 void InitNoneValue(BURN_VARIANT* pValue, BOOL fHidden, LPCWSTR wz, BURN_VARIANT* pActualValue)
68 { 92 {
69 HRESULT hr = S_OK; 93 HRESULT hr = S_OK;
@@ -137,6 +161,26 @@ namespace Bootstrapper
137 } 161 }
138 } 162 }
139 163
164 void VerifyFormattedValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue)
165 {
166 HRESULT hr = S_OK;
167 LPWSTR sczValue = NULL;
168 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_FORMATTED, pExpectedValue->Type);
169 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_FORMATTED, pActualValue->Type);
170
171 try
172 {
173 hr = BVariantGetString(pActualValue, &sczValue);
174 NativeAssert::Succeeded(hr, "Failed to get string value");
175
176 NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue);
177 }
178 finally
179 {
180 ReleaseStr(sczValue);
181 }
182 }
183
140 void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) 184 void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue)
141 { 185 {
142 HRESULT hr = S_OK; 186 HRESULT hr = S_OK;
@@ -167,7 +211,7 @@ namespace Bootstrapper
167 try 211 try
168 { 212 {
169 hr = BVariantGetString(pActualValue, &sczValue); 213 hr = BVariantGetString(pActualValue, &sczValue);
170 NativeAssert::Succeeded(hr, "Failed to get numeric value"); 214 NativeAssert::Succeeded(hr, "Failed to get string value");
171 215
172 NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); 216 NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue);
173 } 217 }
@@ -185,7 +229,7 @@ namespace Bootstrapper
185 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_VERSION, pActualValue->Type); 229 NativeAssert::Equal<DWORD>(BURN_VARIANT_TYPE_VERSION, pActualValue->Type);
186 230
187 hr = BVariantGetVersion(pActualValue, &qwValue); 231 hr = BVariantGetVersion(pActualValue, &qwValue);
188 NativeAssert::Succeeded(hr, "Failed to get numeric value"); 232 NativeAssert::Succeeded(hr, "Failed to get version value");
189 233
190 NativeAssert::Equal<DWORD64>(pExpectedValue->qwValue, qwValue); 234 NativeAssert::Equal<DWORD64>(pExpectedValue->qwValue, qwValue);
191 } 235 }