/// Execute action public override object Execute() { TimeSpan?ts = Utils.ToTimeSpan(Context.TransformStr(Timeout, Transform)); if (!ts.HasValue || ts.Value.TotalMilliseconds == 0) { return(null); } VerboseMessage("Sleeping for {0}", ts); Stopwatch w = Stopwatch.StartNew(); var totalMs = ts.Value.TotalMilliseconds; using (var s = new WaitableTimer(ts)) { while (!s.WaitHandle.WaitOne(200, false)) { Context.OnProgress((int)(w.ElapsedMilliseconds / totalMs * 100)); } } return(null); }
/// Execute action public override object Execute() { string args = (GetTransformedValueStr() ?? string.Empty).Trim(); ShellMode t = this.Mode; if (t == ShellMode.Auto) { if (string.IsNullOrEmpty(Verb)) { t = ShellMode.Comspec; } else { t = ShellMode.ShellExecute; } } string outp = Context.TransformStr(OutTo, Transform); string errorp = Context.TransformStr(ErrorTo, Transform); string enc = Context.TransformStr(Encoding, Transform); object input = Context.Transform(Input, Transform); bool redirectError = !string.IsNullOrEmpty(errorp); bool redirectOutput = !string.IsNullOrEmpty(outp); ProcessStartInfo pi = new ProcessStartInfo(); if (!string.IsNullOrEmpty(enc)) { pi.StandardOutputEncoding = pi.StandardErrorEncoding = Utils.GetEncoding(enc); } pi.CreateNoWindow = CreateNoWindow; pi.WindowStyle = WindowStyle; pi.ErrorDialog = false; pi.WorkingDirectory = Context.TransformStr(Directory, Transform); if (string.IsNullOrEmpty(pi.WorkingDirectory)) { pi.WorkingDirectory = Environment.CurrentDirectory; } string fileToDelete = null; // do a bit of clean up occasionally by deleting older xsh-batch-*.cmd files if (Utils.GenRandom(0, 5) == 3) { try { VerboseMessage("Cleaning up older batch files in temp directory"); var dt = DateTime.UtcNow.AddDays(-1); foreach (var file in new DirectoryInfo(Path.GetTempPath()).GetFileSystemInfos("xsh-batch-*.cmd")) { if (file.LastWriteTimeUtc < dt) { file.Delete(); } } } catch { } } try { switch (t) { case ShellMode.Batch: fileToDelete = Path.Combine(Path.GetTempPath(), "xsh-batch-" + Utils.GenRandom(0, int.MaxValue) + "-" + Utils.GenRandom(0, int.MaxValue) + ".cmd"); File.WriteAllText(fileToDelete, args, System.Text.Encoding.Default); args = Utils.QuoteArg(fileToDelete); if (!Wait) { fileToDelete = null; // don't delete the batch if we're not waiting for it to complete } goto default; default: string cmd = Utils.QuoteArg(Environment.ExpandEnvironmentVariables("%COMSPEC%")); if (!string.IsNullOrEmpty(args)) { cmd += " " + args; } fillFilenameAndArguments(pi, cmd); pi.Arguments = " /C \"" + pi.Arguments + "\""; pi.UseShellExecute = false; pi.RedirectStandardError = redirectError && Wait; pi.RedirectStandardOutput = redirectOutput && Wait; pi.RedirectStandardInput = (input != null) && Wait; break; case ShellMode.Direct: fillFilenameAndArguments(pi, args); pi.UseShellExecute = false; pi.RedirectStandardError = redirectError && Wait; pi.RedirectStandardOutput = redirectOutput && Wait; pi.RedirectStandardInput = (input != null) && Wait; break; case ShellMode.ShellExecute: pi.UseShellExecute = true; fillFilenameAndArguments(pi, args); if (!string.IsNullOrEmpty(Verb)) { pi.Verb = Verb; } break; } TimeSpan?ts = null; if (!string.IsNullOrEmpty(Timeout)) { ts = Utils.ToTimeSpan(Context.TransformStr(Timeout, Transform)); } VerboseMessage("Executing " + Utils.QuoteArg(pi.FileName) + " " + pi.Arguments); if (!string.IsNullOrEmpty(pi.UserName)) { pi.UserName = Context.TransformStr(Username, Transform); var ss = new SecureString(); foreach (var c in (Context.TransformStr(Password, Transform) ?? string.Empty)) { ss.AppendChar(c); } pi.Password = ss; pi.LoadUserProfile = LoadUserProfile; } using (ManualResetEvent terminated = new ManualResetEvent(false)) using (WaitableTimer timeout = new WaitableTimer(ts)) { ExitedWithContext ect = new ExitedWithContext(terminated); using (Process p = Process.Start(pi)) { if (!string.IsNullOrEmpty(ProcessIdTo)) { Context.OutTo(Context.TransformStr(ProcessIdTo, Transform), p.Id); } if (Wait && p != null) { Redir[] r = new Redir[2] { new Redir(Context, outp), new Redir(Context, errorp) }; p.Exited += ect.onExited; p.EnableRaisingEvents = true; AsyncWriter asyncWriter = null; try { if (pi.RedirectStandardInput) { byte[] data; if (input is byte[]) { data = ((byte[])input); } else { Encoding en = System.Text.Encoding.Default; if (string.IsNullOrEmpty(enc)) { en = Utils.GetEncoding(enc); } if (en == null) { en = System.Text.Encoding.Default; } data = en.GetBytes(Utils.To <string>(input)); } asyncWriter = new AsyncWriter(p.StandardInput, data); } if (pi.RedirectStandardOutput) { if (BinaryOutput) { r[0].StartRedirect(p.StandardOutput.BaseStream); } else { r[0].StartRedirect(p.StandardOutput.BaseStream, p.StandardOutput.CurrentEncoding); } } if (pi.RedirectStandardError) { r[1].StartRedirect(p.StandardError.BaseStream, p.StandardError.CurrentEncoding); } var wh = new WaitHandle[] { r[0].Event, r[1].Event, terminated, timeout.WaitHandle }; do { Context.OnProgress(1); int n = WaitHandle.WaitAny(wh, 500, true); switch (n) { case 0: case 1: r[n].Flush(false); break; case 2: break; // Exit case 3: throw new TimeoutException("Command execution timed out"); } } while (!p.HasExited); // must wait to ensure that all output is flushed p.WaitForExit(); } finally { try { if (!p.HasExited && !p.WaitForExit(1000)) { VerboseMessage("Process didn't terminate as expected. TASKKILL will be used."); Shell sh = new Shell(Utils.BackslashAdd(Environment.GetFolderPath(Environment.SpecialFolder.System)) + "TASKKILL.EXE /T /F /pid " + p.Id); sh.CreateNoWindow = true; sh.Mode = ShellMode.Comspec; sh.IgnoreExitCode = true; Context.InitializeAndExecute(sh); VerboseMessage("TASKKILL completed"); } VerboseMessage("Waiting for process to terminate completely."); p.WaitForExit(); } catch { VerboseMessage("Failed to wait until the process is terminated"); } try { if (!p.HasExited) { p.Kill(); } } catch { VerboseMessage("Kill failed"); } r[0].Flush(true); r[1].Flush(true); r[0].Dispose(); r[1].Dispose(); terminated.WaitOne(1000, false); // Restore p.Exited -= ect.onExited; } int exitCode = p.ExitCode; if (!string.IsNullOrEmpty(ExitCodeTo)) { Context.OutTo(Context.TransformStr(ExitCodeTo, Transform), exitCode); } VerboseMessage("Execution completed with exit code={0}", exitCode); if (exitCode != 0 && !IgnoreExitCode) { string a = Utils.QuoteArg(pi.FileName); if (!string.IsNullOrEmpty(pi.Arguments)) { if (!ExtendedErrors) { a += " ... arguments ..."; } else { a += " " + pi.Arguments; } } throw new ScriptRuntimeException(string.Format("Command [{0}] failed with exit code = {1}", a, p.ExitCode)); } } } } } finally { if (fileToDelete != null) { try { File.Delete(fileToDelete); } catch { } } } return(null); }
/// Execute action public override object Execute() { TimeSpan? ts = Utils.ToTimeSpan(Context.TransformStr(Timeout, Transform)); if (!ts.HasValue || ts.Value.TotalMilliseconds==0) return null; VerboseMessage("Sleeping for {0}", ts); Stopwatch w=Stopwatch.StartNew(); var totalMs = ts.Value.TotalMilliseconds; using (var s=new WaitableTimer(ts)) { while (!s.WaitHandle.WaitOne(200, false)) Context.OnProgress((int)(w.ElapsedMilliseconds / totalMs * 100)); } return null; }
/// Execute action public override object Execute() { string args = (GetTransformedValueStr()??string.Empty).Trim(); ShellMode t = this.Mode; if (t == ShellMode.Auto) { if (string.IsNullOrEmpty(Verb)) t = ShellMode.Comspec; else t=ShellMode.ShellExecute; } string outp = Context.TransformStr(OutTo, Transform); string errorp = Context.TransformStr(ErrorTo, Transform); string enc = Context.TransformStr(Encoding, Transform); object input = Context.Transform(Input, Transform); bool redirectError = !string.IsNullOrEmpty(errorp); bool redirectOutput = !string.IsNullOrEmpty(outp); ProcessStartInfo pi = new ProcessStartInfo(); if (!string.IsNullOrEmpty(enc)) pi.StandardOutputEncoding = pi.StandardErrorEncoding = Utils.GetEncoding(enc); pi.CreateNoWindow = CreateNoWindow; pi.WindowStyle = WindowStyle; pi.ErrorDialog = false; pi.WorkingDirectory = Context.TransformStr(Directory, Transform); if (string.IsNullOrEmpty(pi.WorkingDirectory)) pi.WorkingDirectory = Environment.CurrentDirectory; string fileToDelete = null; // do a bit of clean up occasionally by deleting older xsh-batch-*.cmd files if (Utils.GenRandom(0,5)==3) { try { VerboseMessage("Cleaning up older batch files in temp directory"); var dt = DateTime.UtcNow.AddDays(-1); foreach (var file in new DirectoryInfo(Path.GetTempPath()).GetFileSystemInfos("xsh-batch-*.cmd")) { if (file.LastWriteTimeUtc<dt) file.Delete(); } } catch { } } try { switch (t) { case ShellMode.Batch: fileToDelete = Path.Combine(Path.GetTempPath(), "xsh-batch-" +Utils.GenRandom(0, int.MaxValue) + "-" + Utils.GenRandom(0, int.MaxValue) + ".cmd"); File.WriteAllText(fileToDelete, args, System.Text.Encoding.Default); args = Utils.QuoteArg(fileToDelete); if (!Wait) fileToDelete = null; // don't delete the batch if we're not waiting for it to complete goto default; default: string cmd = Utils.QuoteArg(Environment.ExpandEnvironmentVariables("%COMSPEC%")) ; if (!string.IsNullOrEmpty(args)) cmd += " " + args; fillFilenameAndArguments(pi, cmd); pi.Arguments = " /C \"" + pi.Arguments + "\""; pi.UseShellExecute = false; pi.RedirectStandardError = redirectError && Wait; pi.RedirectStandardOutput = redirectOutput && Wait; pi.RedirectStandardInput = (input != null) && Wait; break; case ShellMode.Direct: fillFilenameAndArguments(pi, args); pi.UseShellExecute = false; pi.RedirectStandardError = redirectError && Wait; pi.RedirectStandardOutput = redirectOutput && Wait; pi.RedirectStandardInput = (input != null) && Wait; break; case ShellMode.ShellExecute: pi.UseShellExecute = true; fillFilenameAndArguments(pi, args); if (!string.IsNullOrEmpty(Verb)) pi.Verb = Verb; break; } TimeSpan? ts = null; if (!string.IsNullOrEmpty(Timeout)) ts = Utils.ToTimeSpan(Context.TransformStr(Timeout, Transform)); VerboseMessage("Executing " + Utils.QuoteArg(pi.FileName) + " " + pi.Arguments); if (!string.IsNullOrEmpty(pi.UserName)) { pi.UserName = Context.TransformStr(Username, Transform); var ss = new SecureString(); foreach (var c in (Context.TransformStr(Password, Transform) ?? string.Empty)) ss.AppendChar(c); pi.Password = ss; pi.LoadUserProfile = LoadUserProfile; } using (ManualResetEvent terminated = new ManualResetEvent(false)) using (WaitableTimer timeout = new WaitableTimer(ts)) { ExitedWithContext ect = new ExitedWithContext(terminated); using (Process p = Process.Start(pi)) { if (!string.IsNullOrEmpty(ProcessIdTo)) Context.OutTo(Context.TransformStr(ProcessIdTo, Transform), p.Id); if (Wait && p != null) { Redir[] r = new Redir[2] {new Redir(Context, outp), new Redir(Context, errorp)}; p.Exited += ect.onExited; p.EnableRaisingEvents = true; AsyncWriter asyncWriter = null; try { if (pi.RedirectStandardInput) { byte[] data; if (input is byte[]) data = ((byte[]) input); else { Encoding en = System.Text.Encoding.Default; if (string.IsNullOrEmpty(enc)) en = Utils.GetEncoding(enc); if (en == null) en = System.Text.Encoding.Default; data = en.GetBytes(Utils.To<string>(input)); } asyncWriter = new AsyncWriter(p.StandardInput, data); } if (pi.RedirectStandardOutput) if (BinaryOutput) r[0].StartRedirect(p.StandardOutput.BaseStream); else r[0].StartRedirect(p.StandardOutput.BaseStream, p.StandardOutput.CurrentEncoding); if (pi.RedirectStandardError) r[1].StartRedirect(p.StandardError.BaseStream, p.StandardError.CurrentEncoding); var wh = new WaitHandle[] {r[0].Event, r[1].Event, terminated, timeout.WaitHandle}; do { Context.OnProgress(1); int n = WaitHandle.WaitAny(wh, 500,true); switch (n) { case 0: case 1: r[n].Flush(false); break; case 2: break; // Exit case 3: throw new TimeoutException("Command execution timed out"); } } while (!p.HasExited); // must wait to ensure that all output is flushed p.WaitForExit(); } finally { try { if (!p.HasExited && !p.WaitForExit(1000)) { VerboseMessage("Process didn't terminate as expected. TASKKILL will be used."); Shell sh = new Shell(Utils.BackslashAdd(Environment.GetFolderPath(Environment.SpecialFolder.System)) + "TASKKILL.EXE /T /F /pid " + p.Id); sh.CreateNoWindow = true; sh.Mode = ShellMode.Comspec; sh.IgnoreExitCode = true; Context.InitializeAndExecute(sh); VerboseMessage("TASKKILL completed"); } VerboseMessage("Waiting for process to terminate completely."); p.WaitForExit(); } catch { VerboseMessage("Failed to wait until the process is terminated"); } try { if (!p.HasExited) p.Kill(); } catch { VerboseMessage("Kill failed"); } r[0].Flush(true); r[1].Flush(true); r[0].Dispose(); r[1].Dispose(); terminated.WaitOne(1000, false); // Restore p.Exited -= ect.onExited; } int exitCode = p.ExitCode; if (!string.IsNullOrEmpty(ExitCodeTo)) Context.OutTo(Context.TransformStr(ExitCodeTo, Transform), exitCode); VerboseMessage("Execution completed with exit code={0}", exitCode); if (exitCode != 0 && !IgnoreExitCode) { string a = Utils.QuoteArg(pi.FileName); if (!string.IsNullOrEmpty(pi.Arguments)) { if (!ExtendedErrors) a += " ... arguments ..."; else a += " " + pi.Arguments; } throw new ScriptRuntimeException(string.Format("Command [{0}] failed with exit code = {1}", a, p.ExitCode)); } } } } } finally { if (fileToDelete!=null) { try { File.Delete(fileToDelete); } catch { } } } return null; }