using System.Diagnostics; using System.Windows.Forms; using System.Drawing; using System.Runtime.InteropServices; using System; using System.ComponentModel; using System.Threading; public class Progress { private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); private const UInt32 SWP_NOSIZE = 0x0001; private const UInt32 SWP_NOMOVE = 0x0002; private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); private string line; public Progress(string line) { this.line = line; } public void Run() { Form f = new Form(); f.Width = 800; f.Height = 35; f.Text = line; f.StartPosition = FormStartPosition.CenterParent; f.FormBorderStyle = FormBorderStyle.None; f.Load += ((o, e) => f.TopMost = true); TextBox t = new TextBox(); t.Dock = DockStyle.Fill; t.Multiline = true; t.ReadOnly = true; t.AcceptsReturn = true; t.AcceptsTab = true; t.Font = new Font("Consolas", 14); f.Controls.Add(t); SetWindowPos(f.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); Process proc = new Process(); proc.StartInfo.UseShellExecute = false; proc.StartInfo.FileName = "cmd"; proc.StartInfo.Arguments = "/c " + line; proc.StartInfo.CreateNoWindow = false; proc.StartInfo.RedirectStandardOutput = true; proc.StartInfo.RedirectStandardError = true; proc.EnableRaisingEvents = true; proc.OutputDataReceived += ((sender, args) => f.BeginInvoke(new MethodInvoker( () => {t.AppendText("\r\n" + args.Data);})) ); proc.ErrorDataReceived += ((sender, args) => f.BeginInvoke(new MethodInvoker( () => {t.AppendText("\r\n" + args.Data);})) ); proc.Exited += ((sender, args) => { onexit(proc, f); }); try { proc.Start(); } catch (Exception) { onexit(proc, f); } proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); ChildProcessTracker.AddProcess(proc); f.ShowDialog(); try { if (!proc.HasExited) { proc.Kill(); } } catch (Exception){} } void onexit(Process proc, Form f) { SetWindowPos(f.Handle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); if (proc.ExitCode != 0) { if (MessageBox.Show( String.Format("上一条命令失败了, 是否重试: \n{0}", line), "是否重试?", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question) == DialogResult.Retry) { new Thread(new ThreadStart(() => { Thread.Sleep(500); f.DialogResult = DialogResult.OK; })).Start(); Run(); } else { f.DialogResult = DialogResult.OK; } } else { f.DialogResult = DialogResult.OK; } } } //// http://stackoverflow.com/questions/3342941/kill-child-process-when-parent-process-is-killed /// /// Allows processes to be automatically killed if this parent process unexpectedly quits. /// This feature requires Windows 8 or greater. On Windows 7, nothing is done. /// References: /// http://stackoverflow.com/a/4657392/386091 /// http://stackoverflow.com/a/9164742/386091 public static class ChildProcessTracker { /// /// Add the process to be tracked. If our current process is killed, the child processes /// that we are tracking will be automatically killed, too. If the child process terminates /// first, that's fine, too. /// public static void AddProcess(Process process) { if (s_jobHandle != IntPtr.Zero) { bool success = AssignProcessToJobObject(s_jobHandle, process.Handle); if (!success) throw new Win32Exception(); } } static ChildProcessTracker() { // This feature requires Windows 8 or later. To support Windows 7 requires // registry settings to be added if you are using Visual Studio plus an // app.manifest change. // http://stackoverflow.com/a/4232259/386091 // http://stackoverflow.com/a/9507862/386091 if (Environment.OSVersion.Version < new Version(6, 2)) return; // The job name is optional (and can be null) but it helps with diagnostics. // If it's not null, it has to be unique. Use SysInternals' Handle command-line // utility: handle -a ChildProcessTracker string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; s_jobHandle = CreateJobObject(IntPtr.Zero, jobName); var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); // This is the key flag. When our process is killed, Windows will automatically // close the job handle, and when that happens, we want the child processes to // be killed, too. info.LimitFlags = JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); extendedInfo.BasicLimitInformation = info; int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); try { Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) { throw new Win32Exception(); } } finally { Marshal.FreeHGlobal(extendedInfoPtr); } } [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); [DllImport("kernel32.dll")] static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); // Windows will automatically close any open job handles when our process terminates. // This can be verified by using SysInternals' Handle utility. When the job handle // is closed, the child processes will be killed. private static readonly IntPtr s_jobHandle; } public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, BasicUIRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, GroupInformation = 11 } [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public JOBOBJECTLIMIT LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public UInt32 ActiveProcessLimit; public Int64 Affinity; public UInt32 PriorityClass; public UInt32 SchedulingClass; } [Flags] public enum JOBOBJECTLIMIT : uint { JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000 } [StructLayout(LayoutKind.Sequential)] public struct IO_COUNTERS { public UInt64 ReadOperationCount; public UInt64 WriteOperationCount; public UInt64 OtherOperationCount; public UInt64 ReadTransferCount; public UInt64 WriteTransferCount; public UInt64 OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public IO_COUNTERS IoInfo; public UIntPtr ProcessMemoryLimit; public UIntPtr JobMemoryLimit; public UIntPtr PeakProcessMemoryUsed; public UIntPtr PeakJobMemoryUsed; }