@joebigelow / wix-1 / commits / 8cbfc326

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))