Converted back to CommandlineParser

Converted back to CommandLineParser as there is better ability to
format the Help Text for error messages generated by additional checks
we're now performing after the commandline options have already been
parsed.
This commit is contained in:
temacdonald 2020-04-14 22:18:52 +12:00
parent 476d6bfcb5
commit c061c5c32c
3 changed files with 170 additions and 192 deletions

View File

@ -1,29 +1,39 @@
using System; using CommandLine;
using System.Linq; using CommandLine.Text;
using System.Collections.Generic;
using System.Windows.Forms;
//using CommandLine;
//using CommandLine.Text;
using HeliosDisplayManagement.Resources;
using HeliosDisplayManagement.Shared; using HeliosDisplayManagement.Shared;
using System.Runtime.CompilerServices; using System.Windows.Forms;
namespace HeliosDisplayManagement namespace HeliosDisplayManagement
{ {
internal class CommandLineOptions class CommandLineOptions
{ {
private CommandLineOptions()
{
}
public static HeliosStartupAction Action { get; set; } [Option('a', "action", Default = HeliosStartupAction.None, Required = true, HelpText = "The startup action to perform: None (Show Helios Display Management), SwitchProfile (Change to another profile and optionally run an application/game), CreateShortcut (Create a Desktop Shortcut), EditProfile (Edit a profile)")]
public static string ExecuteArguments { get; set; } public HeliosStartupAction Action { get; set; }
public static string ExecuteFilename { get; set; }
public static string ExecuteProcessName { get; set; } [Option('p', "profile", Required = true, HelpText = "The Profile Name or Profile ID of the profile to you want to use.")]
public static uint ExecuteProcessTimeout { get; set; } public string Profile { get; set; }
public static uint ExecuteSteamApp { get; set; }
public static string ProfileId { get; set; } [Option('e', "execute", SetName = "ByExecutable", Required = false, HelpText = "(optional) The application/game to start when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut. Cannot be used with --steam or --uplay options.")]
public string ExecuteFilename { get; set; }
[Option('w', "waitfor", SetName = "ByExecutable", Required = false, HelpText = "The process name to wait for when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut. Cannot be used with --steam or --uplay options.")]
public string ExecuteProcessName { get; set; }
[Option('s', "steam", SetName = "ByGameId", Required = false, HelpText = "The Steam AppID to run for when we're temporarily switching profile and running the Steam application/game. Also can be used when creating a shortcut. Cannot be used with -e or -w options.")]
public uint ExecuteSteamApp { get; set; }
[Option("uplay", SetName = "ByGameId", Required = false, HelpText = "(optional)The Uplay AppID to run for when we're temporarily switching profile and running the Uplay application/game. Also can be used when creating a shortcut. Cannot be used with -e or -w options.")]
public uint ExecuteUplayApp { get; set; }
[Option('t', "timeout", Default = 30u, Required = false, HelpText = "(optional) The time in seconds we should delay starting the application/game when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut.")]
public uint ExecuteProcessTimeout { get; set; }
[Option("arguments", Required = false, HelpText = "(optional) Extra arguments to pass to the application/game when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut.")]
public string ExecuteArguments { get; set; }
} }
} }

View File

@ -79,6 +79,8 @@
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<DependentUpon>Language.resx</DependentUpon> <DependentUpon>Language.resx</DependentUpon>
</Compile> </Compile>
<Compile Include="Uplay\UplayAppIdNamePair.cs" />
<Compile Include="Uplay\UplayGame.cs" />
<Compile Include="Steam\SteamGame.cs" /> <Compile Include="Steam\SteamGame.cs" />
<Compile Include="Steam\SteamAppIdNamePair.cs" /> <Compile Include="Steam\SteamAppIdNamePair.cs" />
<Compile Include="UIForms\EditForm.cs"> <Compile Include="UIForms\EditForm.cs">
@ -177,6 +179,9 @@
<PackageReference Include="CircularProgressBar"> <PackageReference Include="CircularProgressBar">
<Version>2.7.0.7</Version> <Version>2.7.0.7</Version>
</PackageReference> </PackageReference>
<PackageReference Include="CommandLineParser">
<Version>2.7.82</Version>
</PackageReference>
<PackageReference Include="HtmlAgilityPack"> <PackageReference Include="HtmlAgilityPack">
<Version>1.11.23</Version> <Version>1.11.23</Version>
</PackageReference> </PackageReference>
@ -186,12 +191,6 @@
<PackageReference Include="Newtonsoft.Json"> <PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version> <Version>12.0.3</Version>
</PackageReference> </PackageReference>
<PackageReference Include="System.CommandLine">
<Version>2.0.0-beta1.20158.1</Version>
</PackageReference>
<PackageReference Include="System.CommandLine.DragonFruit">
<Version>0.3.0-alpha.20158.1</Version>
</PackageReference>
<PackageReference Include="WindowsDisplayAPI"> <PackageReference Include="WindowsDisplayAPI">
<Version>1.3.0.13</Version> <Version>1.3.0.13</Version>
</PackageReference> </PackageReference>

View File

@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.CommandLine; using CommandLine;
using System.CommandLine.Invocation; using CommandLine.Text;
//using System.CommandLine.DragonFruit;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -15,7 +14,9 @@ using HeliosDisplayManagement.InterProcess;
using HeliosDisplayManagement.Resources; using HeliosDisplayManagement.Resources;
using HeliosDisplayManagement.Shared; using HeliosDisplayManagement.Shared;
using HeliosDisplayManagement.Steam; using HeliosDisplayManagement.Steam;
using HeliosDisplayManagement.Uplay;
using HeliosDisplayManagement.UIForms; using HeliosDisplayManagement.UIForms;
using System.Net.NetworkInformation;
namespace HeliosDisplayManagement namespace HeliosDisplayManagement
{ {
@ -64,7 +65,7 @@ namespace HeliosDisplayManagement
} }
} }
private static void CreateShortcut(IReadOnlyList<Profile> profiles, int profileIndex) private static void CreateShortcut(IReadOnlyList<Profile> profiles, int profileIndex, CommandLineOptions options)
{ {
if (profileIndex < 0) if (profileIndex < 0)
{ {
@ -74,15 +75,15 @@ namespace HeliosDisplayManagement
IPCService.GetInstance().Status = InstanceStatus.User; IPCService.GetInstance().Status = InstanceStatus.User;
new ShortcutForm(profiles[profileIndex]) new ShortcutForm(profiles[profileIndex])
{ {
FileName = CommandLineOptions.ExecuteFilename, FileName = options.ExecuteFilename,
SteamAppId = CommandLineOptions.ExecuteSteamApp, SteamAppId = options.ExecuteSteamApp,
Arguments = CommandLineOptions.ExecuteArguments, Arguments = options.ExecuteArguments,
ProcessName = CommandLineOptions.ExecuteProcessName, ProcessName = options.ExecuteProcessName,
Timeout = CommandLineOptions.ExecuteProcessTimeout Timeout = options.ExecuteProcessTimeout
}.ShowDialog(); }.ShowDialog();
} }
private static void EditProfile(IList<Profile> profiles, int profileIndex) private static void EditProfile(IList<Profile> profiles, int profileIndex, CommandLineOptions options)
{ {
if (profileIndex < 0) if (profileIndex < 0)
{ {
@ -110,83 +111,48 @@ namespace HeliosDisplayManagement
[STAThread] [STAThread]
private static int Main(string[] args) private static int Main(string[] args)
{ {
var cmd = new RootCommand();
cmd.Description = "This is an application and things.";
cmd.Add(new Option<HeliosStartupAction>(
aliases: new string[] {"--action", "-a"},
getDefaultValue :() => HeliosStartupAction.None,
description: "(required) The startup action to perform: None (Do nothing), SwitchProfile (Change to another profile and optionally run an application/game), CreateShortcut (Create a Desktop Shortcut), EditProfile (Edit a profile)"
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--profile-id", "-p" },
description: "(required) UUID string that selects the profile to use."
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--arguments" },
description: "(optional) Extra arguments to pass to the application/game when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--execute", "-e" },
description: "(optional) The application/game to start when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<string>(
aliases: new string[] { "--waitfor", "-w" },
description: "(optional) The process name to wait for when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--timeout", "-t" },
description: "(optional) The time in seconds we should delay starting the application/game when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--steam", "-s" },
description: "(optional)The Steam AppID to run for when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
/*cmd.Add(new Option<uint>(
aliases: new string[] { "--uplay" },
description: "(optional)The Uplay ID to run when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--epic" },
description: "(optional)The Epic ID to run when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);
cmd.Add(new Option<uint>(
aliases: new string[] { "--origin" },
description: "(optional)The Origin ID to run when we're temporarily switching profile and running the application/game. Also can be used when creating a shortcut."
)
);*/
cmd.Handler = CommandHandler.Create<HeliosStartupAction, string, string, string, string, uint, uint>((action, profileId, arguments, execute, waitfor, timeout, steam) => /*return Parser.Default.ParseArguments<CommandLineOptions>(args).MapResult(
options => RunOptions(options),
_ => 1);*/
//var parser = new CommandLine.Parser(with => with.HelpWriter = null);
//var parserResult = parser.ParseArguments<CommandLineOptions, object>(args)
var result = Parser.Default.ParseArguments<CommandLineOptions>(args)
.MapResult(
options => RunOptions(options),
_ => 1);
return result;
}
static void DisplayHelp<T>(ParserResult<T> result, IEnumerable<Error> errs)
{ {
var helpText = HelpText.AutoBuild(result, h =>
{
h.AdditionalNewLineAfterOption = false;
h.Heading = "Myapp 2.0.0-beta"; //change header
h.Copyright = "Copyright (c) 2019 Global.com"; //change copyright text
return HelpText.DefaultParsingErrorsHandler(result, h);
}, e => e);
Console.WriteLine(helpText);
}
static int RunOptions(CommandLineOptions options)
{
// Validate combinations of Command line options // Validate combinations of Command line options
// If a profileId is supplied then we need an action other than None // If a profileId is supplied then we need an action other than None
if (action == HeliosStartupAction.None && !String.IsNullOrEmpty(profileId)) if (options.Action == HeliosStartupAction.None && !String.IsNullOrEmpty(options.Profile))
{ {
Console.WriteLine("Error - If you supply a Profile ID or Name then you must also provide an Action."); Console.WriteLine("Error - If you supply a Profile ID or Name then you must also provide an Action.");
return 1; return 1;
} }
//var help = new System.CommandLine.Help.HelpBuilder(invocationContext.Console); Console.WriteLine(CommandLine.Parser.Default.FormatCommandLine(options));
//Console.WriteLine(help);
if (options.Action != HeliosStartupAction.None && String.IsNullOrEmpty(options.Profile))
// Save these in CommandLineOptions for easy access from other parts of the application {
CommandLineOptions.Action = action; Console.WriteLine("Error - If you want to perform an Action then you must also provide a Profile ID or Name.");
CommandLineOptions.ProfileId = profileId; return 1;
CommandLineOptions.ExecuteArguments = arguments; }
CommandLineOptions.ExecuteFilename = execute;
CommandLineOptions.ExecuteProcessName = waitfor;
CommandLineOptions.ExecuteProcessTimeout = timeout;
CommandLineOptions.ExecuteSteamApp = steam;
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
@ -202,32 +168,39 @@ namespace HeliosDisplayManagement
// Create an array of profiles // Create an array of profiles
var profiles = Profile.GetAllProfiles().ToArray(); var profiles = Profile.GetAllProfiles().ToArray();
// Show the user the profiles if they want to look // Show the user the profiles if they want to look
foreach (Profile profile in profiles) foreach (Profile aprofile in profiles)
{ {
Console.WriteLine($"Found Profile: {profile.Name} (ID:{profile.Id})"); Console.WriteLine($"Found Profile: {aprofile.Name} (ID:{aprofile.Id})");
} }
// Try and lookup the profileId in the profileIndex // Try and lookup the profile in the profiles' ID fields
var profileIndex = profiles.Length > 0 ? Array.FindIndex(profiles, p => p.Id.Equals(CommandLineOptions.ProfileId, StringComparison.InvariantCultureIgnoreCase)) : -1; var profileIndex = profiles.Length > 0 ? Array.FindIndex(profiles, p => p.Id.Equals(options.Profile, StringComparison.InvariantCultureIgnoreCase)) : -1;
// If the profileID wasn't there, maybe they used the profile name? // If the profileID wasn't there, maybe they used the profile name?
if (profileIndex == -1) if (profileIndex == -1)
{ {
profileIndex = profiles.Length > 0 ? Array.FindIndex(profiles, p => p.Name.Equals(CommandLineOptions.ProfileId, StringComparison.InvariantCultureIgnoreCase)) : -1; // Try and lookup the profile in the profiles' Name fields
profileIndex = profiles.Length > 0 ? Array.FindIndex(profiles, p => p.Name.Equals(options.Profile, StringComparison.InvariantCultureIgnoreCase)) : -1;
}
// If the profileID still isn't there, then raise the alarm
if (profileIndex == -1)
{
Console.WriteLine($"Error - Couldn't find Profile Name or ID supplied via command line: \"{options.Profile}\". Please check the Profile Name or ID you supplied is correct.");
return 1;
} }
Console.WriteLine($"Using Profile: {profiles[profileIndex].Name} (ID:{profiles[profileIndex].Id})"); Console.WriteLine($"Using Profile: {profiles[profileIndex].Name} (ID:{profiles[profileIndex].Id})");
switch (CommandLineOptions.Action) switch (options.Action)
{ {
case HeliosStartupAction.SwitchProfile: case HeliosStartupAction.SwitchProfile:
SwitchProfile(profiles, profileIndex); SwitchProfile(profiles, profileIndex, options);
break; break;
case HeliosStartupAction.EditProfile: case HeliosStartupAction.EditProfile:
EditProfile(profiles, profileIndex); EditProfile(profiles, profileIndex, options);
break; break;
case HeliosStartupAction.CreateShortcut: case HeliosStartupAction.CreateShortcut:
CreateShortcut(profiles, profileIndex); CreateShortcut(profiles, profileIndex, options);
break; break;
default: default:
@ -248,18 +221,14 @@ namespace HeliosDisplayManagement
MessageBoxIcon.Error); MessageBoxIcon.Error);
} }
return 0; return 0;
}); }
static void HandleParseError(IEnumerable<Error> errs)
var result = cmd.InvokeAsync(args).Result; {
//handle errors
return result;
} }
// ReSharper disable once CyclomaticComplexity // ReSharper disable once CyclomaticComplexity
private static void SwitchProfile(IReadOnlyList<Profile> profiles, int profileIndex) private static void SwitchProfile(IReadOnlyList<Profile> profiles, int profileIndex, CommandLineOptions options)
{ {
var rollbackProfile = Profile.GetCurrent(string.Empty); var rollbackProfile = Profile.GetCurrent(string.Empty);
@ -285,9 +254,9 @@ namespace HeliosDisplayManagement
.Another_instance_of_this_program_is_in_working_state_Please_close_other_instances_before_trying_to_switch_profile); .Another_instance_of_this_program_is_in_working_state_Please_close_other_instances_before_trying_to_switch_profile);
} }
if (!string.IsNullOrWhiteSpace(CommandLineOptions.ExecuteFilename)) if (!string.IsNullOrWhiteSpace(options.ExecuteFilename))
{ {
if (!File.Exists(CommandLineOptions.ExecuteFilename)) if (!File.Exists(options.ExecuteFilename))
{ {
throw new Exception(Language.Executable_file_not_found); throw new Exception(Language.Executable_file_not_found);
} }
@ -297,17 +266,17 @@ namespace HeliosDisplayManagement
throw new Exception(Language.Can_not_change_active_profile); throw new Exception(Language.Can_not_change_active_profile);
} }
var process = System.Diagnostics.Process.Start(CommandLineOptions.ExecuteFilename, var process = System.Diagnostics.Process.Start(options.ExecuteFilename,
CommandLineOptions.ExecuteArguments); options.ExecuteArguments);
var processes = new System.Diagnostics.Process[0]; var processes = new System.Diagnostics.Process[0];
if (!string.IsNullOrWhiteSpace(CommandLineOptions.ExecuteProcessName)) if (!string.IsNullOrWhiteSpace(options.ExecuteProcessName))
{ {
var ticks = 0; var ticks = 0;
while (ticks < CommandLineOptions.ExecuteProcessTimeout * 1000) while (ticks < options.ExecuteProcessTimeout * 1000)
{ {
processes = System.Diagnostics.Process.GetProcessesByName(CommandLineOptions.ExecuteProcessName); processes = System.Diagnostics.Process.GetProcessesByName(options.ExecuteProcessName);
if (processes.Length > 0) if (processes.Length > 0)
{ {
@ -374,9 +343,9 @@ namespace HeliosDisplayManagement
} }
} }
} }
else if (CommandLineOptions.ExecuteSteamApp > 0) else if (options.ExecuteSteamApp > 0)
{ {
var steamGame = new SteamGame(CommandLineOptions.ExecuteSteamApp); var steamGame = new SteamGame(options.ExecuteSteamApp);
if (!SteamGame.SteamInstalled) if (!SteamGame.SteamInstalled)
{ {
@ -405,16 +374,16 @@ namespace HeliosDisplayManagement
var address = $"steam://rungameid/{steamGame.AppId}"; var address = $"steam://rungameid/{steamGame.AppId}";
if (!string.IsNullOrWhiteSpace(CommandLineOptions.ExecuteArguments)) if (!string.IsNullOrWhiteSpace(options.ExecuteArguments))
{ {
address += "/" + CommandLineOptions.ExecuteArguments; address += "/" + options.ExecuteArguments;
} }
var steamProcess = System.Diagnostics.Process.Start(address); var steamProcess = System.Diagnostics.Process.Start(address);
// Wait for steam game to update and then run // Wait for steam game to update and then run
var ticks = 0; var ticks = 0;
while (ticks < CommandLineOptions.ExecuteProcessTimeout * 1000) while (ticks < options.ExecuteProcessTimeout * 1000)
{ {
if (steamGame.IsRunning) if (steamGame.IsRunning)
{ {