aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BootstrapperCommand.cs52
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BundleInfo.cs5
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperCommand.cs16
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBundleInfo.cs5
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IMbaCommand.cs30
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IOverridableVariableInfo.cs15
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IOverridableVariables.cs17
-rw-r--r--src/api/burn/WixToolset.Mba.Core/MbaCommand.cs33
-rw-r--r--src/api/burn/WixToolset.Mba.Core/OverridableVariableInfo.cs15
-rw-r--r--src/api/burn/WixToolset.Mba.Core/OverridableVariables.cs51
-rw-r--r--src/api/burn/balutil/balinfo.cpp203
-rw-r--r--src/api/burn/balutil/inc/balinfo.h55
-rw-r--r--src/api/burn/balutil/precomp.h1
-rw-r--r--src/api/burn/test/WixToolsetTest.Mba.Core/BaseBootstrapperApplicationFactoryFixture.cs13
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp131
-rw-r--r--src/test/burn/TestBA/TestBA.cs6
-rw-r--r--src/test/burn/WixToolset.WixBA/InstallationViewModel.cs12
17 files changed, 539 insertions, 121 deletions
diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperCommand.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperCommand.cs
index 65dde0f4..345e0448 100644
--- a/src/api/burn/WixToolset.Mba.Core/BootstrapperCommand.cs
+++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperCommand.cs
@@ -3,6 +3,7 @@
3namespace WixToolset.Mba.Core 3namespace WixToolset.Mba.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
6 using System.ComponentModel; 7 using System.ComponentModel;
7 using System.Runtime.InteropServices; 8 using System.Runtime.InteropServices;
8 9
@@ -11,8 +12,6 @@ namespace WixToolset.Mba.Core
11 /// </summary> 12 /// </summary>
12 public sealed class BootstrapperCommand : IBootstrapperCommand 13 public sealed class BootstrapperCommand : IBootstrapperCommand
13 { 14 {
14 private readonly string commandLine;
15
16 /// <summary> 15 /// <summary>
17 /// 16 ///
18 /// </summary> 17 /// </summary>
@@ -45,7 +44,7 @@ namespace WixToolset.Mba.Core
45 this.Action = action; 44 this.Action = action;
46 this.Display = display; 45 this.Display = display;
47 this.Restart = restart; 46 this.Restart = restart;
48 this.commandLine = commandLine; 47 this.CommandLine = commandLine;
49 this.CmdShow = cmdShow; 48 this.CmdShow = cmdShow;
50 this.Resume = resume; 49 this.Resume = resume;
51 this.SplashScreen = splashScreen; 50 this.SplashScreen = splashScreen;
@@ -66,7 +65,7 @@ namespace WixToolset.Mba.Core
66 public Restart Restart { get; } 65 public Restart Restart { get; }
67 66
68 /// <inheritdoc/> 67 /// <inheritdoc/>
69 public string[] CommandLineArgs => GetCommandLineArgs(this.commandLine); 68 public string CommandLine { get; }
70 69
71 /// <inheritdoc/> 70 /// <inheritdoc/>
72 public int CmdShow { get; } 71 public int CmdShow { get; }
@@ -92,6 +91,49 @@ namespace WixToolset.Mba.Core
92 /// <inheritdoc/> 91 /// <inheritdoc/>
93 public string BootstrapperApplicationDataPath { get; } 92 public string BootstrapperApplicationDataPath { get; }
94 93
94 /// <inheritdoc/>
95 public IMbaCommand ParseCommandLine()
96 {
97 var args = ParseCommandLineToArgs(this.CommandLine);
98 var unknownArgs = new List<string>();
99 var variables = new List<KeyValuePair<string, string>>();
100
101 foreach (var arg in args)
102 {
103 var unknownArg = false;
104
105 if (arg[0] == '-' || arg[0] == '/')
106 {
107 unknownArg = true;
108 }
109 else
110 {
111 var index = arg.IndexOf('=');
112 if (index == -1)
113 {
114 unknownArg = true;
115 }
116 else
117 {
118 var name = arg.Substring(0, index);
119 var value = arg.Substring(index + 1);
120 variables.Add(new KeyValuePair<string, string>(name, value));
121 }
122 }
123
124 if (unknownArg)
125 {
126 unknownArgs.Add(arg);
127 }
128 }
129
130 return new MbaCommand
131 {
132 UnknownCommandLineArgs = unknownArgs.ToArray(),
133 Variables = variables.ToArray(),
134 };
135 }
136
95 /// <summary> 137 /// <summary>
96 /// Gets the command line arguments as a string array. 138 /// Gets the command line arguments as a string array.
97 /// </summary> 139 /// </summary>
@@ -102,7 +144,7 @@ namespace WixToolset.Mba.Core
102 /// <remarks> 144 /// <remarks>
103 /// This method uses the same parsing as the operating system which handles quotes and spaces correctly. 145 /// This method uses the same parsing as the operating system which handles quotes and spaces correctly.
104 /// </remarks> 146 /// </remarks>
105 public static string[] GetCommandLineArgs(string commandLine) 147 public static string[] ParseCommandLineToArgs(string commandLine)
106 { 148 {
107 if (null == commandLine) 149 if (null == commandLine)
108 { 150 {
diff --git a/src/api/burn/WixToolset.Mba.Core/BundleInfo.cs b/src/api/burn/WixToolset.Mba.Core/BundleInfo.cs
index 3d5d535d..4a533bf9 100644
--- a/src/api/burn/WixToolset.Mba.Core/BundleInfo.cs
+++ b/src/api/burn/WixToolset.Mba.Core/BundleInfo.cs
@@ -23,6 +23,9 @@ namespace WixToolset.Mba.Core
23 public string LogVariable { get; internal set; } 23 public string LogVariable { get; internal set; }
24 24
25 /// <inheritdoc/> 25 /// <inheritdoc/>
26 public IOverridableVariables OverridableVariables { get; internal set; }
27
28 /// <inheritdoc/>
26 public IDictionary<string, IPackageInfo> Packages { get; internal set; } 29 public IDictionary<string, IPackageInfo> Packages { get; internal set; }
27 30
28 internal BundleInfo() 31 internal BundleInfo()
@@ -78,6 +81,8 @@ namespace WixToolset.Mba.Core
78 81
79 bundle.LogVariable = BootstrapperApplicationData.GetAttribute(bundleNode, "LogPathVariable"); 82 bundle.LogVariable = BootstrapperApplicationData.GetAttribute(bundleNode, "LogPathVariable");
80 83
84 bundle.OverridableVariables = OverridableVariablesInfo.ParseFromXml(root);
85
81 bundle.Packages = PackageInfo.ParsePackagesFromXml(root); 86 bundle.Packages = PackageInfo.ParsePackagesFromXml(root);
82 87
83 return bundle; 88 return bundle;
diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperCommand.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperCommand.cs
index e861813f..b7baa55c 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperCommand.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperCommand.cs
@@ -25,13 +25,12 @@ namespace WixToolset.Mba.Core
25 Restart Restart { get; } 25 Restart Restart { get; }
26 26
27 /// <summary> 27 /// <summary>
28 /// Gets the command line arguments as a string array. 28 /// Gets the command line arguments.
29 /// </summary> 29 /// </summary>
30 /// <returns> 30 /// <returns>
31 /// Array of command line arguments not handled by the engine. 31 /// Command line arguments not handled by the engine.
32 /// </returns> 32 /// </returns>
33 /// <exception type="Win32Exception">The command line could not be parsed into an array.</exception> 33 string CommandLine { get; }
34 string[] CommandLineArgs { get; }
35 34
36 /// <summary> 35 /// <summary>
37 /// Hint for the initial visibility of the window. 36 /// Hint for the initial visibility of the window.
@@ -72,5 +71,14 @@ namespace WixToolset.Mba.Core
72 /// Gets path to BootstrapperApplicationData.xml. 71 /// Gets path to BootstrapperApplicationData.xml.
73 /// </summary> 72 /// </summary>
74 string BootstrapperApplicationDataPath { get; } 73 string BootstrapperApplicationDataPath { get; }
74
75 /// <summary>
76 /// Parses the command line arguments into an <see cref="IMbaCommand"/>.
77 /// </summary>
78 /// <returns>
79 /// The parsed information.
80 /// </returns>
81 /// <exception type="Win32Exception">The command line could not be parsed.</exception>
82 IMbaCommand ParseCommandLine();
75 } 83 }
76} 84}
diff --git a/src/api/burn/WixToolset.Mba.Core/IBundleInfo.cs b/src/api/burn/WixToolset.Mba.Core/IBundleInfo.cs
index f4a82f36..35decc88 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBundleInfo.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBundleInfo.cs
@@ -22,6 +22,11 @@ namespace WixToolset.Mba.Core
22 /// <summary> 22 /// <summary>
23 /// 23 ///
24 /// </summary> 24 /// </summary>
25 IOverridableVariables OverridableVariables { get; }
26
27 /// <summary>
28 ///
29 /// </summary>
25 IDictionary<string, IPackageInfo> Packages { get; } 30 IDictionary<string, IPackageInfo> Packages { get; }
26 31
27 /// <summary> 32 /// <summary>
diff --git a/src/api/burn/WixToolset.Mba.Core/IMbaCommand.cs b/src/api/burn/WixToolset.Mba.Core/IMbaCommand.cs
new file mode 100644
index 00000000..a3ad68d8
--- /dev/null
+++ b/src/api/burn/WixToolset.Mba.Core/IMbaCommand.cs
@@ -0,0 +1,30 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Mba.Core
4{
5 using System.Collections.Generic;
6
7 /// <summary>
8 /// Command information parsed from the command line.
9 /// </summary>
10 public interface IMbaCommand
11 {
12 /// <summary>
13 /// The command line arguments not parsed into <see cref="IBootstrapperCommand"/> or <see cref="IMbaCommand"/>.
14 /// </summary>
15 string[] UnknownCommandLineArgs { get; }
16
17 /// <summary>
18 /// The variables that were parsed from the command line.
19 /// Key = variable name, Value = variable value.
20 /// </summary>
21 KeyValuePair<string, string>[] Variables { get; }
22
23 /// <summary>
24 /// Sets overridable variables from the command line.
25 /// </summary>
26 /// <param name="overridableVariables">The overridable variable information from <see cref="IBootstrapperApplicationData"/>.</param>
27 /// <param name="engine">The engine.</param>
28 void SetOverridableVariables(IOverridableVariables overridableVariables, IEngine engine);
29 }
30}
diff --git a/src/api/burn/WixToolset.Mba.Core/IOverridableVariableInfo.cs b/src/api/burn/WixToolset.Mba.Core/IOverridableVariableInfo.cs
new file mode 100644
index 00000000..d01dead3
--- /dev/null
+++ b/src/api/burn/WixToolset.Mba.Core/IOverridableVariableInfo.cs
@@ -0,0 +1,15 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Mba.Core
4{
5 /// <summary>
6 /// Overridable variable from the BA manifest.
7 /// </summary>
8 public interface IOverridableVariableInfo
9 {
10 /// <summary>
11 /// The Variable name.
12 /// </summary>
13 string Name { get; }
14 }
15} \ No newline at end of file
diff --git a/src/api/burn/WixToolset.Mba.Core/IOverridableVariables.cs b/src/api/burn/WixToolset.Mba.Core/IOverridableVariables.cs
new file mode 100644
index 00000000..3944913b
--- /dev/null
+++ b/src/api/burn/WixToolset.Mba.Core/IOverridableVariables.cs
@@ -0,0 +1,17 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Mba.Core
4{
5 using System.Collections.Generic;
6
7 /// <summary>
8 /// Overridable variable information from the BA manifest.
9 /// </summary>
10 public interface IOverridableVariables
11 {
12 /// <summary>
13 /// Variable Dictionary of variable name to <see cref="IOverridableVariableInfo"/>.
14 /// </summary>
15 IDictionary<string, IOverridableVariableInfo> Variables { get; }
16 }
17} \ No newline at end of file
diff --git a/src/api/burn/WixToolset.Mba.Core/MbaCommand.cs b/src/api/burn/WixToolset.Mba.Core/MbaCommand.cs
new file mode 100644
index 00000000..e7e49607
--- /dev/null
+++ b/src/api/burn/WixToolset.Mba.Core/MbaCommand.cs
@@ -0,0 +1,33 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Mba.Core
4{
5 using System.Collections.Generic;
6
7 /// <summary>
8 /// Default implementation of <see cref="IMbaCommand"/>.
9 /// </summary>
10 internal sealed class MbaCommand : IMbaCommand
11 {
12 public string[] UnknownCommandLineArgs { get; internal set; }
13
14 public KeyValuePair<string, string>[] Variables { get; internal set; }
15
16 internal MbaCommand() { }
17
18 public void SetOverridableVariables(IOverridableVariables overridableVariables, IEngine engine)
19 {
20 foreach (var kvp in this.Variables)
21 {
22 if (!overridableVariables.Variables.TryGetValue(kvp.Key, out var overridableVariable))
23 {
24 engine.Log(LogLevel.Error, string.Format("Ignoring attempt to set non-overridable variable: '{0}'.", kvp.Key));
25 }
26 else
27 {
28 engine.SetVariableString(overridableVariable.Name, kvp.Value, false);
29 }
30 }
31 }
32 }
33}
diff --git a/src/api/burn/WixToolset.Mba.Core/OverridableVariableInfo.cs b/src/api/burn/WixToolset.Mba.Core/OverridableVariableInfo.cs
new file mode 100644
index 00000000..b8e85c34
--- /dev/null
+++ b/src/api/burn/WixToolset.Mba.Core/OverridableVariableInfo.cs
@@ -0,0 +1,15 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Mba.Core
4{
5 /// <summary>
6 /// Default implementation of <see cref="IOverridableVariableInfo"/>.
7 /// </summary>
8 internal class OverridableVariableInfo : IOverridableVariableInfo
9 {
10 /// <inheritdoc />
11 public string Name { get; internal set; }
12
13 internal OverridableVariableInfo() { }
14 }
15}
diff --git a/src/api/burn/WixToolset.Mba.Core/OverridableVariables.cs b/src/api/burn/WixToolset.Mba.Core/OverridableVariables.cs
new file mode 100644
index 00000000..855ce9a9
--- /dev/null
+++ b/src/api/burn/WixToolset.Mba.Core/OverridableVariables.cs
@@ -0,0 +1,51 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Mba.Core
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Xml;
8 using System.Xml.XPath;
9
10 /// <summary>
11 /// Default implementation of <see cref="IOverridableVariables"/>.
12 /// </summary>
13 public class OverridableVariablesInfo : IOverridableVariables
14 {
15 /// <inheritdoc />
16 public IDictionary<string, IOverridableVariableInfo> Variables { get; internal set; }
17
18 internal OverridableVariablesInfo() { }
19
20 /// <summary>
21 /// Parses the overridable variable info from the BA manifest.
22 /// </summary>
23 /// <param name="root">XML root</param>
24 /// <returns>The parsed information.</returns>
25 public static IOverridableVariables ParseFromXml(XPathNavigator root)
26 {
27 XmlNamespaceManager namespaceManager = new XmlNamespaceManager(root.NameTable);
28 namespaceManager.AddNamespace("p", BootstrapperApplicationData.XMLNamespace);
29 XPathNodeIterator nodes = root.Select("/p:BootstrapperApplicationData/p:WixStdbaOverridableVariable", namespaceManager);
30
31 var overridableVariables = new OverridableVariablesInfo();
32 overridableVariables.Variables = new Dictionary<string, IOverridableVariableInfo>();
33
34 foreach (XPathNavigator node in nodes)
35 {
36 var variable = new OverridableVariableInfo();
37
38 string name = BootstrapperApplicationData.GetAttribute(node, "Name");
39 if (name == null)
40 {
41 throw new Exception("Failed to get name for overridable variable.");
42 }
43 variable.Name = name;
44
45 overridableVariables.Variables.Add(variable.Name, variable);
46 }
47
48 return overridableVariables;
49 }
50 }
51}
diff --git a/src/api/burn/balutil/balinfo.cpp b/src/api/burn/balutil/balinfo.cpp
index 3abb9286..5927ef72 100644
--- a/src/api/burn/balutil/balinfo.cpp
+++ b/src/api/burn/balutil/balinfo.cpp
@@ -11,7 +11,88 @@ static HRESULT ParseBalPackageInfoFromXml(
11 __in BAL_INFO_PACKAGES* pPackages, 11 __in BAL_INFO_PACKAGES* pPackages,
12 __in IXMLDOMDocument* pixdManifest 12 __in IXMLDOMDocument* pixdManifest
13 ); 13 );
14static HRESULT ParseOverridableVariablesFromXml(
15 __in BAL_INFO_OVERRIDABLE_VARIABLES* pOverridableVariables,
16 __in IXMLDOMDocument* pixdManifest
17 );
18
19
20DAPI_(HRESULT) BalInfoParseCommandLine(
21 __in BAL_INFO_COMMAND* pCommand,
22 __in LPCWSTR wzCommandLine
23 )
24{
25 HRESULT hr = S_OK;
26 int argc = 0;
27 LPWSTR* argv = NULL;
28 BOOL fUnknownArg = FALSE;
29
30 BalInfoUninitializeCommandLine(pCommand);
31
32 if (!wzCommandLine || !*wzCommandLine)
33 {
34 ExitFunction();
35 }
36
37 hr = AppParseCommandLine(wzCommandLine, &argc, &argv);
38 ExitOnFailure(hr, "Failed to parse command line.");
14 39
40 for (int i = 0; i < argc; ++i)
41 {
42 fUnknownArg = FALSE;
43
44 if (argv[i][0] == L'-' || argv[i][0] == L'/')
45 {
46 fUnknownArg = TRUE;
47 }
48 else
49 {
50 const wchar_t* pwc = wcschr(argv[i], L'=');
51 if (!pwc)
52 {
53 fUnknownArg = TRUE;
54 }
55 else
56 {
57 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pCommand->rgVariableNames), pCommand->cVariables, 1, sizeof(LPWSTR), 5);
58 ExitOnFailure(hr, "Failed to ensure size for variable names.");
59
60 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pCommand->rgVariableValues), pCommand->cVariables, 1, sizeof(LPWSTR), 5);
61 ExitOnFailure(hr, "Failed to ensure size for variable values.");
62
63 LPWSTR* psczVariableName = pCommand->rgVariableNames + pCommand->cVariables;
64 LPWSTR* psczVariableValue = pCommand->rgVariableValues + pCommand->cVariables;
65 pCommand->cVariables += 1;
66
67 hr = StrAllocString(psczVariableName, argv[i], pwc - argv[i]);
68 BalExitOnFailure(hr, "Failed to copy variable name.");
69
70 hr = StrAllocString(psczVariableValue, ++pwc, 0);
71 BalExitOnFailure(hr, "Failed to copy variable value.");
72 }
73 }
74
75 if (fUnknownArg)
76 {
77 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pCommand->rgUnknownArgs), pCommand->cUnknownArgs, 1, sizeof(LPWSTR), 5);
78 BalExitOnFailure(hr, "Failed to ensure size for unknown args.");
79
80 LPWSTR* psczArg = pCommand->rgUnknownArgs + pCommand->cUnknownArgs;
81 pCommand->cUnknownArgs += 1;
82
83 StrAllocString(psczArg, argv[i], 0);
84 BalExitOnFailure(hr, "Failed to copy unknown arg.");
85 }
86 }
87
88LExit:
89 if (argv)
90 {
91 AppFreeCommandLineArgs(argv);
92 }
93
94 return hr;
95}
15 96
16DAPI_(HRESULT) BalInfoParseFromXml( 97DAPI_(HRESULT) BalInfoParseFromXml(
17 __in BAL_INFO_BUNDLE* pBundle, 98 __in BAL_INFO_BUNDLE* pBundle,
@@ -45,6 +126,9 @@ DAPI_(HRESULT) BalInfoParseFromXml(
45 } 126 }
46 } 127 }
47 128
129 hr = ParseOverridableVariablesFromXml(&pBundle->overridableVariables, pixdManifest);
130 BalExitOnFailure(hr, "Failed to parse overridable variables from bootstrapper application data.");
131
48 hr = ParsePackagesFromXml(&pBundle->packages, pixdManifest); 132 hr = ParsePackagesFromXml(&pBundle->packages, pixdManifest);
49 BalExitOnFailure(hr, "Failed to parse package information from bootstrapper application data."); 133 BalExitOnFailure(hr, "Failed to parse package information from bootstrapper application data.");
50 134
@@ -163,12 +247,76 @@ DAPI_(void) BalInfoUninitialize(
163 247
164 ReleaseMem(pBundle->packages.rgPackages); 248 ReleaseMem(pBundle->packages.rgPackages);
165 249
250 for (DWORD i = 0; i < pBundle->overridableVariables.cVariables; ++i)
251 {
252 ReleaseStr(pBundle->overridableVariables.rgVariables[i].sczName);
253 }
254
255 ReleaseMem(pBundle->overridableVariables.rgVariables);
256 ReleaseDict(pBundle->overridableVariables.sdVariables);
257
166 ReleaseStr(pBundle->sczName); 258 ReleaseStr(pBundle->sczName);
167 ReleaseStr(pBundle->sczLogVariable); 259 ReleaseStr(pBundle->sczLogVariable);
168 memset(pBundle, 0, sizeof(BAL_INFO_BUNDLE)); 260 memset(pBundle, 0, sizeof(BAL_INFO_BUNDLE));
169} 261}
170 262
171 263
264DAPI_(void) BalInfoUninitializeCommandLine(
265 __in BAL_INFO_COMMAND* pCommand
266 )
267{
268 for (DWORD i = 0; i < pCommand->cUnknownArgs; ++i)
269 {
270 ReleaseNullStrSecure(pCommand->rgUnknownArgs[i]);
271 }
272
273 ReleaseMem(pCommand->rgUnknownArgs);
274
275 for (DWORD i = 0; i < pCommand->cVariables; ++i)
276 {
277 ReleaseNullStrSecure(pCommand->rgVariableNames[i]);
278 ReleaseNullStrSecure(pCommand->rgVariableValues[i]);
279 }
280
281 ReleaseMem(pCommand->rgVariableNames);
282 ReleaseMem(pCommand->rgVariableValues);
283
284 memset(pCommand, 0, sizeof(BAL_INFO_COMMAND));
285}
286
287
288DAPI_(HRESULT) BalSetOverridableVariablesFromEngine(
289 __in BAL_INFO_OVERRIDABLE_VARIABLES* pOverridableVariables,
290 __in BAL_INFO_COMMAND* pCommand,
291 __in IBootstrapperEngine* pEngine
292 )
293{
294 HRESULT hr = S_OK;
295 BAL_INFO_OVERRIDABLE_VARIABLE* pOverridableVariable = NULL;
296
297 for (DWORD i = 0; i < pCommand->cVariables; ++i)
298 {
299 LPCWSTR wzVariableName = pCommand->rgVariableNames[i];
300 LPCWSTR wzVariableValue = pCommand->rgVariableValues[i];
301
302 hr = DictGetValue(pOverridableVariables->sdVariables, wzVariableName, reinterpret_cast<void**>(&pOverridableVariable));
303 if (E_NOTFOUND == hr)
304 {
305 BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", wzVariableName);
306 hr = S_OK;
307 continue;
308 }
309 BalExitOnFailure(hr, "Failed to check the dictionary of overridable variables.");
310
311 hr = pEngine->SetVariableString(pOverridableVariable->sczName, wzVariableValue, FALSE);
312 BalExitOnFailure(hr, "Failed to set variable: '%ls'.", pOverridableVariable->sczName);
313 }
314
315LExit:
316 return hr;
317}
318
319
172static HRESULT ParsePackagesFromXml( 320static HRESULT ParsePackagesFromXml(
173 __in BAL_INFO_PACKAGES* pPackages, 321 __in BAL_INFO_PACKAGES* pPackages,
174 __in IXMLDOMDocument* pixdManifest 322 __in IXMLDOMDocument* pixdManifest
@@ -371,3 +519,58 @@ LExit:
371 519
372 return hr; 520 return hr;
373} 521}
522
523
524static HRESULT ParseOverridableVariablesFromXml(
525 __in BAL_INFO_OVERRIDABLE_VARIABLES* pOverridableVariables,
526 __in IXMLDOMDocument* pixdManifest
527 )
528{
529 HRESULT hr = S_OK;
530 IXMLDOMNode* pNode = NULL;
531 IXMLDOMNodeList* pNodes = NULL;
532 BAL_INFO_OVERRIDABLE_VARIABLE* pOverridableVariable = NULL;
533
534 // Get the list of variables users can override on the command line.
535 hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes);
536 if (S_FALSE == hr)
537 {
538 ExitFunction1(hr = S_OK);
539 }
540 ExitOnFailure(hr, "Failed to select overridable variable nodes.");
541
542 hr = pNodes->get_length(reinterpret_cast<long*>(&pOverridableVariables->cVariables));
543 ExitOnFailure(hr, "Failed to get overridable variable node count.");
544
545 if (pOverridableVariables->cVariables)
546 {
547 hr = DictCreateWithEmbeddedKey(&pOverridableVariables->sdVariables, pOverridableVariables->cVariables, reinterpret_cast<void**>(&pOverridableVariables->rgVariables), offsetof(BAL_INFO_OVERRIDABLE_VARIABLE, sczName), DICT_FLAG_NONE);
548 ExitOnFailure(hr, "Failed to create the overridable variables string dictionary.");
549
550 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&pOverridableVariables->rgVariables), sizeof(pOverridableVariable), pOverridableVariables->cVariables);
551 ExitOnFailure(hr, "Failed to create the overridable variables array.");
552
553 for (DWORD i = 0; i < pOverridableVariables->cVariables; ++i)
554 {
555 pOverridableVariable = pOverridableVariables->rgVariables + i;
556
557 hr = XmlNextElement(pNodes, &pNode, NULL);
558 ExitOnFailure(hr, "Failed to get next node.");
559
560 // @Name
561 hr = XmlGetAttributeEx(pNode, L"Name", &pOverridableVariable->sczName);
562 ExitOnFailure(hr, "Failed to get name for overridable variable.");
563
564 hr = DictAddValue(pOverridableVariables->sdVariables, pOverridableVariable);
565 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pOverridableVariable->sczName);
566
567 // prepare next iteration
568 ReleaseNullObject(pNode);
569 }
570 }
571
572LExit:
573 ReleaseObject(pNode);
574 ReleaseObject(pNodes);
575 return hr;
576}
diff --git a/src/api/burn/balutil/inc/balinfo.h b/src/api/burn/balutil/inc/balinfo.h
index 8c2155e9..0fce35ec 100644
--- a/src/api/burn/balutil/inc/balinfo.h
+++ b/src/api/burn/balutil/inc/balinfo.h
@@ -47,15 +47,50 @@ typedef struct _BAL_INFO_PACKAGES
47} BAL_INFO_PACKAGES; 47} BAL_INFO_PACKAGES;
48 48
49 49
50typedef struct _BAL_INFO_OVERRIDABLE_VARIABLE
51{
52 LPWSTR sczName;
53} BAL_INFO_OVERRIDABLE_VARIABLE;
54
55
56typedef struct _BAL_INFO_OVERRIDABLE_VARIABLES
57{
58 BAL_INFO_OVERRIDABLE_VARIABLE* rgVariables;
59 DWORD cVariables;
60 STRINGDICT_HANDLE sdVariables;
61} BAL_INFO_OVERRIDABLE_VARIABLES;
62
63
50typedef struct _BAL_INFO_BUNDLE 64typedef struct _BAL_INFO_BUNDLE
51{ 65{
52 BOOL fPerMachine; 66 BOOL fPerMachine;
53 LPWSTR sczName; 67 LPWSTR sczName;
54 LPWSTR sczLogVariable; 68 LPWSTR sczLogVariable;
55 BAL_INFO_PACKAGES packages; 69 BAL_INFO_PACKAGES packages;
70 BAL_INFO_OVERRIDABLE_VARIABLES overridableVariables;
56} BAL_INFO_BUNDLE; 71} BAL_INFO_BUNDLE;
57 72
58 73
74typedef struct _BAL_INFO_COMMAND
75{
76 DWORD cUnknownArgs;
77 LPWSTR* rgUnknownArgs;
78 DWORD cVariables;
79 LPWSTR* rgVariableNames;
80 LPWSTR* rgVariableValues;
81} BAL_INFO_COMMAND;
82
83
84/*******************************************************************
85 BalInfoParseCommandLine - parses wzCommandLine from BOOTSTRAPPER_COMMAND.
86
87********************************************************************/
88HRESULT DAPI BalInfoParseCommandLine(
89 __in BAL_INFO_COMMAND* pCommand,
90 __in LPCWSTR wzCommandLine
91 );
92
93
59/******************************************************************* 94/*******************************************************************
60 BalInfoParseFromXml - loads the bundle and package info from the UX 95 BalInfoParseFromXml - loads the bundle and package info from the UX
61 manifest. 96 manifest.
@@ -100,6 +135,26 @@ DAPI_(void) BalInfoUninitialize(
100 ); 135 );
101 136
102 137
138/*******************************************************************
139 BalInfoUninitializeCommandLine - uninitializes BAL_INFO_COMMAND.
140
141********************************************************************/
142void DAPI BalInfoUninitializeCommandLine(
143 __in BAL_INFO_COMMAND* pCommand
144);
145
146
147/*******************************************************************
148 BalInfoSetOverridableVariablesFromEngine - sets overridable variables from command line.
149
150 ********************************************************************/
151HRESULT DAPI BalSetOverridableVariablesFromEngine(
152 __in BAL_INFO_OVERRIDABLE_VARIABLES* pOverridableVariables,
153 __in BAL_INFO_COMMAND* pCommand,
154 __in IBootstrapperEngine* pEngine
155 );
156
157
103#ifdef __cplusplus 158#ifdef __cplusplus
104} 159}
105#endif 160#endif
diff --git a/src/api/burn/balutil/precomp.h b/src/api/burn/balutil/precomp.h
index 15142210..64d4a6cf 100644
--- a/src/api/burn/balutil/precomp.h
+++ b/src/api/burn/balutil/precomp.h
@@ -16,6 +16,7 @@
16#include <strsafe.h> 16#include <strsafe.h>
17 17
18#include <dutil.h> 18#include <dutil.h>
19#include <apputil.h>
19#include <pathutil.h> 20#include <pathutil.h>
20#include <locutil.h> 21#include <locutil.h>
21#include <memutil.h> 22#include <memutil.h>
diff --git a/src/api/burn/test/WixToolsetTest.Mba.Core/BaseBootstrapperApplicationFactoryFixture.cs b/src/api/burn/test/WixToolsetTest.Mba.Core/BaseBootstrapperApplicationFactoryFixture.cs
index aaf5ee29..b49b3927 100644
--- a/src/api/burn/test/WixToolsetTest.Mba.Core/BaseBootstrapperApplicationFactoryFixture.cs
+++ b/src/api/burn/test/WixToolsetTest.Mba.Core/BaseBootstrapperApplicationFactoryFixture.cs
@@ -3,6 +3,7 @@
3namespace WixToolsetTest.Mba.Core 3namespace WixToolsetTest.Mba.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
6 using System.Runtime.InteropServices; 7 using System.Runtime.InteropServices;
7 using WixToolset.Mba.Core; 8 using WixToolset.Mba.Core;
8 using Xunit; 9 using Xunit;
@@ -17,7 +18,7 @@ namespace WixToolsetTest.Mba.Core
17 action = LaunchAction.Install, 18 action = LaunchAction.Install,
18 cbSize = Marshal.SizeOf(typeof(TestCommand)), 19 cbSize = Marshal.SizeOf(typeof(TestCommand)),
19 display = Display.Full, 20 display = Display.Full,
20 wzCommandLine = "this \"is a\" test", 21 wzCommandLine = "this \"is a\" test VariableA=AVariable =EmptyName EmptyValue=",
21 }; 22 };
22 var pCommand = Marshal.AllocHGlobal(command.cbSize); 23 var pCommand = Marshal.AllocHGlobal(command.cbSize);
23 try 24 try
@@ -42,7 +43,15 @@ namespace WixToolsetTest.Mba.Core
42 Assert.Equal(baFactory.BA, createResults.pBA); 43 Assert.Equal(baFactory.BA, createResults.pBA);
43 Assert.Equal(baFactory.BA.Command.Action, command.action); 44 Assert.Equal(baFactory.BA.Command.Action, command.action);
44 Assert.Equal(baFactory.BA.Command.Display, command.display); 45 Assert.Equal(baFactory.BA.Command.Display, command.display);
45 Assert.Equal(baFactory.BA.Command.CommandLineArgs, new string[] { "this", "is a", "test" }); 46
47 var mbaCommand = baFactory.BA.Command.ParseCommandLine();
48 Assert.Equal(mbaCommand.UnknownCommandLineArgs, new string[] { "this", "is a", "test" });
49 Assert.Equal(mbaCommand.Variables, new KeyValuePair<string, string>[]
50 {
51 new KeyValuePair<string, string>("VariableA", "AVariable"),
52 new KeyValuePair<string, string>("", "EmptyName"),
53 new KeyValuePair<string, string>("EmptyValue", ""),
54 });
46 } 55 }
47 finally 56 finally
48 { 57 {
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
index 62cdc70a..9f0b0082 100644
--- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
@@ -2095,8 +2095,8 @@ private: // privates
2095 hr = BalManifestLoad(m_hModule, &pixdManifest); 2095 hr = BalManifestLoad(m_hModule, &pixdManifest);
2096 BalExitOnFailure(hr, "Failed to load bootstrapper application manifest."); 2096 BalExitOnFailure(hr, "Failed to load bootstrapper application manifest.");
2097 2097
2098 hr = ParseOverridableVariablesFromXml(pixdManifest); 2098 hr = BalInfoParseFromXml(&m_Bundle, pixdManifest);
2099 BalExitOnFailure(hr, "Failed to read overridable variables."); 2099 BalExitOnFailure(hr, "Failed to load bundle information.");
2100 2100
2101 hr = ProcessCommandLine(&m_sczLanguage); 2101 hr = ProcessCommandLine(&m_sczLanguage);
2102 ExitOnFailure(hr, "Unknown commandline parameters."); 2102 ExitOnFailure(hr, "Unknown commandline parameters.");
@@ -2110,9 +2110,6 @@ private: // privates
2110 hr = LoadTheme(sczModulePath, m_sczLanguage); 2110 hr = LoadTheme(sczModulePath, m_sczLanguage);
2111 ExitOnFailure(hr, "Failed to load theme."); 2111 ExitOnFailure(hr, "Failed to load theme.");
2112 2112
2113 hr = BalInfoParseFromXml(&m_Bundle, pixdManifest);
2114 BalExitOnFailure(hr, "Failed to load bundle information.");
2115
2116 hr = BalConditionsParseFromXml(&m_Conditions, pixdManifest, m_pWixLoc); 2113 hr = BalConditionsParseFromXml(&m_Conditions, pixdManifest, m_pWixLoc);
2117 BalExitOnFailure(hr, "Failed to load conditions from XML."); 2114 BalExitOnFailure(hr, "Failed to load conditions from XML.");
2118 2115
@@ -2173,16 +2170,20 @@ private: // privates
2173 HRESULT hr = S_OK; 2170 HRESULT hr = S_OK;
2174 int argc = 0; 2171 int argc = 0;
2175 LPWSTR* argv = NULL; 2172 LPWSTR* argv = NULL;
2176 LPWSTR sczVariableName = NULL; 2173 BOOL fUnknownArg = FALSE;
2177 LPWSTR sczVariableValue = NULL;
2178 2174
2179 if (m_command.wzCommandLine && *m_command.wzCommandLine) 2175 hr = BalInfoParseCommandLine(&m_BalInfoCommand, m_command.wzCommandLine);
2180 { 2176 BalExitOnFailure(hr, "Failed to parse command line with balutil.");
2181 hr = AppParseCommandLine(m_command.wzCommandLine, &argc, &argv); 2177
2182 ExitOnFailure(hr, "Failed to parse command line."); 2178 argc = m_BalInfoCommand.cUnknownArgs;
2179 argv = m_BalInfoCommand.rgUnknownArgs;
2183 2180
2181 if (argc)
2182 {
2184 for (int i = 0; i < argc; ++i) 2183 for (int i = 0; i < argc; ++i)
2185 { 2184 {
2185 fUnknownArg = FALSE;
2186
2186 if (argv[i][0] == L'-' || argv[i][0] == L'/') 2187 if (argv[i][0] == L'-' || argv[i][0] == L'/')
2187 { 2188 {
2188 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) 2189 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1))
@@ -2198,51 +2199,31 @@ private: // privates
2198 hr = StrAllocString(psczLanguage, &argv[i][0], 0); 2199 hr = StrAllocString(psczLanguage, &argv[i][0], 0);
2199 BalExitOnFailure(hr, "Failed to copy language."); 2200 BalExitOnFailure(hr, "Failed to copy language.");
2200 } 2201 }
2201 } 2202 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"cache", -1))
2202 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"cache", -1))
2203 {
2204 m_plannedAction = BOOTSTRAPPER_ACTION_CACHE;
2205 }
2206 else if (m_sdOverridableVariables)
2207 {
2208 const wchar_t* pwc = wcschr(argv[i], L'=');
2209 if (pwc)
2210 { 2203 {
2211 hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); 2204 m_plannedAction = BOOTSTRAPPER_ACTION_CACHE;
2212 BalExitOnFailure(hr, "Failed to copy variable name.");
2213
2214 hr = DictKeyExists(m_sdOverridableVariables, sczVariableName);
2215 if (E_NOTFOUND == hr)
2216 {
2217 BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", sczVariableName);
2218 hr = S_OK;
2219 continue;
2220 }
2221 ExitOnFailure(hr, "Failed to check the dictionary of overridable variables.");
2222
2223 hr = StrAllocString(&sczVariableValue, ++pwc, 0);
2224 BalExitOnFailure(hr, "Failed to copy variable value.");
2225
2226 hr = m_pEngine->SetVariableString(sczVariableName, sczVariableValue, FALSE);
2227 BalExitOnFailure(hr, "Failed to set variable.");
2228 } 2205 }
2229 else 2206 else
2230 { 2207 {
2231 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]); 2208 fUnknownArg = TRUE;
2232 } 2209 }
2233 } 2210 }
2234 } 2211 else
2235 } 2212 {
2213 fUnknownArg = TRUE;
2214 }
2236 2215
2237 LExit: 2216 if (fUnknownArg)
2238 if (argv) 2217 {
2239 { 2218 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);
2240 AppFreeCommandLineArgs(argv); 2219 }
2220 }
2241 } 2221 }
2242 2222
2243 ReleaseStr(sczVariableName); 2223 hr = BalSetOverridableVariablesFromEngine(&m_Bundle.overridableVariables, &m_BalInfoCommand, m_pEngine);
2244 ReleaseStr(sczVariableValue); 2224 BalExitOnFailure(hr, "Failed to set overridable variables from the command line.");
2245 2225
2226 LExit:
2246 return hr; 2227 return hr;
2247 } 2228 }
2248 2229
@@ -2323,57 +2304,6 @@ private: // privates
2323 } 2304 }
2324 2305
2325 2306
2326 HRESULT ParseOverridableVariablesFromXml(
2327 __in IXMLDOMDocument* pixdManifest
2328 )
2329 {
2330 HRESULT hr = S_OK;
2331 IXMLDOMNode* pNode = NULL;
2332 IXMLDOMNodeList* pNodes = NULL;
2333 DWORD cNodes = 0;
2334 LPWSTR scz = NULL;
2335
2336 // Get the list of variables users can override on the command line.
2337 hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes);
2338 if (S_FALSE == hr)
2339 {
2340 ExitFunction1(hr = S_OK);
2341 }
2342 ExitOnFailure(hr, "Failed to select overridable variable nodes.");
2343
2344 hr = pNodes->get_length((long*)&cNodes);
2345 ExitOnFailure(hr, "Failed to get overridable variable node count.");
2346
2347 if (cNodes)
2348 {
2349 hr = DictCreateStringList(&m_sdOverridableVariables, 32, DICT_FLAG_NONE);
2350 ExitOnFailure(hr, "Failed to create the string dictionary.");
2351
2352 for (DWORD i = 0; i < cNodes; ++i)
2353 {
2354 hr = XmlNextElement(pNodes, &pNode, NULL);
2355 ExitOnFailure(hr, "Failed to get next node.");
2356
2357 // @Name
2358 hr = XmlGetAttributeEx(pNode, L"Name", &scz);
2359 ExitOnFailure(hr, "Failed to get @Name.");
2360
2361 hr = DictAddKey(m_sdOverridableVariables, scz);
2362 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", scz);
2363
2364 // prepare next iteration
2365 ReleaseNullObject(pNode);
2366 }
2367 }
2368
2369 LExit:
2370 ReleaseObject(pNode);
2371 ReleaseObject(pNodes);
2372 ReleaseStr(scz);
2373 return hr;
2374 }
2375
2376
2377 HRESULT InitializePackageInfo() 2307 HRESULT InitializePackageInfo()
2378 { 2308 {
2379 HRESULT hr = S_OK; 2309 HRESULT hr = S_OK;
@@ -3951,6 +3881,7 @@ public:
3951 3881
3952 m_pWixLoc = NULL; 3882 m_pWixLoc = NULL;
3953 memset(&m_Bundle, 0, sizeof(m_Bundle)); 3883 memset(&m_Bundle, 0, sizeof(m_Bundle));
3884 memset(&m_BalInfoCommand, 0, sizeof(m_BalInfoCommand));
3954 memset(&m_Conditions, 0, sizeof(m_Conditions)); 3885 memset(&m_Conditions, 0, sizeof(m_Conditions));
3955 m_sczConfirmCloseMessage = NULL; 3886 m_sczConfirmCloseMessage = NULL;
3956 m_sczFailedMessage = NULL; 3887 m_sczFailedMessage = NULL;
@@ -3977,7 +3908,6 @@ public:
3977 m_fSuppressRepair = FALSE; 3908 m_fSuppressRepair = FALSE;
3978 m_fSupportCacheOnly = FALSE; 3909 m_fSupportCacheOnly = FALSE;
3979 3910
3980 m_sdOverridableVariables = NULL;
3981 m_pTaskbarList = NULL; 3911 m_pTaskbarList = NULL;
3982 m_uTaskbarButtonCreatedMessage = UINT_MAX; 3912 m_uTaskbarButtonCreatedMessage = UINT_MAX;
3983 m_fTaskbarButtonOK = FALSE; 3913 m_fTaskbarButtonOK = FALSE;
@@ -4013,11 +3943,11 @@ public:
4013 } 3943 }
4014 3944
4015 ::DeleteCriticalSection(&m_csShowingInternalUiThisPackage); 3945 ::DeleteCriticalSection(&m_csShowingInternalUiThisPackage);
4016 ReleaseDict(m_sdOverridableVariables);
4017 ReleaseStr(m_sczFailedMessage); 3946 ReleaseStr(m_sczFailedMessage);
4018 ReleaseStr(m_sczConfirmCloseMessage); 3947 ReleaseStr(m_sczConfirmCloseMessage);
4019 BalConditionsUninitialize(&m_Conditions); 3948 BalConditionsUninitialize(&m_Conditions);
4020 BalInfoUninitialize(&m_Bundle); 3949 BalInfoUninitialize(&m_Bundle);
3950 BalInfoUninitializeCommandLine(&m_BalInfoCommand);
4021 LocFree(m_pWixLoc); 3951 LocFree(m_pWixLoc);
4022 3952
4023 ReleaseStr(m_sczLanguage); 3953 ReleaseStr(m_sczLanguage);
@@ -4050,6 +3980,7 @@ private:
4050 3980
4051 WIX_LOCALIZATION* m_pWixLoc; 3981 WIX_LOCALIZATION* m_pWixLoc;
4052 BAL_INFO_BUNDLE m_Bundle; 3982 BAL_INFO_BUNDLE m_Bundle;
3983 BAL_INFO_COMMAND m_BalInfoCommand;
4053 BAL_CONDITIONS m_Conditions; 3984 BAL_CONDITIONS m_Conditions;
4054 LPWSTR m_sczFailedMessage; 3985 LPWSTR m_sczFailedMessage;
4055 LPWSTR m_sczConfirmCloseMessage; 3986 LPWSTR m_sczConfirmCloseMessage;
@@ -4080,8 +4011,6 @@ private:
4080 BOOL m_fSuppressRepair; 4011 BOOL m_fSuppressRepair;
4081 BOOL m_fSupportCacheOnly; 4012 BOOL m_fSupportCacheOnly;
4082 4013
4083 STRINGDICT_HANDLE m_sdOverridableVariables;
4084
4085 BOOL m_fPrereq; 4014 BOOL m_fPrereq;
4086 BOOL m_fPrereqInstalled; 4015 BOOL m_fPrereqInstalled;
4087 BOOL m_fPrereqAlreadyInstalled; 4016 BOOL m_fPrereqAlreadyInstalled;
diff --git a/src/test/burn/TestBA/TestBA.cs b/src/test/burn/TestBA/TestBA.cs
index 09378bc5..5c70253d 100644
--- a/src/test/burn/TestBA/TestBA.cs
+++ b/src/test/burn/TestBA/TestBA.cs
@@ -74,8 +74,6 @@ namespace WixToolset.Test.BA
74 return; 74 return;
75 } 75 }
76 76
77 base.OnStartup(args);
78
79 this.action = this.Command.Action; 77 this.action = this.Command.Action;
80 this.TestVariables(); 78 this.TestVariables();
81 79
@@ -84,7 +82,7 @@ namespace WixToolset.Test.BA
84 82
85 List<string> verifyArguments = this.ReadVerifyArguments(); 83 List<string> verifyArguments = this.ReadVerifyArguments();
86 84
87 foreach (string arg in this.Command.CommandLineArgs) 85 foreach (string arg in BootstrapperCommand.ParseCommandLineToArgs(this.Command.CommandLine))
88 { 86 {
89 // If we're not in the update already, process the updatebundle. 87 // If we're not in the update already, process the updatebundle.
90 if (this.Command.Relation != RelationType.Update && arg.StartsWith("-updatebundle:", StringComparison.OrdinalIgnoreCase)) 88 if (this.Command.Relation != RelationType.Update && arg.StartsWith("-updatebundle:", StringComparison.OrdinalIgnoreCase))
@@ -116,6 +114,8 @@ namespace WixToolset.Test.BA
116 return; 114 return;
117 } 115 }
118 116
117 base.OnStartup(args);
118
119 int redetectCount; 119 int redetectCount;
120 string redetect = this.ReadPackageAction(null, "RedetectCount"); 120 string redetect = this.ReadPackageAction(null, "RedetectCount");
121 if (String.IsNullOrEmpty(redetect) || !Int32.TryParse(redetect, out redetectCount)) 121 if (String.IsNullOrEmpty(redetect) || !Int32.TryParse(redetect, out redetectCount))
diff --git a/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs b/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
index 410ab110..14ea561f 100644
--- a/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
+++ b/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
@@ -256,7 +256,7 @@ namespace WixToolset.WixBA
256 256
257 public bool IsComplete 257 public bool IsComplete
258 { 258 {
259 get { return IsSuccessfulCompletion || IsFailedCompletion; } 259 get { return this.IsSuccessfulCompletion || this.IsFailedCompletion; }
260 } 260 }
261 261
262 public bool IsSuccessfulCompletion 262 public bool IsSuccessfulCompletion
@@ -358,7 +358,7 @@ namespace WixToolset.WixBA
358 { 358 {
359 this.root.Canceled = false; 359 this.root.Canceled = false;
360 WixBA.Plan(WixBA.Model.PlannedAction); 360 WixBA.Plan(WixBA.Model.PlannedAction);
361 }, param => IsFailedCompletion); 361 }, param => this.IsFailedCompletion);
362 } 362 }
363 363
364 return this.tryAgainCommand; 364 return this.tryAgainCommand;
@@ -456,7 +456,7 @@ namespace WixToolset.WixBA
456 456
457 // Force all commands to reevaluate CanExecute. 457 // Force all commands to reevaluate CanExecute.
458 // InvalidateRequerySuggested must be run on the UI thread. 458 // InvalidateRequerySuggested must be run on the UI thread.
459 root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested)); 459 this.root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested));
460 } 460 }
461 461
462 private void PlanPackageBegin(object sender, PlanPackageBeginEventArgs e) 462 private void PlanPackageBegin(object sender, PlanPackageBeginEventArgs e)
@@ -639,7 +639,7 @@ namespace WixToolset.WixBA
639 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close)); 639 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
640 return; 640 return;
641 } 641 }
642 else if (root.AutoClose) 642 else if (this.root.AutoClose)
643 { 643 {
644 // Automatically closing since the user clicked the X button. 644 // Automatically closing since the user clicked the X button.
645 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close)); 645 WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
@@ -648,13 +648,13 @@ namespace WixToolset.WixBA
648 648
649 // Force all commands to reevaluate CanExecute. 649 // Force all commands to reevaluate CanExecute.
650 // InvalidateRequerySuggested must be run on the UI thread. 650 // InvalidateRequerySuggested must be run on the UI thread.
651 root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested)); 651 this.root.Dispatcher.Invoke(new Action(CommandManager.InvalidateRequerySuggested));
652 } 652 }
653 653
654 private void ParseCommandLine() 654 private void ParseCommandLine()
655 { 655 {
656 // Get array of arguments based on the system parsing algorithm. 656 // Get array of arguments based on the system parsing algorithm.
657 string[] args = WixBA.Model.Command.CommandLineArgs; 657 string[] args = BootstrapperCommand.ParseCommandLineToArgs(WixBA.Model.Command.CommandLine);
658 for (int i = 0; i < args.Length; ++i) 658 for (int i = 0; i < args.Length; ++i)
659 { 659 {
660 if (args[i].StartsWith("InstallFolder=", StringComparison.InvariantCultureIgnoreCase)) 660 if (args[i].StartsWith("InstallFolder=", StringComparison.InvariantCultureIgnoreCase))