Change the version info in About dialog.
[marukotoolbox.git] / mp4box / WorkingForm.cs
blobd611be55f79741b8eb1ece4c491f2a22089cb166
1 // ------------------------------------------------------------------
2 // Copyright (C) 2011-2015 Maruko Toolbox Project
3 //
4 // Authors: komaruchan <sandy_0308@hotmail.com>
5 // LunarShaddow <aflyhorse@hotmail.com>
6 //
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 // -------------------------------------------------------------------
22 using System;
23 using System.Collections.Generic;
24 using System.ComponentModel;
25 using System.Data;
26 using System.Drawing;
27 using System.Runtime.InteropServices;
28 using System.Text;
29 using System.Text.RegularExpressions;
30 using System.Windows.Forms;
31 using Microsoft.WindowsAPICodePack.Taskbar;
33 namespace mp4box
35 using Extension;
36 using System.IO;
37 public partial class WorkingForm : Form
39 /// <summary>
40 /// Initialize a cmd output wrapper.
41 /// </summary>
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();
49 Commands = commands;
50 WorkQueued = workcount;
51 this.Parent = this.Owner;
52 StartPosition = FormStartPosition.CenterScreen;
55 /// <summary>
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>
59 /// </summary>
60 public string Commands
62 get
64 return cmds;
66 set
68 if (String.IsNullOrEmpty(value))
69 throw new ArgumentException("Null or empty commands string.");
70 else
71 cmds = value;
75 /// <summary>
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>
79 /// </summary>
80 public int WorkQueued
82 get
84 return workQueued;
86 set
88 if (value <= 0)
89 throw new ArgumentException("Negative number? Really?");
90 else
91 workQueued = value;
95 /// <summary>
96 /// Makesure a file really exists.
97 /// </summary>
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
105 + "其他信息:" + info);
106 return System.IO.File.Exists(path);
109 #region Private Members Declaration
111 /// <summary>
112 /// Proposed commands.
113 /// </summary>
114 private string cmds;
116 /// <summary>
117 /// Queued works quantity.
118 /// </summary>
119 private int workQueued;
121 /// <summary>
122 /// Completed works quantity.
123 /// </summary>
124 private int workCompleted;
126 /// <summary>
127 /// Path to the batch file.
128 /// </summary>
129 private string batPath;
131 /// <summary>
132 /// Potential working process.
133 /// </summary>
134 private System.Diagnostics.Process proc;
136 /// <summary>
137 /// Frame count of current processing file via FFmpeg.
138 /// </summary>
139 private int frameCount;
141 /// <summary>
142 /// Path to the tools directory.
143 /// </summary>
144 private string workPath;
146 /// <summary>
147 /// true if the system is Win7 or later
148 /// </summary>
149 private bool win7supported;
151 /// <summary>
152 /// true if autoscroll is enabled
153 /// </summary>
154 private bool autoscroll;
156 #endregion
158 #region Regex Patterns
159 /// <summary>
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.
163 /// </summary>
164 public static class Patterns
166 /// <summary>
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>
169 /// </summary>
170 private const string ffmsRegStr = @"^\[(?<percent>\d+\.\d+)%\]";
172 /// <summary>
173 /// ffms splitter Regex.
174 /// Available patterns: percent.
175 /// </summary>
176 public static readonly Regex ffmsReg = new Regex(ffmsRegStr);
178 /// <summary>
179 /// lavf splitter sample output:
180 /// <para>387 frames: 68.65 fps, 14.86 kb/s, 29.27 KB</para>
181 /// </summary>
182 private const string lavfRegStr = @"^(?<frame>\d+) frames:";
184 /// <summary>
185 /// lavf splitter Regex.
186 /// Available patterns: frame.
187 /// </summary>
188 public static readonly Regex lavfReg = new Regex(lavfRegStr);
190 /// <summary>
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>
196 /// </summary>
197 /// <remarks>
198 /// Without double quote: ^.+>"(?<workDIR>[^"]+)\\x264.+ -o "(?<fileOut>[^"]+)" "(?<fileIn>[^"]+)"
199 /// </remarks>
200 private const string fileRegStr = @">""(?<workDIR>[^""]+)\\x264.+-o ""(?<fileOut>[^""]+)"" ""(?<fileIn>[^""]+)""";
202 /// <summary>
203 /// Filename and working directory Regex.
204 /// Available patterns: workDIR, fileOut, fileIn.
205 /// </summary>
206 public static readonly Regex fileReg = new Regex(fileRegStr, RegexOptions.Singleline);
208 /// <summary>
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>
213 /// </summary>
214 private const string ffmpegRegStr = @"\bDuration: (?<duration>\d{2}:\d{2}:\d{2}\.\d{2}), start: " +
215 @".+: Video: .+ (?<tbr>\d+\.?\d+) tbr, ";
217 /// <summary>
218 /// ffmpeg output Regex.
219 /// Available patterns: duration, tbr.
220 /// </summary>
221 public static readonly Regex ffmpegReg = new Regex(ffmpegRegStr, RegexOptions.Singleline);
224 #endregion
226 #region Native Functions
227 /// <summary>
228 /// Class hosting Native Win32 APIs
229 /// </summary>
230 private class NativeMethods
232 #region <WinUser.h>
234 /// <summary>
235 /// Specifies the type of scroll bar.
236 /// </summary>
237 public enum ScrollBarConsts : int
239 /// <summary>
240 /// Window's standard horizontal scroll bar.
241 /// </summary>
242 SB_HORZ = 0,
243 /// <summary>
244 /// Window's standard vertical scroll bar.
245 /// </summary>
246 SB_VERT = 1,
247 /// <summary>
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.
250 /// </summary>
251 SB_CTL = 2,
252 /// <summary>
253 /// Window's standard scroll bar.
254 /// </summary>
255 SB_BOTH = 3
258 /// <summary>
259 /// Specifies the commands to scroll bar.
260 /// </summary>
261 public enum ScrollBarCmds : int
263 /// <summary>
264 /// Scrolls one line down.
265 /// </summary>
266 SB_LINEUP = 0,
267 /// <summary>
268 /// Scrolls left by one unit.
269 /// </summary>
270 SB_LINELEFT = 0,
271 /// <summary>
272 /// Scrolls one line up.
273 /// </summary>
274 SB_LINEDOWN = 1,
275 /// <summary>
276 /// Scrolls right by one unit.
277 /// </summary>
278 SB_LINERIGHT = 1,
279 /// <summary>
280 /// Scrolls one page up.
281 /// </summary>
282 SB_PAGEUP = 2,
283 /// <summary>
284 /// Scrolls left by the width of the window.
285 /// </summary>
286 SB_PAGELEFT = 2,
287 /// <summary>
288 /// Scrolls one page down.
289 /// </summary>
290 SB_PAGEDOWN = 3,
291 /// <summary>
292 /// Scrolls right by the width of the window.
293 /// </summary>
294 SB_PAGERIGHT = 3,
295 /// <summary>
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.
298 /// </summary>
299 SB_THUMBPOSITION = 4,
300 /// <summary>
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.
303 /// </summary>
304 SB_THUMBTRACK = 5,
305 /// <summary>
306 /// Scrolls to the upper left.
307 /// </summary>
308 SB_TOP = 6,
309 /// <summary>
310 /// Scrolls to the upper left.
311 /// </summary>
312 SB_LEFT = 6,
313 /// <summary>
314 /// Scrolls to the lower right.
315 /// </summary>
316 SB_BOTTOM = 7,
317 /// <summary>
318 /// Scrolls to the lower right.
319 /// </summary>
320 SB_RIGHT = 7,
321 /// <summary>
322 /// Ends scroll.
323 /// </summary>
324 SB_ENDSCROLL = 8
327 /// <summary>
328 /// Indicate the parameters to set or get.
329 /// </summary>
330 public enum ScrollBarFlags : uint
332 /// <summary>
333 /// The nMin and nMax members contain the minimum and maximum values for the scrolling range.
334 /// </summary>
335 SIF_RANGE = 0x1,
336 /// <summary>
337 /// The nPage member contains the page size for a proportional scroll bar.
338 /// </summary>
339 SIF_PAGE = 0x2,
340 /// <summary>
341 /// The nPos member contains the scroll box position, which is not updated while the user drags the scroll box.
342 /// </summary>
343 SIF_POS = 0x4,
344 /// <summary>
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.
347 /// </summary>
348 SIF_DISABLENOSCROLL = 0x8,
349 /// <summary>
350 /// The nTrackPos member contains the current position of the scroll box while the user is dragging it.
351 /// </summary>
352 SIF_TRACKPOS = 0x10,
353 /// <summary>
354 /// Combination of SIF_PAGE, SIF_POS, SIF_RANGE, and SIF_TRACKPOS.
355 /// </summary>
356 SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS)
359 /// <summary>
360 /// The flash status.
361 /// </summary>
362 public enum FlashWindowFlags : uint
364 /// <summary>
365 /// Stop flashing. The system restores the window to its original state.
366 /// </summary>
367 FLASHW_STOP = 0,
368 /// <summary>
369 /// Flash the window caption.
370 /// </summary>
371 FLASHW_CAPTION = 0x1,
372 /// <summary>
373 /// Flash the taskbar button.
374 /// </summary>
375 FLASHW_TRAY = 0x2,
376 /// <summary>
377 /// Flash both the window caption and taskbar button.
378 /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
379 /// </summary>
380 FLASHW_ALL = (FLASHW_CAPTION | FLASHW_TRAY),
381 /// <summary>
382 /// Flash continuously, until the FLASHW_STOP flag is set.
383 /// </summary>
384 FLASHW_TIMER = 0x4,
385 /// <summary>
386 /// Flash continuously until the window comes to the foreground.
387 /// </summary>
388 FLASHW_TIMERNOFG = 0xC
391 /// <summary>
392 /// Window Messages
393 /// </summary>
394 public enum Win32Msgs : uint
396 /// <summary>
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.
399 /// </summary>
400 WM_HSCROLL = 0x114,
401 /// <summary>
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.
404 /// </summary>
405 WM_VSCROLL = 0x115,
406 /// <summary>
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.
409 /// </summary>
410 WM_CTLCOLORSCROLLBAR = 0x137
413 /// <summary>
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>
416 /// </summary>
417 [StructLayout(LayoutKind.Sequential)]
418 public struct SCROLLINFO
420 /// <summary>
421 /// Specifies the size, in bytes, of this structure.
422 /// </summary>
423 public uint cbSize;
424 /// <summary>
425 /// Specifies the scroll bar parameters to set or retrieve.
426 /// </summary>
427 public ScrollBarFlags fMask;
428 /// <summary>
429 /// Specifies the minimum scrolling position.
430 /// </summary>
431 public int nMin;
432 /// <summary>
433 /// Specifies the maximum scrolling position.
434 /// </summary>
435 public int nMax;
436 /// <summary>
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.
439 /// </summary>
440 public uint nPage;
441 /// <summary>
442 /// Specifies the position of the scroll box.
443 /// </summary>
444 public int nPos;
445 /// <summary>
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.
448 /// </summary>
449 public int nTrackPos;
452 /// <summary>
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>
455 /// </summary>
456 [StructLayout(LayoutKind.Sequential)]
457 public struct FLASHWINFO
459 /// <summary>
460 /// The size of the structure, in bytes.
461 /// </summary>
462 public uint cbSize;
463 /// <summary>
464 /// A Handle to the Window to be Flashed. The window can be either opened or minimized.
465 /// </summary>
466 public IntPtr hwnd;
467 /// <summary>
468 /// The Flash Status.
469 /// </summary>
470 public FlashWindowFlags dwFlags;
471 /// <summary>
472 /// The number of times to Flash the window.
473 /// </summary>
474 public uint uCount;
475 /// <summary>
476 /// The rate at which the Window is to be flashed, in milliseconds.
477 /// If Zero, the function uses the default cursor blink rate.
478 /// </summary>
479 public uint dwTimeout;
482 /// <summary>
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>
486 /// </summary>
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);
495 /// <summary>
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>
499 /// </summary>
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);
508 #endregion
510 #region <WinDef.h>
512 /// <summary>
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>
515 /// </summary>
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;
523 /// <summary>
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>
526 /// </summary>
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;
534 /// <summary>
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>
537 /// </summary>
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);
546 #endregion
548 /// <summary>
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>
553 /// </summary>
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(
564 IntPtr hWnd,
565 ScrollBarConsts fnBar,
566 ref SCROLLINFO lpsi);
568 /// <summary>
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>
572 /// </summary>
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(
583 IntPtr hWnd,
584 ScrollBarConsts fnBar,
585 ref SCROLLINFO lpsi,
586 [MarshalAs(UnmanagedType.Bool)] bool fRedraw);
588 /// <summary>
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>
591 /// </summary>
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);
598 /// <summary>
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>
602 /// </summary>
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(
611 IntPtr hWnd,
612 Win32Msgs Msg,
613 [MarshalAs(UnmanagedType.SysUInt)] UIntPtr wParam,
614 [MarshalAs(UnmanagedType.SysInt)] IntPtr lParam);
616 #endregion
618 #region UI Methods
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);
629 sw.Close();
630 // synchronize UI
631 workCompleted = -1;
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));
636 if (win7supported)
637 TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal);
638 // validate the command string
639 if (Commands.Equals(encoder.GetString(encoder.GetBytes(Commands))))
640 ProcStart();
641 else
643 MessageBox.Show("Path or filename contains invalid characters, please rename and retry."
644 + Environment.NewLine +
645 "路径或文件名含有不可识别的字符,请重命名后重试。");
646 this.Close();
648 notifyIcon.Visible = true;
649 MainForm main = (MainForm)this.Owner;
650 workPath = main.workPath;
651 autoscroll = true;
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
665 e.Cancel = true;
666 return;
668 else
669 ProcAbort();
671 // clean up temp batch file
672 System.IO.File.Delete(batPath);
675 private void buttonAbort_Click(object sender, EventArgs e)
677 ProcAbort();
680 private void buttonSave_Click(object sender, EventArgs e)
682 // prohibit saving before work completion
683 if (!proc.HasExited)
684 MessageBox.Show("Saving before completion is not recommended." +
685 Environment.NewLine + "Please manually abort the process or wait patiently.");
686 else
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)
717 this.Visible = true;
718 this.WindowState = FormWindowState.Normal;
719 this.Activate();
722 private void richTextBoxOutput_Enter(object sender, EventArgs e)
724 this.ActiveControl = null;
727 #endregion
729 /// <summary>
730 /// Start the working process.
731 /// </summary>
732 private void ProcStart()
734 // setup the process
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();
750 /// <summary>
751 /// Process Exit event handler. Automatically called when process exit normally.
752 /// </summary>
753 private void ProcExit(object sender, EventArgs e)
755 // synchronize UI
756 UpdateWorkCountUI();
757 buttonAbort.InvokeIfRequired(() =>
758 buttonAbort.Enabled = false);
759 UpdateProgress(1);
760 if (win7supported)
761 TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress);
762 // wait a little bit for the last asynchronous reading
763 System.Threading.Thread.Sleep(75);
764 // append finish tag
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
776 FlashForm();
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");
797 /// <summary>
798 /// Manually call this method to abort all workings.
799 /// </summary>
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);
806 // terminate threads
807 killProcTree(proc.Id);
808 // reset taskbar progress
809 if (win7supported)
810 TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress);
811 // Print abort message to log
812 Print(Environment.NewLine + "Work is aborted by user.");
815 /// <summary>
816 /// Asynchronous reading DataReceived event handler.
817 /// </summary>
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);
826 if (result.Success)
828 UpdateWorkCountUI();
829 UpdateProgress(0);
830 frameCount = EstimateFrame(workPath, result.Groups["fileIn"].Value);
832 // try ffms pattern
833 result = Patterns.ffmsReg.Match(e.Data);
834 if (result.Success)
835 UpdateProgress(Double.Parse(result.Groups["percent"].Value) / 100);
836 // try lavf pattern
837 result = Patterns.lavfReg.Match(e.Data);
838 if (result.Success)
839 UpdateProgress(Double.Parse(result.Groups["frame"].Value) / frameCount);
843 /// <summary>
844 /// Kill a process as well as all childs by PID.
845 /// </summary>
846 /// <param name="pid">The PID of the target process.</param>
847 private void killProcTree(int pid)
849 // recursive first
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) { }
863 /// <summary>
864 /// Update Work Count UI on call. Each call will bump up completed work count by 1.
865 /// </summary>
866 private void UpdateWorkCountUI()
868 ++workCompleted;
869 this.InvokeIfRequired(() =>
870 this.Text = "Xiaowan (" + workCompleted + '/' + WorkQueued + ')');
871 this.labelworkCount.InvokeIfRequired(() =>
872 labelworkCount.Text = workCompleted.ToString() + '/' + WorkQueued + " Completed");
875 /// <summary>
876 /// Update Progress Bar as well as the number on it.
877 /// </summary>
878 /// <param name="value">Progress expressed in decimal (0.00-1.00).</param>
879 private void UpdateProgress(double value)
881 // Some kind of safeguard
882 if (value < 0)
883 value = 0;
884 if (value > 1)
885 value = 1;
886 // Update UI
887 progressBarX264.InvokeIfRequired(() =>
888 progressBarX264.Value = Convert.ToInt32(value * progressBarX264.Maximum));
889 labelProgress.InvokeIfRequired(() =>
890 labelProgress.Text = value.ToString("P"));
891 if (win7supported)
892 TaskbarManager.Instance.SetProgressValue(
893 Convert.ToInt32(value * progressBarX264.Maximum), progressBarX264.Maximum);
894 notifyIcon.Text = "小丸工具箱" + Environment.NewLine +
895 labelworkCount.Text + " - " + labelProgress.Text;
898 /// <summary>
899 /// Get a rough estimation on frame counts via FFmpeg.
900 /// If failed, return <see cref="Int32.MaxValue"/> instead.
901 /// </summary>
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);
914 // log and append
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);
920 if (!result.Success)
922 Print("Warning: Error detected on previous file. Estimatation may not work."
923 + Environment.NewLine);
924 return Int32.MaxValue;
926 else
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);
932 /// <summary>
933 /// Flash the current form.
934 /// </summary>
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;
944 // Flash 3 times
945 info.uCount = 3;
946 info.dwTimeout = 0;
947 return NativeMethods.FlashWindowEx(ref info);
950 /// <summary>
951 /// Pop a balloon tip.
952 /// </summary>
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;
958 if (main.trayMode)
960 notifyIcon.Visible = true;
961 notifyIcon.ShowBalloonTip(timeout, "小丸工具箱", notes, ToolTipIcon.Info);
965 /// <summary>
966 /// Append and scroll Textbox if needed.
967 /// </summary>
968 /// <param name="str">String to append.</param>
969 private void Print(string str)
971 richTextBoxOutput.InvokeIfRequired(() =>
973 richTextBoxOutput.AppendText(str);
974 if (autoscroll)
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);