public static bool IsAsync(ConsoleCommand command, CommandList commands) { // Validate the command name: var found = false; Dictionary<string, ConsoleExecutingMethod> methodDictionary = null; foreach (var key in commands.Keys.Where(key => key.Namespace == command.LibraryClassName)) { found = true; methodDictionary = commands[key]; break; } if (!found) { return false; } if (!methodDictionary.ContainsKey(command.Name)) { var newCommand = char.ToUpperInvariant(command.Name[0]) + command.Name.Substring(1); if (!methodDictionary.ContainsKey(newCommand)) { return false; } command.Name = char.ToUpperInvariant(command.Name[0]) + command.Name.Substring(1); } return methodDictionary[command.Name].Async; }
public static async Task<ConsoleExecuteResult> ExecuteAsync(ConsoleCommand command, CommandList commands) { ConsoleExecutingAssembly instance; Dictionary<string, ConsoleExecutingMethod> methodDictionary; if (!ValidateCommand(command, commands, out instance, out methodDictionary)) { return new ConsoleExecuteResult($"Unrecognized command \'{command.LibraryClassName}.{command.Name}\'. " + "Please type a valid command."); } object[] inputArgs; Type typeInfo; string errorMessage; if (BuildCommand(command, instance, methodDictionary, out inputArgs, out typeInfo, out errorMessage)) { // This will throw if the number of arguments provided does not match the number // required by the method signature, even if some are optional: int? id = -1; try { //add the command to the current macro if capture is enabled id = _consoleMacro?.Add(command.RawCommand); var result = await ((Task<ConsoleExecuteResult>) typeInfo.InvokeMember( command.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, instance.Instance, inputArgs)).ConfigureAwait(false); return result; } catch (TargetInvocationException ex) { //remove bad commands from the macro if (id.HasValue && id > -1) { _consoleMacro?.Remove(id.Value); } throw ex.InnerException; } } return new ConsoleExecuteResult(errorMessage); }
private static bool BuildCommand(ConsoleCommand command, ConsoleExecutingAssembly instance, IReadOnlyDictionary<string, ConsoleExecutingMethod> methodDictionary, out object[] inputArgs, out Type typeInfo, out string errorMessage) { inputArgs = null; typeInfo = null; errorMessage = null; // Make sure the correct number of required arguments are provided: // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ var methodParameterValueList = new List<object>(); IEnumerable<ParameterInfo> paramInfoList = methodDictionary[command.Name].Parameters.ToList(); // Validate proper # of required arguments provided. Some may be optional: var requiredParams = paramInfoList.Where(p => !p.IsOptional).ToList(); var optionalParams = paramInfoList.Where(p => p.IsOptional).ToList(); var requiredCount = requiredParams.Count; var optionalCount = optionalParams.Count; var providedCount = command.Arguments.Count(); if (requiredCount > providedCount) { var message = new StringBuilder(); message.AppendLine( $"Missing required argument. {requiredCount} required, {optionalCount} optional, {providedCount} provided"); if (requiredCount > 0) { message.AppendLine(""); message.AppendLine("Required"); foreach (var required in requiredParams) { message.Append(required.Name); message.Append(":"); message.AppendLine(required.ParameterType.ToString()); } } if (optionalParams.Count > 0) { message.AppendLine(""); message.AppendLine("Optional"); foreach (var required in optionalParams) { message.Append(required.Name); message.Append(":"); message.Append(required.ParameterType); message.Append(":"); message.AppendLine(required.RawDefaultValue?.ToString() ?? "null"); } } errorMessage = message.ToString(); return false; } // Make sure all arguments are coerced to the proper type, and that there is a // value for every method parameter. The InvokeMember method fails if the number // of arguments provided does not match the number of parameters in the // method signature, even if some are optional: // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (paramInfoList.Any()) { // Populate the list with default values: methodParameterValueList.AddRange(paramInfoList.Select(param => param.DefaultValue)); // Now walk through all the arguments passed from the console and assign // accordingly. Any optional arguments not provided have already been set to // the default specified by the method signature: for (var i = 0; i < command.Arguments.Count(); i++) { var methodParam = paramInfoList.ElementAt(i); var typeRequired = methodParam.ParameterType; try { // Coming from the Console, all of our arguments are passed in as // strings. Coerce to the type to match the method parameter: var value = ConsoleParseArgument.CoerceArgument(typeRequired, command.Arguments.ElementAt(i)); methodParameterValueList.RemoveAt(i); methodParameterValueList.Insert(i, value); } // ReSharper disable once UncatchableException catch (ArgumentException ex) { var argumentName = methodParam.Name; var argumentTypeName = typeRequired.Name; var message = $"The value passed for argument '{argumentName}' cannot be parsed to type '{argumentTypeName}'"; throw new ArgumentException(message, ex); } } } // Set up to invoke the method using reflection: // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Need the full Namespace for this: var commandLibraryClass = instance.Instance.GetType(); if (methodParameterValueList.Count > 0) { inputArgs = methodParameterValueList.ToArray(); } typeInfo = commandLibraryClass; return true; }
private static bool ValidateCommand(ConsoleCommand command, CommandList commands, out ConsoleExecutingAssembly instance, out Dictionary<string, ConsoleExecutingMethod> methodDictionary) { instance = null; methodDictionary = null; // Validate the command name: var found = false; foreach (var key in commands.Keys.Where(key => key.Namespace == command.LibraryClassName)) { found = true; instance = key; methodDictionary = commands[key]; break; } if (!found) { return false; } if (!methodDictionary.ContainsKey(command.Name)) { var newCommand = char.ToUpperInvariant(command.Name[0]) + command.Name.Substring(1); if (!methodDictionary.ContainsKey(newCommand)) { return false; } command.Name = char.ToUpperInvariant(command.Name[0]) + command.Name.Substring(1); } return true; }
/// <summary> /// Shells the control1 on command entered. /// </summary> /// <param name="sender">The sender.</param> /// <param name="commandEnteredEventArgs">The <see cref="CommandEnteredEventArgs"/> instance containing the event data.</param> private async void ShellControl1OnCommandEntered(object sender, CommandEnteredEventArgs commandEnteredEventArgs) { if (string.IsNullOrWhiteSpace(commandEnteredEventArgs.Command)) return; var async = false; try { // Create a ConsoleCommand instance: var cmd = new ConsoleCommand(commandEnteredEventArgs.Command.Trim().Replace("\t", "")); // Execute the command: ConsoleExecuteResult result; if (ConsoleExecute.IsAsync(cmd, _commandLibraries)) { Interlocked.Increment(ref _asyncCount); async = true; result = await ConsoleExecute.ExecuteAsync(cmd, _commandLibraries).ConfigureAwait(false); } else { result = ConsoleExecute.Execute(cmd, _commandLibraries); } // Write out the result: WriteToConsole(result.Message); if (result.Action != null && result.Action.Action == ConsoleExecuteActions.Exit) { //If we exit here and async tasks are running, we won't ever finish them. That's because they are tied to the shell's command event... if (Interlocked.Read(ref _asyncCount) == 0) { shellControl1.CommandEntered -= ShellControl1OnCommandEntered; Close(); } else { WriteToConsole($"Async tasks are still running {Interlocked.Read(ref _asyncCount)}"); } } else if (result.Action != null && result.Action.Action == ConsoleExecuteActions.StatusUri) { queueStatusControl1.Display(result.Action.Target); } else if (result.Action != null && result.Action.Action == ConsoleExecuteActions.StartProcess) { Process.Start(result.Action.Target); } else if (result.Action != null && result.Action.Action == ConsoleExecuteActions.StartMacro) { ConsoleExecute.StartMacroCapture(); } else if (result.Action != null && result.Action.Action == ConsoleExecuteActions.CancelMacro) { ConsoleExecute.CancelMacroCapture(); } else if (result.Action != null && result.Action.Action == ConsoleExecuteActions.SaveMacro) { ConsoleExecute.SaveMacro(Path.Combine(Path.GetDirectoryName(_commandAssembly.Location) + @"\Macro\", result.Action.Target)); } else if (result.Action != null && result.Action.Action == ConsoleExecuteActions.RunMacro) { shellControl1.PrintLine(); foreach(ConsoleExecuteResult command in ConsoleExecute.RunMacro(Path.Combine(Path.GetDirectoryName(_commandAssembly.Location) + @"\Macro\", result.Action.Target))) { if (command.Action.Action == ConsoleExecuteActions.RunCommand) { shellControl1.SendCommand(command.Action.Target); } } } } catch (Exception ex) { WriteToConsole(ex.ToString()); } finally { if (async) { Interlocked.Decrement(ref _asyncCount); } } }