Expose overridable variable APIs in balutil and Mba.Core.
Fixes #4777
Sean Hall committed
Jun 29, 2021 at 19:14 UTC
8cbfc326cccf8d9b3b63cb6f752fc770f7dee0fc
17 files changed
+539
-121
src/api/burn/WixToolset.Mba.Core/BootstrapperCommand.cs
+47
-5
@@ -3,6 +3,7 @@
3
namespace WixToolset.Mba.Core
4
{
5
using System;
6
+ using System.Collections.Generic;
7
using System.ComponentModel;
8
using System.Runtime.InteropServices;
9
@@ -11,8 +12,6 @@ namespace WixToolset.Mba.Core
12
/// </summary>
13
public sealed class BootstrapperCommand : IBootstrapperCommand
14
{
14
- private readonly string commandLine;
15
-
15
/// <summary>
16
///
17
/// </summary>
@@ -45,7 +44,7 @@ namespace WixToolset.Mba.Core
44
this.Action = action;
45
this.Display = display;
46
this.Restart = restart;
48
- this.commandLine = commandLine;
47
+ this.CommandLine = commandLine;
48
this.CmdShow = cmdShow;
49
this.Resume = resume;
50
this.SplashScreen = splashScreen;
@@ -66,7 +65,7 @@ namespace WixToolset.Mba.Core
65
public Restart Restart { get; }
66
67
/// <inheritdoc/>
69
- public string[] CommandLineArgs => GetCommandLineArgs(this.commandLine);
68
+ public string CommandLine { get; }
69
70
/// <inheritdoc/>
71
public int CmdShow { get; }
@@ -92,6 +91,49 @@ namespace WixToolset.Mba.Core
91
/// <inheritdoc/>
92
public string BootstrapperApplicationDataPath { get; }
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
+
137
/// <summary>
138
/// Gets the command line arguments as a string array.
139
/// </summary>
@@ -102,7 +144,7 @@ namespace WixToolset.Mba.Core
144
/// <remarks>
145
/// This method uses the same parsing as the operating system which handles quotes and spaces correctly.
146
/// </remarks>
105
- public static string[] GetCommandLineArgs(string commandLine)
147
+ public static string[] ParseCommandLineToArgs(string commandLine)
148
{
149
if (null == commandLine)
150
{
src/api/burn/WixToolset.Mba.Core/BundleInfo.cs
+5
@@ -22,6 +22,9 @@ namespace WixToolset.Mba.Core
22
/// <inheritdoc/>
23
public string LogVariable { get; internal set; }
24
25
+ /// <inheritdoc/>
26
+ public IOverridableVariables OverridableVariables { get; internal set; }
27
+
28
/// <inheritdoc/>
29
public IDictionary<string, IPackageInfo> Packages { get; internal set; }
30
@@ -78,6 +81,8 @@ namespace WixToolset.Mba.Core
81
82
bundle.LogVariable = BootstrapperApplicationData.GetAttribute(bundleNode, "LogPathVariable");
83
84
+ bundle.OverridableVariables = OverridableVariablesInfo.ParseFromXml(root);
85
+
86
bundle.Packages = PackageInfo.ParsePackagesFromXml(root);
87
88
return bundle;
src/api/burn/WixToolset.Mba.Core/IBootstrapperCommand.cs
+12
-4
@@ -25,13 +25,12 @@ namespace WixToolset.Mba.Core
25
Restart Restart { get; }
26
27
/// <summary>
28
- /// Gets the command line arguments as a string array.
28
+ /// Gets the command line arguments.
29
/// </summary>
30
/// <returns>
31
- /// Array of command line arguments not handled by the engine.
31
+ /// Command line arguments not handled by the engine.
32
/// </returns>
33
- /// <exception type="Win32Exception">The command line could not be parsed into an array.</exception>
34
- string[] CommandLineArgs { get; }
33
+ string CommandLine { get; }
34
35
/// <summary>
36
/// Hint for the initial visibility of the window.
@@ -72,5 +71,14 @@ namespace WixToolset.Mba.Core
71
/// Gets path to BootstrapperApplicationData.xml.
72
/// </summary>
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();
83
}
84
}
src/api/burn/WixToolset.Mba.Core/IBundleInfo.cs
+5
@@ -19,6 +19,11 @@ namespace WixToolset.Mba.Core
19
/// </summary>
20
string Name { get; }
21
22
+ /// <summary>
23
+ ///
24
+ /// </summary>
25
+ IOverridableVariables OverridableVariables { get; }
26
+
27
/// <summary>
28
///
29
/// </summary>
src/api/burn/WixToolset.Mba.Core/IMbaCommand.cs
new
+30
@@ -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
+
3
+namespace 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
+}
src/api/burn/WixToolset.Mba.Core/IOverridableVariableInfo.cs
new
+15
@@ -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
+
3
+namespace 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
src/api/burn/WixToolset.Mba.Core/IOverridableVariables.cs
new
+17
@@ -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
+
3
+namespace 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
src/api/burn/WixToolset.Mba.Core/MbaCommand.cs
new
+33
@@ -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
+
3
+namespace 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
+}
src/api/burn/WixToolset.Mba.Core/OverridableVariableInfo.cs
new
+15
@@ -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
+
3
+namespace 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
+}
src/api/burn/WixToolset.Mba.Core/OverridableVariables.cs
new
+51
@@ -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
+
3
+namespace 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
+}
src/api/burn/balutil/balinfo.cpp
+203
@@ -11,7 +11,88 @@ static HRESULT ParseBalPackageInfoFromXml(
11
__in BAL_INFO_PACKAGES* pPackages,
12
__in IXMLDOMDocument* pixdManifest
13
);
14
+static HRESULT ParseOverridableVariablesFromXml(
15
+ __in BAL_INFO_OVERRIDABLE_VARIABLES* pOverridableVariables,
16
+ __in IXMLDOMDocument* pixdManifest
17
+ );
18
+
19
+
20
+DAPI_(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.");
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
+
88
+LExit:
89
+ if (argv)
90
+ {
91
+ AppFreeCommandLineArgs(argv);
92
+ }
93
+
94
+ return hr;
95
+}
96
97
DAPI_(HRESULT) BalInfoParseFromXml(
98
__in BAL_INFO_BUNDLE* pBundle,
@@ -45,6 +126,9 @@ DAPI_(HRESULT) BalInfoParseFromXml(
126
}
127
}
128
129
+ hr = ParseOverridableVariablesFromXml(&pBundle->overridableVariables, pixdManifest);
130
+ BalExitOnFailure(hr, "Failed to parse overridable variables from bootstrapper application data.");
131
+
132
hr = ParsePackagesFromXml(&pBundle->packages, pixdManifest);
133
BalExitOnFailure(hr, "Failed to parse package information from bootstrapper application data.");
134
@@ -163,12 +247,76 @@ DAPI_(void) BalInfoUninitialize(
247
248
ReleaseMem(pBundle->packages.rgPackages);
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
+
258
ReleaseStr(pBundle->sczName);
259
ReleaseStr(pBundle->sczLogVariable);
260
memset(pBundle, 0, sizeof(BAL_INFO_BUNDLE));
261
}
262
263
264
+DAPI_(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
+
288
+DAPI_(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
+
315
+LExit:
316
+ return hr;
317
+}
318
+
319
+
320
static HRESULT ParsePackagesFromXml(
321
__in BAL_INFO_PACKAGES* pPackages,
322
__in IXMLDOMDocument* pixdManifest
@@ -371,3 +519,58 @@ LExit:
519
520
return hr;
521
}
522
+
523
+
524
+static 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
+
572
+LExit:
573
+ ReleaseObject(pNode);
574
+ ReleaseObject(pNodes);
575
+ return hr;
576
+}
src/api/burn/balutil/inc/balinfo.h
+55
@@ -47,15 +47,50 @@ typedef struct _BAL_INFO_PACKAGES
47
} BAL_INFO_PACKAGES;
48
49
50
+typedef struct _BAL_INFO_OVERRIDABLE_VARIABLE
51
+{
52
+ LPWSTR sczName;
53
+} BAL_INFO_OVERRIDABLE_VARIABLE;
54
+
55
+
56
+typedef 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
+
64
typedef struct _BAL_INFO_BUNDLE
65
{
66
BOOL fPerMachine;
67
LPWSTR sczName;
68
LPWSTR sczLogVariable;
69
BAL_INFO_PACKAGES packages;
70
+ BAL_INFO_OVERRIDABLE_VARIABLES overridableVariables;
71
} BAL_INFO_BUNDLE;
72
73
74
+typedef 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
+********************************************************************/
88
+HRESULT DAPI BalInfoParseCommandLine(
89
+ __in BAL_INFO_COMMAND* pCommand,
90
+ __in LPCWSTR wzCommandLine
91
+ );
92
+
93
+
94
/*******************************************************************
95
BalInfoParseFromXml - loads the bundle and package info from the UX
96
manifest.
@@ -100,6 +135,26 @@ DAPI_(void) BalInfoUninitialize(
135
);
136
137
138
+/*******************************************************************
139
+ BalInfoUninitializeCommandLine - uninitializes BAL_INFO_COMMAND.
140
+
141
+********************************************************************/
142
+void DAPI BalInfoUninitializeCommandLine(
143
+ __in BAL_INFO_COMMAND* pCommand
144
+);
145
+
146
+
147
+/*******************************************************************
148
+ BalInfoSetOverridableVariablesFromEngine - sets overridable variables from command line.
149
+
150
+ ********************************************************************/
151
+HRESULT DAPI BalSetOverridableVariablesFromEngine(
152
+ __in BAL_INFO_OVERRIDABLE_VARIABLES* pOverridableVariables,
153
+ __in BAL_INFO_COMMAND* pCommand,
154
+ __in IBootstrapperEngine* pEngine
155
+ );
156
+
157
+
158
#ifdef __cplusplus
159
}
160
#endif
src/api/burn/balutil/precomp.h
+1
@@ -16,6 +16,7 @@
16
#include <strsafe.h>
17
18
#include <dutil.h>
19
+#include <apputil.h>
20
#include <pathutil.h>
21
#include <locutil.h>
22
#include <memutil.h>
src/api/burn/test/WixToolsetTest.Mba.Core/BaseBootstrapperApplicationFactoryFixture.cs
+11
-2
@@ -3,6 +3,7 @@
3
namespace WixToolsetTest.Mba.Core
4
{
5
using System;
6
+ using System.Collections.Generic;
7
using System.Runtime.InteropServices;
8
using WixToolset.Mba.Core;
9
using Xunit;
@@ -17,7 +18,7 @@ namespace WixToolsetTest.Mba.Core
18
action = LaunchAction.Install,
19
cbSize = Marshal.SizeOf(typeof(TestCommand)),
20
display = Display.Full,
20
- wzCommandLine = "this \"is a\" test",
21
+ wzCommandLine = "this \"is a\" test VariableA=AVariable =EmptyName EmptyValue=",
22
};
23
var pCommand = Marshal.AllocHGlobal(command.cbSize);
24
try
@@ -42,7 +43,15 @@ namespace WixToolsetTest.Mba.Core
43
Assert.Equal(baFactory.BA, createResults.pBA);
44
Assert.Equal(baFactory.BA.Command.Action, command.action);
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
+ });
55
}
56
finally
57
{
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+30
-101
@@ -2095,8 +2095,8 @@ private: // privates
2095
hr = BalManifestLoad(m_hModule, &pixdManifest);
2096
BalExitOnFailure(hr, "Failed to load bootstrapper application manifest.");
2097
2098
- hr = ParseOverridableVariablesFromXml(pixdManifest);
2099
- BalExitOnFailure(hr, "Failed to read overridable variables.");
2098
+ hr = BalInfoParseFromXml(&m_Bundle, pixdManifest);
2099
+ BalExitOnFailure(hr, "Failed to load bundle information.");
2100
2101
hr = ProcessCommandLine(&m_sczLanguage);
2102
ExitOnFailure(hr, "Unknown commandline parameters.");
@@ -2110,9 +2110,6 @@ private: // privates
2110
hr = LoadTheme(sczModulePath, m_sczLanguage);
2111
ExitOnFailure(hr, "Failed to load theme.");
2112
2113
- hr = BalInfoParseFromXml(&m_Bundle, pixdManifest);
2114
- BalExitOnFailure(hr, "Failed to load bundle information.");
2115
-
2113
hr = BalConditionsParseFromXml(&m_Conditions, pixdManifest, m_pWixLoc);
2114
BalExitOnFailure(hr, "Failed to load conditions from XML.");
2115
@@ -2173,16 +2170,20 @@ private: // privates
2170
HRESULT hr = S_OK;
2171
int argc = 0;
2172
LPWSTR* argv = NULL;
2176
- LPWSTR sczVariableName = NULL;
2177
- LPWSTR sczVariableValue = NULL;
2173
+ BOOL fUnknownArg = FALSE;
2174
2179
- if (m_command.wzCommandLine && *m_command.wzCommandLine)
2180
- {
2181
- hr = AppParseCommandLine(m_command.wzCommandLine, &argc, &argv);
2182
- ExitOnFailure(hr, "Failed to parse command line.");
2175
+ hr = BalInfoParseCommandLine(&m_BalInfoCommand, m_command.wzCommandLine);
2176
+ BalExitOnFailure(hr, "Failed to parse command line with balutil.");
2177
+
2178
+ argc = m_BalInfoCommand.cUnknownArgs;
2179
+ argv = m_BalInfoCommand.rgUnknownArgs;
2180
2181
+ if (argc)
2182
+ {
2183
for (int i = 0; i < argc; ++i)
2184
{
2185
+ fUnknownArg = FALSE;
2186
+
2187
if (argv[i][0] == L'-' || argv[i][0] == L'/')
2188
{
2189
if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1))
@@ -2198,51 +2199,31 @@ private: // privates
2199
hr = StrAllocString(psczLanguage, &argv[i][0], 0);
2200
BalExitOnFailure(hr, "Failed to copy language.");
2201
}
2201
- }
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)
2202
+ else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"cache", -1))
2203
{
2211
- hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
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.");
2204
+ m_plannedAction = BOOTSTRAPPER_ACTION_CACHE;
2205
}
2206
else
2207
{
2231
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);
2208
+ fUnknownArg = TRUE;
2209
}
2210
}
2234
- }
2235
- }
2211
+ else
2212
+ {
2213
+ fUnknownArg = TRUE;
2214
+ }
2215
2237
- LExit:
2238
- if (argv)
2239
- {
2240
- AppFreeCommandLineArgs(argv);
2216
+ if (fUnknownArg)
2217
+ {
2218
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);
2219
+ }
2220
+ }
2221
}
2222
2243
- ReleaseStr(sczVariableName);
2244
- ReleaseStr(sczVariableValue);
2223
+ hr = BalSetOverridableVariablesFromEngine(&m_Bundle.overridableVariables, &m_BalInfoCommand, m_pEngine);
2224
+ BalExitOnFailure(hr, "Failed to set overridable variables from the command line.");
2225
2226
+ LExit:
2227
return hr;
2228
}
2229
@@ -2323,57 +2304,6 @@ private: // privates
2304
}
2305
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
-
2307
HRESULT InitializePackageInfo()
2308
{
2309
HRESULT hr = S_OK;
@@ -3951,6 +3881,7 @@ public:
3881
3882
m_pWixLoc = NULL;
3883
memset(&m_Bundle, 0, sizeof(m_Bundle));
3884
+ memset(&m_BalInfoCommand, 0, sizeof(m_BalInfoCommand));
3885
memset(&m_Conditions, 0, sizeof(m_Conditions));
3886
m_sczConfirmCloseMessage = NULL;
3887
m_sczFailedMessage = NULL;
@@ -3977,7 +3908,6 @@ public:
3908
m_fSuppressRepair = FALSE;
3909
m_fSupportCacheOnly = FALSE;
3910
3980
- m_sdOverridableVariables = NULL;
3911
m_pTaskbarList = NULL;
3912
m_uTaskbarButtonCreatedMessage = UINT_MAX;
3913
m_fTaskbarButtonOK = FALSE;
@@ -4013,11 +3943,11 @@ public:
3943
}
3944
3945
::DeleteCriticalSection(&m_csShowingInternalUiThisPackage);
4016
- ReleaseDict(m_sdOverridableVariables);
3946
ReleaseStr(m_sczFailedMessage);
3947
ReleaseStr(m_sczConfirmCloseMessage);
3948
BalConditionsUninitialize(&m_Conditions);
3949
BalInfoUninitialize(&m_Bundle);
3950
+ BalInfoUninitializeCommandLine(&m_BalInfoCommand);
3951
LocFree(m_pWixLoc);
3952
3953
ReleaseStr(m_sczLanguage);
@@ -4050,6 +3980,7 @@ private:
3980
3981
WIX_LOCALIZATION* m_pWixLoc;
3982
BAL_INFO_BUNDLE m_Bundle;
3983
+ BAL_INFO_COMMAND m_BalInfoCommand;
3984
BAL_CONDITIONS m_Conditions;
3985
LPWSTR m_sczFailedMessage;
3986
LPWSTR m_sczConfirmCloseMessage;
@@ -4080,8 +4011,6 @@ private:
4011
BOOL m_fSuppressRepair;
4012
BOOL m_fSupportCacheOnly;
4013
4083
- STRINGDICT_HANDLE m_sdOverridableVariables;
4084
-
4014
BOOL m_fPrereq;
4015
BOOL m_fPrereqInstalled;
4016
BOOL m_fPrereqAlreadyInstalled;
src/test/burn/TestBA/TestBA.cs
+3
-3
@@ -74,8 +74,6 @@ namespace WixToolset.Test.BA
74
return;
75
}
76
77
- base.OnStartup(args);
78
-
77
this.action = this.Command.Action;
78
this.TestVariables();
79
@@ -84,7 +82,7 @@ namespace WixToolset.Test.BA
82
83
List<string> verifyArguments = this.ReadVerifyArguments();
84
87
- foreach (string arg in this.Command.CommandLineArgs)
85
+ foreach (string arg in BootstrapperCommand.ParseCommandLineToArgs(this.Command.CommandLine))
86
{
87
// If we're not in the update already, process the updatebundle.
88
if (this.Command.Relation != RelationType.Update && arg.StartsWith("-updatebundle:", StringComparison.OrdinalIgnoreCase))
@@ -116,6 +114,8 @@ namespace WixToolset.Test.BA
114
return;
115
}
116
117
+ base.OnStartup(args);
118
+
119
int redetectCount;
120
string redetect = this.ReadPackageAction(null, "RedetectCount");
121
if (String.IsNullOrEmpty(redetect) || !Int32.TryParse(redetect, out redetectCount))
src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
+6
-6
@@ -256,7 +256,7 @@ namespace WixToolset.WixBA
256
257
public bool IsComplete
258
{
259
- get { return IsSuccessfulCompletion || IsFailedCompletion; }
259
+ get { return this.IsSuccessfulCompletion || this.IsFailedCompletion; }
260
}
261
262
public bool IsSuccessfulCompletion
@@ -358,7 +358,7 @@ namespace WixToolset.WixBA
358
{
359
this.root.Canceled = false;
360
WixBA.Plan(WixBA.Model.PlannedAction);
361
- }, param => IsFailedCompletion);
361
+ }, param => this.IsFailedCompletion);
362
}
363
364
return this.tryAgainCommand;
@@ -456,7 +456,7 @@ namespace WixToolset.WixBA
456
457
// Force all commands to reevaluate CanExecute.
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
}
461
462
private void PlanPackageBegin(object sender, PlanPackageBeginEventArgs e)
@@ -639,7 +639,7 @@ namespace WixToolset.WixBA
639
WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
640
return;
641
}
642
- else if (root.AutoClose)
642
+ else if (this.root.AutoClose)
643
{
644
// Automatically closing since the user clicked the X button.
645
WixBA.Dispatcher.BeginInvoke(new Action(WixBA.View.Close));
@@ -648,13 +648,13 @@ namespace WixToolset.WixBA
648
649
// Force all commands to reevaluate CanExecute.
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
}
653
654
private void ParseCommandLine()
655
{
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)
659
{
660
if (args[i].StartsWith("InstallFolder=", StringComparison.InvariantCultureIgnoreCase))