// 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. namespace WixToolset.Mba.Core { using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; /// /// Default implementation of . /// public sealed class BootstrapperCommand : IBootstrapperCommand { /// /// /// /// /// /// /// /// /// /// /// /// /// /// public BootstrapperCommand( LaunchAction action, Display display, string commandLine, int cmdShow, ResumeType resume, IntPtr splashScreen, RelationType relation, bool passthrough, string layoutDirectory, string bootstrapperWorkingFolder, string bootstrapperApplicationDataPath) { this.Action = action; this.Display = display; this.CommandLine = commandLine; this.CmdShow = cmdShow; this.Resume = resume; this.SplashScreen = splashScreen; this.Relation = relation; this.Passthrough = passthrough; this.LayoutDirectory = layoutDirectory; this.BootstrapperWorkingFolder = bootstrapperWorkingFolder; this.BootstrapperApplicationDataPath = bootstrapperApplicationDataPath; } /// public LaunchAction Action { get; } /// public Display Display { get; } /// public string CommandLine { get; } /// public int CmdShow { get; } /// public ResumeType Resume { get; } /// public IntPtr SplashScreen { get; } /// public RelationType Relation { get; } /// public bool Passthrough { get; } /// public string LayoutDirectory { get; } /// public string BootstrapperWorkingFolder { get; } /// public string BootstrapperApplicationDataPath { get; } /// public IMbaCommand ParseCommandLine() { var args = ParseCommandLineToArgs(this.CommandLine); var unknownArgs = new List(); var variables = new List>(); var restart = Restart.Unknown; foreach (var arg in args) { var unknownArg = false; if (arg[0] == '-' || arg[0] == '/') { var parameter = arg.Substring(1).ToLowerInvariant(); switch (parameter) { case "norestart": if (restart == Restart.Unknown) { restart = Restart.Never; } break; case "forcerestart": if (restart == Restart.Unknown) { restart = Restart.Always; } break; default: unknownArg = true; break; } } else { var index = arg.IndexOf('='); if (index == -1) { unknownArg = true; } else { var name = arg.Substring(0, index); var value = arg.Substring(index + 1); variables.Add(new KeyValuePair(name, value)); } } if (unknownArg) { unknownArgs.Add(arg); } } if (restart == Restart.Unknown) { restart = this.Display < Display.Full ? Restart.Automatic : Restart.Prompt; } return new MbaCommand { Restart = restart, UnknownCommandLineArgs = unknownArgs.ToArray(), Variables = variables.ToArray(), }; } /// /// Gets the command line arguments as a string array. /// /// /// Array of command line arguments. /// /// The command line could not be parsed into an array. /// /// This method uses the same parsing as the operating system which handles quotes and spaces correctly. /// public static string[] ParseCommandLineToArgs(string commandLine) { if (null == commandLine) { return new string[0]; } // Parse the filtered command line arguments into a native array. int argc = 0; // CommandLineToArgvW tries to treat the first argument as the path to the process, // which fails pretty miserably if your first argument is something like // FOO="C:\Program Files\My Company". So give it something harmless to play with. IntPtr argv = NativeMethods.CommandLineToArgvW("ignored " + commandLine, out argc); if (IntPtr.Zero == argv) { // Throw an exception with the last error. throw new Win32Exception(); } // Marshal each native array pointer to a managed string. try { // Skip "ignored" argument/hack. string[] args = new string[argc - 1]; for (int i = 1; i < argc; ++i) { IntPtr argvi = Marshal.ReadIntPtr(argv, i * IntPtr.Size); args[i - 1] = Marshal.PtrToStringUni(argvi); } return args; } finally { NativeMethods.LocalFree(argv); } } } }