示例#1
0
        public ShellModeEntered(ShellMode shellMode)
        {
            Guard.NotNull(shellMode, nameof(shellMode));

            this.ShellMode = shellMode;
        }
示例#2
0
        public ShellModeLeft(ShellMode shellMode)
        {
            Guard.NotNull(shellMode, nameof(shellMode));

            this.ShellMode = shellMode;
        }
示例#3
0
        /// 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);
        }