diff --git a/.gitignore b/.gitignore
index 1998c29..3cd5480 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
-.cache
\ No newline at end of file
+.cache
+*~
+*#*#
+.appdata
diff --git a/bin/progress.cmd b/bin/progress.cmd
new file mode 100644
index 0000000..0126e86
--- /dev/null
+++ b/bin/progress.cmd
@@ -0,0 +1,2 @@
+@echo off
+powershell -File %~dp0\progress.ps1 -- "%*"
\ No newline at end of file
diff --git a/bin/progress.cs b/bin/progress.cs
new file mode 100644
index 0000000..09c38f1
--- /dev/null
+++ b/bin/progress.cs
@@ -0,0 +1,239 @@
+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;
+}
\ No newline at end of file
diff --git a/bin/progress.ps1 b/bin/progress.ps1
new file mode 100644
index 0000000..a47bb8a
--- /dev/null
+++ b/bin/progress.ps1
@@ -0,0 +1,9 @@
+$cmdline = [System.Environment]::CommandLine
+$index = $cmdline.IndexOf("--")
+echo $cmdline
+$cmdline = $cmdline.SubString($index + 2).Trim()
+$dir = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$env:path="$dir;$env:path"
+Add-Type -TypeDefinition (Get-Content $dir\progress.cs -raw) -ReferencedAssemblies System.Windows.Forms,System.Drawing,System.Runtime
+$wnd = New-Object Progress($cmdline)
+$wnd.Run()
\ No newline at end of file
diff --git a/start.cmd b/start.cmd
index dcf33bd..f5b9784 100644
--- a/start.cmd
+++ b/start.cmd
@@ -1,3 +1,2 @@
@echo off
-call %~dp0\env.cmd
-cmd /k
\ No newline at end of file
+cmd /k %~dp0\env.cmd