using System.Diagnostics; namespace Plandent.MstscScriptHook.Configurator; internal static class ScriptTestLauncher { public static Process StartConnectionProgram(HookConfig config) => Start( config.ScriptPath, config.Arguments, config.WorkingDirectory, config.Hidden); public static Process StartTriggerProgram(HookConfig config) => Start( config.TriggerProgramPath, config.TriggerArguments, config.TriggerWorkingDirectory, config.TriggerHidden); private static Process Start(string configuredPath, string configuredArguments, string configuredWorkingDirectory, bool hidden) { string programPath = Environment.ExpandEnvironmentVariables(configuredPath ?? string.Empty); if (string.IsNullOrWhiteSpace(programPath) || !File.Exists(programPath)) { throw new FileNotFoundException("Das ausgewählte Programm/Script wurde nicht gefunden.", programPath); } string extension = Path.GetExtension(programPath).ToLowerInvariant(); string workingDirectory = string.IsNullOrWhiteSpace(configuredWorkingDirectory) ? Path.GetDirectoryName(programPath) ?? Environment.CurrentDirectory : Environment.ExpandEnvironmentVariables(configuredWorkingDirectory); var startInfo = new ProcessStartInfo { UseShellExecute = false, CreateNoWindow = hidden, WindowStyle = hidden ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal, WorkingDirectory = workingDirectory }; switch (extension) { case ".bat": case ".cmd": startInfo.FileName = Environment.GetEnvironmentVariable("ComSpec") ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe"); startInfo.ArgumentList.Add("/D"); startInfo.ArgumentList.Add("/S"); startInfo.ArgumentList.Add("/C"); startInfo.ArgumentList.Add(BuildCmdCommand(programPath, configuredArguments)); break; case ".ps1": startInfo.FileName = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.System), "WindowsPowerShell", "v1.0", "powershell.exe"); startInfo.ArgumentList.Add("-NoLogo"); startInfo.ArgumentList.Add("-NoProfile"); startInfo.ArgumentList.Add("-NonInteractive"); startInfo.ArgumentList.Add("-ExecutionPolicy"); startInfo.ArgumentList.Add("Bypass"); startInfo.ArgumentList.Add("-File"); startInfo.ArgumentList.Add(programPath); AddRawArguments(startInfo, configuredArguments); break; case ".exe": startInfo.FileName = programPath; AddRawArguments(startInfo, configuredArguments); break; default: throw new NotSupportedException("Unterstützt werden .bat, .cmd, .ps1 und .exe."); } return Process.Start(startInfo) ?? throw new InvalidOperationException("Der Testprozess konnte nicht gestartet werden."); } private static string BuildCmdCommand(string programPath, string? arguments) { string command = QuoteForCmd(programPath); if (!string.IsNullOrWhiteSpace(arguments)) { command += " " + arguments; } return command; } private static string QuoteForCmd(string value) { return "\"" + value.Replace("\"", "\\\"") + "\""; } private static void AddRawArguments(ProcessStartInfo startInfo, string? raw) { if (string.IsNullOrWhiteSpace(raw)) { return; } foreach (string argument in CommandLineSplitter.Split(raw)) { startInfo.ArgumentList.Add(argument); } } } internal static class CommandLineSplitter { public static IReadOnlyList Split(string commandLine) { var result = new List(); var current = new System.Text.StringBuilder(); bool inQuotes = false; int backslashes = 0; for (int i = 0; i < commandLine.Length; i++) { char ch = commandLine[i]; if (ch == '\\') { backslashes++; continue; } if (ch == '"') { current.Append('\\', backslashes / 2); if (backslashes % 2 == 0) { inQuotes = !inQuotes; } else { current.Append('"'); } backslashes = 0; continue; } current.Append('\\', backslashes); backslashes = 0; if (char.IsWhiteSpace(ch) && !inQuotes) { if (current.Length > 0) { result.Add(current.ToString()); current.Clear(); } } else { current.Append(ch); } } current.Append('\\', backslashes); if (current.Length > 0) { result.Add(current.ToString()); } return result; } }