1
// ------------------------------------------------------------------
2 // Copyright (C) 2011-2015 Maruko Toolbox Project
4 // Authors: komaruchan <sandy_0308@hotmail.com>
5 // LunarShaddow <aflyhorse@hotmail.com>
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16 // express or implied.
17 // See the License for the specific language governing permissions
18 // and limitations under the License.
19 // -------------------------------------------------------------------
23 using System
.Collections
.Generic
;
24 using System
.ComponentModel
;
27 using System
.Runtime
.InteropServices
;
29 using System
.Text
.RegularExpressions
;
30 using System
.Windows
.Forms
;
31 using Microsoft
.WindowsAPICodePack
.Taskbar
;
37 public partial class WorkingForm
: Form
40 /// Initialize a cmd output wrapper.
42 /// <param name="commands">Proposing commands.
43 /// Expecting <see cref="Environment.NewLine"/> as line breaker.</param>
44 /// <param name="workcount">The amount of roposing commands.
45 /// Single-pass work don't need to specify this parameter.</param>
46 public WorkingForm(string commands
, int workcount
= 1)
48 InitializeComponent();
50 WorkQueued
= workcount
;
51 this.Parent
= this.Owner
;
52 StartPosition
= FormStartPosition
.CenterScreen
;
56 /// Gets or sets the proposed commands. Expecting <see cref="Environment.NewLine"/> as line breaker.
57 /// <exception cref="System.ArgumentException">
58 /// An exception is thrown if set it empty or null.</exception>
60 public string Commands
68 if (String
.IsNullOrEmpty(value))
69 throw new ArgumentException("Null or empty commands string.");
76 /// Gets or sets the amount of proposed works.
77 /// <exception cref="System.ArgumentException">
78 /// An exception is thrown if set it zero of negative.</exception>
89 throw new ArgumentException("Negative number? Really?");
96 /// Makesure a file really exists.
98 /// <param name="path">Path to the file.</param>
99 /// <param name="arg">Supplement info.</param>
100 /// <returns>The file exist or not.</returns>
101 public static bool CheckFileExist(string path
, string info
= "")
103 if (!System
.IO
.File
.Exists(path
))
104 MessageBox
.Show("找不到 " + path
+ " 了啦" + Environment
.NewLine
106 return System
.IO
.File
.Exists(path
);
109 #region Private Members Declaration
112 /// Proposed commands.
117 /// Queued works quantity.
119 private int workQueued
;
122 /// Completed works quantity.
124 private int workCompleted
;
127 /// Path to the batch file.
129 private string batPath
;
132 /// Potential working process.
134 private System
.Diagnostics
.Process proc
;
137 /// Frame count of current processing file via FFmpeg.
139 private int frameCount
;
142 /// Path to the tools directory.
144 private string workPath
;
147 /// true if the system is Win7 or later
149 private bool win7supported
;
152 /// true if autoscroll is enabled
154 private bool autoscroll
;
158 #region Regex Patterns
160 /// Store const regex patterns.
161 /// The Regex objects are made to speed up matchup instead of
162 /// passing the pattern string as arguments from time to time.
164 public static class Patterns
167 /// ffms splitter sample output:
168 /// <para>[8.1%] 273/3357 frames, 56.22 fps, 17.99 kb/s, 25.01 KB, eta 0:00:54, est.size 307.54 KB</para>
170 private const string ffmsRegStr
= @"^\[(?<percent>\d+\.\d+)%\]";
173 /// ffms splitter Regex.
174 /// Available patterns: percent.
176 public static readonly Regex ffmsReg
= new Regex(ffmsRegStr
);
179 /// lavf splitter sample output:
180 /// <para>387 frames: 68.65 fps, 14.86 kb/s, 29.27 KB</para>
182 private const string lavfRegStr
= @"^(?<frame>\d+) frames:";
185 /// lavf splitter Regex.
186 /// Available patterns: frame.
188 public static readonly Regex lavfReg
= new Regex(lavfRegStr
);
191 /// x264 execution sample output:
192 /// <para>X:\toolDIR>"X:\toolDIR\tools\x264_32_tMod-8bit-420.exe" --crf 24 --preset 8 --demuxer ffms
193 /// -r 3 --b 3 --me umh -i 1 --scenecut 60 -f 1:1 --qcomp 0.5 --psy-rd 0.3:0 --aq-mode 2 --aq-strength 0.8
194 /// -o "X:\workDIR\output.ext" "X:\workDIR\input.ext" --vf subtitles --sub "X:\workDIR\sub.ext"
195 /// --acodec faac --abitrate 128</para>
198 /// Without double quote: ^.+>"(?<workDIR>[^"]+)\\x264.+ -o "(?<fileOut>[^"]+)" "(?<fileIn>[^"]+)"
200 private const string fileRegStr
= @">""(?<workDIR>[^""]+)\\x264.+-o ""(?<fileOut>[^""]+)"" ""(?<fileIn>[^""]+)""";
203 /// Filename and working directory Regex.
204 /// Available patterns: workDIR, fileOut, fileIn.
206 public static readonly Regex fileReg
= new Regex(fileRegStr
, RegexOptions
.Singleline
);
209 /// ffmpeg -i sample output:
210 /// <para>Duration: 00:02:20.22, start: 0.000000, bitrate: 827 kb/s</para>
211 /// <para>Stream #0:0: Video: h264 (High), yuv420p, 1280x720, 545 kb/s, 24.42 fps, 23.98 tbr, 1k tbn, 47.95 tbc</para>
212 /// <para>Stream #0:1: Audio: aac, 48000 Hz, stereo, fltp, 128 kb/s</para>
214 private const string ffmpegRegStr
= @"\bDuration: (?<duration>\d{2}:\d{2}:\d{2}\.\d{2}), start: " +
215 @".+: Video: .+ (?<tbr>\d+\.?\d+) tbr, ";
218 /// ffmpeg output Regex.
219 /// Available patterns: duration, tbr.
221 public static readonly Regex ffmpegReg
= new Regex(ffmpegRegStr
, RegexOptions
.Singleline
);
226 #region Native Functions
228 /// Class hosting Native Win32 APIs
230 private class NativeMethods
235 /// Specifies the type of scroll bar.
237 public enum ScrollBarConsts
: int
240 /// Window's standard horizontal scroll bar.
244 /// Window's standard vertical scroll bar.
248 /// Retrieves the position of the scroll box in a scroll bar control.
249 /// The hWnd parameter must be the handle to the scroll bar control.
253 /// Window's standard scroll bar.
259 /// Specifies the commands to scroll bar.
261 public enum ScrollBarCmds
: int
264 /// Scrolls one line down.
268 /// Scrolls left by one unit.
272 /// Scrolls one line up.
276 /// Scrolls right by one unit.
280 /// Scrolls one page up.
284 /// Scrolls left by the width of the window.
288 /// Scrolls one page down.
292 /// Scrolls right by the width of the window.
296 /// The user has dragged the scroll box (thumb) and released the mouse button.
297 /// The HIWORD indicates the position of the scroll box at the end of the drag operation.
299 SB_THUMBPOSITION
= 4,
301 /// The user is dragging the scroll box. This message is sent repeatedly until the user releases the mouse button.
302 /// The HIWORD indicates the position that the scroll box has been dragged to.
306 /// Scrolls to the upper left.
310 /// Scrolls to the upper left.
314 /// Scrolls to the lower right.
318 /// Scrolls to the lower right.
328 /// Indicate the parameters to set or get.
330 public enum ScrollBarFlags
: uint
333 /// The nMin and nMax members contain the minimum and maximum values for the scrolling range.
337 /// The nPage member contains the page size for a proportional scroll bar.
341 /// The nPos member contains the scroll box position, which is not updated while the user drags the scroll box.
345 /// This value is used only when setting a scroll bar's parameters.
346 /// If the scroll bar's new parameters make the scroll bar unnecessary, disable the scroll bar instead of removing it.
348 SIF_DISABLENOSCROLL
= 0x8,
350 /// The nTrackPos member contains the current position of the scroll box while the user is dragging it.
354 /// Combination of SIF_PAGE, SIF_POS, SIF_RANGE, and SIF_TRACKPOS.
356 SIF_ALL
= (SIF_RANGE
| SIF_PAGE
| SIF_POS
| SIF_TRACKPOS
)
360 /// The flash status.
362 public enum FlashWindowFlags
: uint
365 /// Stop flashing. The system restores the window to its original state.
369 /// Flash the window caption.
371 FLASHW_CAPTION
= 0x1,
373 /// Flash the taskbar button.
377 /// Flash both the window caption and taskbar button.
378 /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
380 FLASHW_ALL
= (FLASHW_CAPTION
| FLASHW_TRAY
),
382 /// Flash continuously, until the FLASHW_STOP flag is set.
386 /// Flash continuously until the window comes to the foreground.
388 FLASHW_TIMERNOFG
= 0xC
394 public enum Win32Msgs
: uint
397 /// The WM_HSCROLL message is sent to a window when a scroll event occurs in the window's standard horizontal scroll bar.
398 /// This message is also sent to the owner of a horizontal scroll bar control when a scroll event occurs in the control.
402 /// The WM_VSCROLL message is sent to a window when a scroll event occurs in the window's standard vertical scroll bar.
403 /// This message is also sent to the owner of a vertical scroll bar control when a scroll event occurs in the control.
407 /// The WM_CTLCOLORSCROLLBAR message is sent to the parent window of a scroll bar control when the control is about to be drawn.
408 /// By responding to this message, the parent window can use the display context handle to set the background color of the scroll bar control.
410 WM_CTLCOLORSCROLLBAR
= 0x137
414 /// Contains scroll bar parameters to be set by the SetScrollInfo function, or retrieved by the GetScrollInfo function.
415 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/bb787537(v=vs.85).aspx</para>
417 [StructLayout(LayoutKind
.Sequential
)]
418 public struct SCROLLINFO
421 /// Specifies the size, in bytes, of this structure.
425 /// Specifies the scroll bar parameters to set or retrieve.
427 public ScrollBarFlags fMask
;
429 /// Specifies the minimum scrolling position.
433 /// Specifies the maximum scrolling position.
437 /// Specifies the page size, in device units.
438 /// A scroll bar uses this value to determine the appropriate size of the proportional scroll box.
442 /// Specifies the position of the scroll box.
446 /// Specifies the immediate position of a scroll box that the user is dragging.
447 /// An application cannot set the immediate scroll position; the SetScrollInfo function ignores this member.
449 public int nTrackPos
;
453 /// Contains the flash status for a window and the number of times the system should flash the window.
454 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms679348(v=vs.85).aspx</para>
456 [StructLayout(LayoutKind
.Sequential
)]
457 public struct FLASHWINFO
460 /// The size of the structure, in bytes.
464 /// A Handle to the Window to be Flashed. The window can be either opened or minimized.
468 /// The Flash Status.
470 public FlashWindowFlags dwFlags
;
472 /// The number of times to Flash the window.
476 /// The rate at which the Window is to be flashed, in milliseconds.
477 /// If Zero, the function uses the default cursor blink rate.
479 public uint dwTimeout
;
483 /// Creates a value for use as a wParam parameter in a message.
484 /// The macro concatenates the specified values.
485 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632664(v=vs.85).aspx</para>
487 /// <param name="wLow">The low-order word of the new value.</param>
488 /// <param name="wHi">The high-order word of the new value.</param>
489 /// <returns>The return value is a WPARAM value.</returns>
490 public static UIntPtr
MakeWParam(uint wLow
, uint wHi
)
492 return (UIntPtr
)MakeLong(wLow
, wHi
);
496 /// Creates a value for use as an lParam parameter in a message.
497 /// The macro concatenates the specified values.
498 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632661(v=vs.85).aspx</para>
500 /// <param name="wLow">The low-order word of the new value.</param>
501 /// <param name="wHi">The high-order word of the new value. </param>
502 /// <returns>The return value is an LPARAM value. </returns>
503 public static IntPtr
MakeLParam(uint wLow
, uint wHi
)
505 return (IntPtr
)MakeLong(wLow
, wHi
);
513 /// Retrieves the high-order word from the specified 32-bit value.
514 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632657(v=vs.85).aspx</para>
516 /// <param name="ptr">The value to be converted.</param>
517 /// <returns>The return value is the high-order word of the specified value.</returns>
518 public static uint HiWord(IntPtr ptr
)
520 return ((uint)ptr
>> 16) & 0xFFFFu
;
524 /// Retrieves the low-order word from the specified value.
525 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632659(v=vs.85).aspx</para>
527 /// <param name="ptr">The value to be converted.</param>
528 /// <returns>The return value is the low-order word of the specified value.</returns>
529 public static uint LoWord(IntPtr ptr
)
531 return (uint)ptr
& 0xFFFFu
;
535 /// Creates a LONG value by concatenating the specified values.
536 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632660(v=vs.85).aspx</para>
538 /// <param name="wLow">The low-order word of the new value.</param>
539 /// <param name="wHi">The high-order word of the new value.</param>
540 /// <returns>The return value is a LONG value.</returns>
541 public static uint MakeLong(uint wLow
, uint wHi
)
543 return (wLow
& 0xFFFFu
) | ((wHi
& 0xFFFFu
) << 16);
549 /// The GetScrollInfo function retrieves the parameters of a scroll bar,
550 /// including the minimum and maximum scrolling positions, the page size,
551 /// and the position of the scroll box (thumb).
552 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/bb787583(v=vs.85).aspx</para>
554 /// <param name="hWnd">Handle to a scroll bar control or a window with a standard scroll bar,
555 /// depending on the value of the fnBar parameter. </param>
556 /// <param name="fnBar">Specifies the type of scroll bar for which to retrieve parameters.</param>
557 /// <param name="lpsi">Pointer to a SCROLLINFO structure. Before calling GetScrollInfo, set the cbSize member to sizeof(SCROLLINFO),
558 /// and set the fMask member to specify the scroll bar parameters to retrieve.
559 /// Before returning, the function copies the specified parameters to the appropriate members of the structure.</param>
560 /// <returns>If the function retrieved any values, the return value is nonzero.</returns>
561 [DllImport("user32.dll")]
562 [return: MarshalAs(UnmanagedType
.Bool
)]
563 public static extern bool GetScrollInfo(
565 ScrollBarConsts fnBar
,
566 ref SCROLLINFO lpsi
);
569 /// The SetScrollInfo function sets the parameters of a scroll bar, including the minimum and maximum scrolling positions,
570 /// the page size, and the position of the scroll box (thumb). The function also redraws the scroll bar, if requested.
571 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/bb787595(v=vs.85).aspx</para>
573 /// <param name="hWnd">Handle to a scroll bar control or a window with a standard scroll bar,
574 /// depending on the value of the fnBar parameter. </param>
575 /// <param name="fnBar">Specifies the type of scroll bar for which to set parameters.</param>
576 /// <param name="lpsi">Pointer to a SCROLLINFO structure. Before calling SetScrollInfo, set the cbSize member of the structure to sizeof(SCROLLINFO),
577 /// set the fMask member to indicate the parameters to set, and specify the new parameter values in the appropriate members.</param>
578 /// <param name="fRedraw">Specifies whether the scroll bar is redrawn to reflect the changes to the scroll bar.
579 /// If this parameter is TRUE, the scroll bar is redrawn, otherwise, it is not redrawn. </param>
580 /// <returns>The current position of the scroll box.</returns>
581 [DllImport("user32.dll")]
582 public static extern int SetScrollInfo(
584 ScrollBarConsts fnBar
,
586 [MarshalAs(UnmanagedType
.Bool
)] bool fRedraw
);
589 /// Flashes the specified window. It does not change the active state of the window.
590 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms679347(v=vs.85).aspx</para>
592 /// <param name="pwfi">A pointer to a FLASHWINFO structure.</param>
593 /// <returns>The return value specifies the window's state before the call to the FlashWindowEx function.
594 /// If the window caption was drawn as active before the call, the return value is nonzero. Otherwise, the return value is zero.</returns>
595 [DllImport("user32.dll")]
596 public static extern bool FlashWindowEx(ref FLASHWINFO pwfi
);
599 /// Places (posts) a message in the message queue associated with the thread that created
600 /// the specified window and returns without waiting for the thread to process the message.
601 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx</para>
603 /// <param name="hWnd">A handle to the window whose window procedure is to receive the message.</param>
604 /// <param name="Msg">The message to be posted.</param>
605 /// <param name="wParam">Additional message-specific information.</param>
606 /// <param name="lParam">Additional message-specific information.</param>
607 /// <returns>If the function succeeds, the return value is nonzero.</returns>
608 [DllImport("user32.dll")]
609 [return: MarshalAs(UnmanagedType
.Bool
)]
610 public static extern bool PostMessage(
613 [MarshalAs(UnmanagedType
.SysUInt
)] UIntPtr wParam
,
614 [MarshalAs(UnmanagedType
.SysInt
)] IntPtr lParam
);
620 private void WorkingForm_Load(object sender
, EventArgs e
)
622 // save commands into a batch file
623 batPath
= System
.IO
.Path
.Combine(
624 Environment
.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget
.Machine
),
625 "xiaowan" + DateTime
.Now
.ToFileTimeUtc().ToString() + ".bat");
626 var encoder
= Encoding
.GetEncoding(0);
627 var sw
= new System
.IO
.StreamWriter(batPath
, false, encoder
);
628 sw
.WriteLine(Commands
);
632 richTextBoxOutput
.Select();
633 // check win7 supported
634 win7supported
= (Environment
.OSVersion
.Version
.Major
> 6 ||
635 (Environment
.OSVersion
.Version
.Major
== 6 && Environment
.OSVersion
.Version
.Minor
>= 1));
637 TaskbarManager
.Instance
.SetProgressState(TaskbarProgressBarState
.Normal
);
638 // validate the command string
639 if (Commands
.Equals(encoder
.GetString(encoder
.GetBytes(Commands
))))
643 MessageBox
.Show("Path or filename contains invalid characters, please rename and retry."
644 + Environment
.NewLine
+
645 "路径或文件名含有不可识别的字符,请重命名后重试。");
648 notifyIcon
.Visible
= true;
649 MainForm main
= (MainForm
)this.Owner
;
650 workPath
= main
.workPath
;
654 private void WorkingForm_FormClosing(object sender
, FormClosingEventArgs e
)
656 // check remaining works
657 if (!(proc
== null || proc
.HasExited
))
659 // ask if user hit close button on any accident
660 var result
= MessageBox
.Show("Processing is still undergoing, confirm exit?",
661 "Xiaowan", MessageBoxButtons
.YesNo
, MessageBoxIcon
.Warning
);
662 if (result
== System
.Windows
.Forms
.DialogResult
.No
)
664 // cancel event and return
671 // clean up temp batch file
672 System
.IO
.File
.Delete(batPath
);
675 private void buttonAbort_Click(object sender
, EventArgs e
)
680 private void buttonSave_Click(object sender
, EventArgs e
)
682 // prohibit saving before work completion
684 MessageBox
.Show("Saving before completion is not recommended." +
685 Environment
.NewLine
+ "Please manually abort the process or wait patiently.");
688 var savDlg
= new SaveFileDialog();
689 savDlg
.FileName
= "log_" + DateTime
.Now
.ToShortDateString().Replace('/', '-');
690 savDlg
.Filter
= "Log files|*.log|All files|*.*";
691 savDlg
.FilterIndex
= 1;
692 savDlg
.RestoreDirectory
= true;
693 if (savDlg
.ShowDialog() == System
.Windows
.Forms
.DialogResult
.OK
)
694 File
.WriteAllText(savDlg
.FileName
, richTextBoxOutput
.Text
);
695 //richTextBoxOutput.SaveFile(savDlg.FileName, RichTextBoxStreamType.UnicodePlainText);
699 private void richTextBoxOutput_VScroll(object sender
, EventArgs e
)
701 var info
= new NativeMethods
.SCROLLINFO();
702 info
.cbSize
= Convert
.ToUInt32(Marshal
.SizeOf(info
));
703 info
.fMask
= NativeMethods
.ScrollBarFlags
.SIF_ALL
;
704 NativeMethods
.GetScrollInfo(richTextBoxOutput
.Handle
, NativeMethods
.ScrollBarConsts
.SB_VERT
, ref info
);
705 autoscroll
= (info
.nTrackPos
+ richTextBoxOutput
.Font
.Height
706 > info
.nMax
- richTextBoxOutput
.ClientSize
.Height
);
709 private void WorkingForm_Resize(object sender
, EventArgs e
)
711 if (this.WindowState
== FormWindowState
.Minimized
)
712 BalloonTip("丸子跑到这里了喔~", 75);
715 private void notifyIcon_MouseClick(object sender
, MouseEventArgs e
)
718 this.WindowState
= FormWindowState
.Normal
;
722 private void richTextBoxOutput_Enter(object sender
, EventArgs e
)
724 this.ActiveControl
= null;
730 /// Start the working process.
732 private void ProcStart()
735 CheckFileExist(batPath
);
736 var processInfo
= new System
.Diagnostics
.ProcessStartInfo(batPath
, "2>&1");
737 processInfo
.WorkingDirectory
= System
.IO
.Directory
.GetCurrentDirectory();
738 processInfo
.CreateNoWindow
= true;
739 processInfo
.UseShellExecute
= false;
740 processInfo
.RedirectStandardOutput
= true;
741 proc
= System
.Diagnostics
.Process
.Start(processInfo
);
742 // attach exit event handler
743 proc
.EnableRaisingEvents
= true;
744 proc
.Exited
+= new EventHandler(ProcExit
);
745 // setup asynchronous reading
746 proc
.OutputDataReceived
+= new System
.Diagnostics
.DataReceivedEventHandler(OutputHandler
);
747 proc
.BeginOutputReadLine();
751 /// Process Exit event handler. Automatically called when process exit normally.
753 private void ProcExit(object sender
, EventArgs e
)
757 buttonAbort
.InvokeIfRequired(() =>
758 buttonAbort
.Enabled
= false);
761 TaskbarManager
.Instance
.SetProgressState(TaskbarProgressBarState
.NoProgress
);
762 // wait a little bit for the last asynchronous reading
763 System
.Threading
.Thread
.Sleep(75);
765 Print(Environment
.NewLine
+ "Work Complete!");
766 // fire a warning if something went wrong
767 // this feature need %ERRORLEVEL% support in batch commands
768 if (proc
.ExitCode
!= 0)
770 Print(Environment
.NewLine
+
771 "Potential Error detected. Please double check the log.");
772 Print(Environment
.NewLine
+
773 "Exit code is: " + proc
.ExitCode
.ToString());
775 // flash form and show balloon tips
777 BalloonTip(@"完成了喵~ (= ω =)");
779 // shutdown the system if required
780 MainForm main
= (MainForm
)this.Owner
;
781 if (main
.shutdownState
)
783 System
.Diagnostics
.Process
.Start("shutdown", "-s");
784 // wait a bit to ensure synchronization
785 System
.Threading
.Thread
.Sleep(75);
786 if (System
.Windows
.Forms
.DialogResult
.Cancel
== MessageBox
.Show(
787 "System will shutdown in 20 seconds. Click \"Cancel\" to stop countdown."
788 + Environment
.NewLine
+
789 "系统将在20秒后自动关机。点击“取消”停止倒计时。",
790 "Warning", MessageBoxButtons
.OKCancel
))
792 System
.Diagnostics
.Process
.Start("shutdown", "-a");
798 /// Manually call this method to abort all workings.
800 private void ProcAbort()
802 // return value is useless when force aborted
803 proc
.CancelOutputRead();
804 // exit process should be omitted too
805 proc
.Exited
-= new EventHandler(ProcExit
);
807 killProcTree(proc
.Id
);
808 // reset taskbar progress
810 TaskbarManager
.Instance
.SetProgressState(TaskbarProgressBarState
.NoProgress
);
811 // Print abort message to log
812 Print(Environment
.NewLine
+ "Work is aborted by user.");
816 /// Asynchronous reading DataReceived event handler.
818 private void OutputHandler(object sender
, System
.Diagnostics
.DataReceivedEventArgs e
)
820 if (!String
.IsNullOrEmpty(e
.Data
))
822 // convert and log first
823 Print(e
.Data
+ Environment
.NewLine
);
824 // test if it is command
825 Match result
= Patterns
.fileReg
.Match(e
.Data
);
830 frameCount
= EstimateFrame(workPath
, result
.Groups
["fileIn"].Value
);
833 result
= Patterns
.ffmsReg
.Match(e
.Data
);
835 UpdateProgress(Double
.Parse(result
.Groups
["percent"].Value
) / 100);
837 result
= Patterns
.lavfReg
.Match(e
.Data
);
839 UpdateProgress(Double
.Parse(result
.Groups
["frame"].Value
) / frameCount
);
844 /// Kill a process as well as all childs by PID.
846 /// <param name="pid">The PID of the target process.</param>
847 private void killProcTree(int pid
)
850 foreach (var m
in new System
.Management
.ManagementObjectSearcher(
851 "Select * From Win32_Process Where ParentProcessID=" + pid
).Get())
853 killProcTree(Convert
.ToInt32(m
["ProcessID"]));
855 // then suicide, usually threads throw exceptions when killed. Dump them silently.
858 System
.Diagnostics
.Process
.GetProcessById(pid
).Kill();
860 catch (ArgumentException
) { }
864 /// Update Work Count UI on call. Each call will bump up completed work count by 1.
866 private void UpdateWorkCountUI()
869 this.InvokeIfRequired(() =>
870 this.Text
= "Xiaowan (" + workCompleted
+ '/' + WorkQueued
+ ')');
871 this.labelworkCount
.InvokeIfRequired(() =>
872 labelworkCount
.Text
= workCompleted
.ToString() + '/' + WorkQueued
+ " Completed");
876 /// Update Progress Bar as well as the number on it.
878 /// <param name="value">Progress expressed in decimal (0.00-1.00).</param>
879 private void UpdateProgress(double value)
881 // Some kind of safeguard
887 progressBarX264
.InvokeIfRequired(() =>
888 progressBarX264
.Value
= Convert
.ToInt32(value * progressBarX264
.Maximum
));
889 labelProgress
.InvokeIfRequired(() =>
890 labelProgress
.Text
= value.ToString("P"));
892 TaskbarManager
.Instance
.SetProgressValue(
893 Convert
.ToInt32(value * progressBarX264
.Maximum
), progressBarX264
.Maximum
);
894 notifyIcon
.Text
= "小丸工具箱" + Environment
.NewLine
+
895 labelworkCount
.Text
+ " - " + labelProgress
.Text
;
899 /// Get a rough estimation on frame counts via FFmpeg.
900 /// If failed, return <see cref="Int32.MaxValue"/> instead.
902 /// <param name="workPath">Path to ffmpeg binary.</param>
903 /// <param name="filePath">Path to target file.</param>
904 /// <returns>Estimated frame count. 1% tolerance added.</returns>
905 private int EstimateFrame(string workPath
, string filePath
)
907 string ffmpegPath
= System
.IO
.Path
.Combine(workPath
, "ffmpeg.exe");
908 CheckFileExist(ffmpegPath
);
909 var processInfo
= new System
.Diagnostics
.ProcessStartInfo(ffmpegPath
, "-i \"" + filePath
+ '"');
910 processInfo
.CreateNoWindow
= true;
911 processInfo
.UseShellExecute
= false;
912 processInfo
.RedirectStandardError
= true;
913 var ffproc
= System
.Diagnostics
.Process
.Start(processInfo
);
915 string mediaInfo
= ffproc
.StandardError
.ReadToEnd();
916 Print("Input file: " + filePath
+ Environment
.NewLine
);
917 Print(mediaInfo
+ Environment
.NewLine
);
918 ffproc
.WaitForExit();
919 var result
= Patterns
.ffmpegReg
.Match(mediaInfo
);
922 Print("Warning: Error detected on previous file. Estimatation may not work."
923 + Environment
.NewLine
);
924 return Int32
.MaxValue
;
927 // add a 1% tolerance to avoid unintentional overflow on progress bar
928 return Convert
.ToInt32(TimeSpan
.Parse(result
.Groups
["duration"].Value
).TotalSeconds
929 * Double
.Parse(result
.Groups
["tbr"].Value
) * 1.01);
933 /// Flash the current form.
935 /// <returns>Whether the form need flash or not.</returns>
936 private bool FlashForm()
938 var info
= new NativeMethods
.FLASHWINFO();
939 info
.cbSize
= Convert
.ToUInt32(Marshal
.SizeOf(info
));
940 this.InvokeIfRequired(() =>
941 info
.hwnd
= this.Handle
);
942 info
.dwFlags
= NativeMethods
.FlashWindowFlags
.FLASHW_ALL
|
943 NativeMethods
.FlashWindowFlags
.FLASHW_TIMERNOFG
;
947 return NativeMethods
.FlashWindowEx(ref info
);
951 /// Pop a balloon tip.
953 /// <param name="notes">The content to show.</param>
954 /// <param name="timeout">Tip timeout.</param>
955 private void BalloonTip(string notes
, int timeout
= 500)
957 MainForm main
= (MainForm
)this.Owner
;
960 notifyIcon
.Visible
= true;
961 notifyIcon
.ShowBalloonTip(timeout
, "小丸工具箱", notes
, ToolTipIcon
.Info
);
966 /// Append and scroll Textbox if needed.
968 /// <param name="str">String to append.</param>
969 private void Print(string str
)
971 richTextBoxOutput
.InvokeIfRequired(() =>
973 richTextBoxOutput
.AppendText(str
);
976 var info
= new NativeMethods
.SCROLLINFO();
977 info
.cbSize
= Convert
.ToUInt32(Marshal
.SizeOf(info
));
978 info
.fMask
= NativeMethods
.ScrollBarFlags
.SIF_RANGE
;
979 NativeMethods
.GetScrollInfo(richTextBoxOutput
.Handle
, NativeMethods
.ScrollBarConsts
.SB_VERT
, ref info
);
980 NativeMethods
.PostMessage(richTextBoxOutput
.Handle
, NativeMethods
.Win32Msgs
.WM_VSCROLL
,
981 NativeMethods
.MakeWParam((uint)NativeMethods
.ScrollBarCmds
.SB_THUMBPOSITION
, (uint)info
.nMax
), IntPtr
.Zero
);