change the path where batch files saved in WorkingForm.
[marukotoolbox.git] / mp4box / WorkingForm.cs
blob1586bdb04f3c3c7ac75c32e342ee095242ab620c
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 public partial class WorkingForm : Form
38 /// <summary>
39 /// Initialize a cmd output wrapper.
40 /// </summary>
41 /// <param name="commands">Proposing commands.
42 /// Expecting <see cref="Environment.NewLine"/> as line breaker.</param>
43 /// <param name="workcount">The amount of roposing commands.
44 /// Single-pass work don't need to specify this parameter.</param>
45 public WorkingForm(string commands, int workcount = 1)
47 InitializeComponent();
48 Commands = commands;
49 WorkQueued = workcount;
50 this.Parent = this.Owner;
51 StartPosition = FormStartPosition.CenterScreen;
54 /// <summary>
55 /// Gets or sets the proposed commands. Expecting <see cref="Environment.NewLine"/> as line breaker.
56 /// <exception cref="System.ArgumentException">
57 /// An exception is thrown if set it empty or null.</exception>
58 /// </summary>
59 public string Commands
61 get
63 return cmds;
65 set
67 if (String.IsNullOrEmpty(value))
68 throw new ArgumentException("Null or empty commands string.");
69 else
70 cmds = value;
74 /// <summary>
75 /// Gets or sets the amount of proposed works.
76 /// <exception cref="System.ArgumentException">
77 /// An exception is thrown if set it zero of negative.</exception>
78 /// </summary>
79 public int WorkQueued
81 get
83 return workQueued;
85 set
87 if (value <= 0)
88 throw new ArgumentException("Negative number? Really?");
89 else
90 workQueued = value;
94 /// <summary>
95 /// Makesure a file really exists.
96 /// </summary>
97 /// <param name="path">Path to the file.</param>
98 /// <param name="arg">Supplement info.</param>
99 /// <returns>The file exist or not.</returns>
100 public static bool CheckFileExist(string path, string info = "")
102 if (!System.IO.File.Exists(path))
103 MessageBox.Show("找不到 " + path + " 了啦" + Environment.NewLine
104 + "其他信息:" + info);
105 return System.IO.File.Exists(path);
108 #region Private Members Declaration
110 /// <summary>
111 /// Proposed commands.
112 /// </summary>
113 private string cmds;
115 /// <summary>
116 /// Queued works quantity.
117 /// </summary>
118 private int workQueued;
120 /// <summary>
121 /// Completed works quantity.
122 /// </summary>
123 private int workCompleted;
125 /// <summary>
126 /// Path to the batch file.
127 /// </summary>
128 private string batPath;
130 /// <summary>
131 /// Potential working process.
132 /// </summary>
133 private System.Diagnostics.Process proc;
135 /// <summary>
136 /// Frame count of current processing file via FFmpeg.
137 /// </summary>
138 private int frameCount;
140 /// <summary>
141 /// Path to the tools directory.
142 /// </summary>
143 private string workPath;
145 /// <summary>
146 /// true if the system is Win7 or later
147 /// </summary>
148 private bool win7supported;
150 /// <summary>
151 /// true if autoscroll is enabled
152 /// </summary>
153 private bool autoscroll;
155 /// <summary>
156 /// Internal log, without progress Indicators
157 /// </summary>
158 private StringBuilder internellog = new StringBuilder();
160 #endregion
162 #region Regex Patterns
163 /// <summary>
164 /// Store const regex patterns.
165 /// The Regex objects are made to speed up matchup instead of
166 /// passing the pattern string as arguments from time to time.
167 /// </summary>
168 public static class Patterns
170 /// <summary>
171 /// ffms splitter sample output:
172 /// <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>
173 /// </summary>
174 private const string ffmsRegStr = @"^\[(?<percent>\d+\.\d+)%\]";
176 /// <summary>
177 /// ffms splitter Regex.
178 /// Available patterns: percent.
179 /// </summary>
180 public static readonly Regex ffmsReg = new Regex(ffmsRegStr);
182 /// <summary>
183 /// lavf splitter sample output:
184 /// <para>387 frames: 68.65 fps, 14.86 kb/s, 29.27 KB</para>
185 /// </summary>
186 private const string lavfRegStr = @"^(?<frame>\d+) frames:";
188 /// <summary>
189 /// lavf splitter Regex.
190 /// Available patterns: frame.
191 /// </summary>
192 public static readonly Regex lavfReg = new Regex(lavfRegStr);
194 /// <summary>
195 /// NeroAAC sample output:
196 /// <para>Processed 10 seconds...</para>
197 /// </summary>
198 private const string neroRegStr = @"Processed (?<duration>\d+) seconds...";
200 /// <summary>
201 /// NeroAAC Progress Regex.
202 /// Available patterns: duration.
203 /// </summary>
204 public static readonly Regex neroReg = new Regex(neroRegStr);
206 /// <summary>
207 /// x264 execution sample output:
208 /// <para>X:\toolDIR>"X:\toolDIR\tools\x264_32_tMod-8bit-420.exe" --crf 24 --preset 8 --demuxer ffms
209 /// -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
210 /// -o "X:\workDIR\output.ext" "X:\workDIR\input.ext" --vf subtitles --sub "X:\workDIR\sub.ext"
211 /// --acodec faac --abitrate 128</para>
212 /// </summary>
213 /// <remarks>
214 /// Without double quote: ^.+>"(?<workDIR>[^"]+)\\x264.+ -o "(?<fileOut>[^"]+)" "(?<fileIn>[^"]+)"
215 /// </remarks>
216 private const string fileRegStr = @">""(?<workDIR>[^""]+)\\x264.+-o ""(?<fileOut>[^""]+)"" ""(?<fileIn>[^""]+)""";
218 /// <summary>
219 /// Filename and working directory Regex.
220 /// Available patterns: workDIR, fileOut, fileIn.
221 /// </summary>
222 public static readonly Regex fileReg = new Regex(fileRegStr, RegexOptions.Singleline);
224 /// <summary>
225 /// ffmpeg -i sample output:
226 /// <para>Duration: 00:02:20.22, start: 0.000000, bitrate: 827 kb/s</para>
227 /// <para>Stream #0:0: Video: h264 (High), yuv420p, 1280x720, 545 kb/s, 24.42 fps, 23.98 tbr, 1k tbn, 47.95 tbc</para>
228 /// <para>Stream #0:1: Audio: aac, 48000 Hz, stereo, fltp, 128 kb/s</para>
229 /// </summary>
230 private const string ffmpegRegStr = @"\bDuration: (?<duration>\d{2}:\d{2}:\d{2}\.\d{2}), start: " +
231 @".+: Video: .+ (?<tbr>\d+\.?\d+) tbr, ";
233 /// <summary>
234 /// ffmpeg output Regex.
235 /// Available patterns: duration, tbr.
236 /// </summary>
237 public static readonly Regex ffmpegReg = new Regex(ffmpegRegStr, RegexOptions.Singleline);
240 #endregion
242 #region Native Functions
243 /// <summary>
244 /// Class hosting Native Win32 APIs
245 /// </summary>
246 private class NativeMethods
248 #region <WinUser.h>
250 /// <summary>
251 /// Specifies the type of scroll bar.
252 /// </summary>
253 public enum ScrollBarConsts : int
255 /// <summary>
256 /// Window's standard horizontal scroll bar.
257 /// </summary>
258 SB_HORZ = 0,
259 /// <summary>
260 /// Window's standard vertical scroll bar.
261 /// </summary>
262 SB_VERT = 1,
263 /// <summary>
264 /// Retrieves the position of the scroll box in a scroll bar control.
265 /// The hWnd parameter must be the handle to the scroll bar control.
266 /// </summary>
267 SB_CTL = 2,
268 /// <summary>
269 /// Window's standard scroll bar.
270 /// </summary>
271 SB_BOTH = 3
274 /// <summary>
275 /// Specifies the commands to scroll bar.
276 /// </summary>
277 public enum ScrollBarCmds : int
279 /// <summary>
280 /// Scrolls one line down.
281 /// </summary>
282 SB_LINEUP = 0,
283 /// <summary>
284 /// Scrolls left by one unit.
285 /// </summary>
286 SB_LINELEFT = 0,
287 /// <summary>
288 /// Scrolls one line up.
289 /// </summary>
290 SB_LINEDOWN = 1,
291 /// <summary>
292 /// Scrolls right by one unit.
293 /// </summary>
294 SB_LINERIGHT = 1,
295 /// <summary>
296 /// Scrolls one page up.
297 /// </summary>
298 SB_PAGEUP = 2,
299 /// <summary>
300 /// Scrolls left by the width of the window.
301 /// </summary>
302 SB_PAGELEFT = 2,
303 /// <summary>
304 /// Scrolls one page down.
305 /// </summary>
306 SB_PAGEDOWN = 3,
307 /// <summary>
308 /// Scrolls right by the width of the window.
309 /// </summary>
310 SB_PAGERIGHT = 3,
311 /// <summary>
312 /// The user has dragged the scroll box (thumb) and released the mouse button.
313 /// The HIWORD indicates the position of the scroll box at the end of the drag operation.
314 /// </summary>
315 SB_THUMBPOSITION = 4,
316 /// <summary>
317 /// The user is dragging the scroll box. This message is sent repeatedly until the user releases the mouse button.
318 /// The HIWORD indicates the position that the scroll box has been dragged to.
319 /// </summary>
320 SB_THUMBTRACK = 5,
321 /// <summary>
322 /// Scrolls to the upper left.
323 /// </summary>
324 SB_TOP = 6,
325 /// <summary>
326 /// Scrolls to the upper left.
327 /// </summary>
328 SB_LEFT = 6,
329 /// <summary>
330 /// Scrolls to the lower right.
331 /// </summary>
332 SB_BOTTOM = 7,
333 /// <summary>
334 /// Scrolls to the lower right.
335 /// </summary>
336 SB_RIGHT = 7,
337 /// <summary>
338 /// Ends scroll.
339 /// </summary>
340 SB_ENDSCROLL = 8
343 /// <summary>
344 /// Indicate the parameters to set or get.
345 /// </summary>
346 public enum ScrollBarFlags : uint
348 /// <summary>
349 /// The nMin and nMax members contain the minimum and maximum values for the scrolling range.
350 /// </summary>
351 SIF_RANGE = 0x1,
352 /// <summary>
353 /// The nPage member contains the page size for a proportional scroll bar.
354 /// </summary>
355 SIF_PAGE = 0x2,
356 /// <summary>
357 /// The nPos member contains the scroll box position, which is not updated while the user drags the scroll box.
358 /// </summary>
359 SIF_POS = 0x4,
360 /// <summary>
361 /// This value is used only when setting a scroll bar's parameters.
362 /// If the scroll bar's new parameters make the scroll bar unnecessary, disable the scroll bar instead of removing it.
363 /// </summary>
364 SIF_DISABLENOSCROLL = 0x8,
365 /// <summary>
366 /// The nTrackPos member contains the current position of the scroll box while the user is dragging it.
367 /// </summary>
368 SIF_TRACKPOS = 0x10,
369 /// <summary>
370 /// Combination of SIF_PAGE, SIF_POS, SIF_RANGE, and SIF_TRACKPOS.
371 /// </summary>
372 SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS)
375 /// <summary>
376 /// The flash status.
377 /// </summary>
378 public enum FlashWindowFlags : uint
380 /// <summary>
381 /// Stop flashing. The system restores the window to its original state.
382 /// </summary>
383 FLASHW_STOP = 0,
384 /// <summary>
385 /// Flash the window caption.
386 /// </summary>
387 FLASHW_CAPTION = 0x1,
388 /// <summary>
389 /// Flash the taskbar button.
390 /// </summary>
391 FLASHW_TRAY = 0x2,
392 /// <summary>
393 /// Flash both the window caption and taskbar button.
394 /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
395 /// </summary>
396 FLASHW_ALL = (FLASHW_CAPTION | FLASHW_TRAY),
397 /// <summary>
398 /// Flash continuously, until the FLASHW_STOP flag is set.
399 /// </summary>
400 FLASHW_TIMER = 0x4,
401 /// <summary>
402 /// Flash continuously until the window comes to the foreground.
403 /// </summary>
404 FLASHW_TIMERNOFG = 0xC
407 /// <summary>
408 /// Window Messages
409 /// </summary>
410 public enum Win32Msgs : uint
412 /// <summary>
413 /// The WM_HSCROLL message is sent to a window when a scroll event occurs in the window's standard horizontal scroll bar.
414 /// This message is also sent to the owner of a horizontal scroll bar control when a scroll event occurs in the control.
415 /// </summary>
416 WM_HSCROLL = 0x114,
417 /// <summary>
418 /// The WM_VSCROLL message is sent to a window when a scroll event occurs in the window's standard vertical scroll bar.
419 /// This message is also sent to the owner of a vertical scroll bar control when a scroll event occurs in the control.
420 /// </summary>
421 WM_VSCROLL = 0x115,
422 /// <summary>
423 /// The WM_CTLCOLORSCROLLBAR message is sent to the parent window of a scroll bar control when the control is about to be drawn.
424 /// By responding to this message, the parent window can use the display context handle to set the background color of the scroll bar control.
425 /// </summary>
426 WM_CTLCOLORSCROLLBAR = 0x137
429 /// <summary>
430 /// Contains scroll bar parameters to be set by the SetScrollInfo function, or retrieved by the GetScrollInfo function.
431 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/bb787537(v=vs.85).aspx</para>
432 /// </summary>
433 [StructLayout(LayoutKind.Sequential)]
434 public struct SCROLLINFO
436 /// <summary>
437 /// Specifies the size, in bytes, of this structure.
438 /// </summary>
439 public uint cbSize;
440 /// <summary>
441 /// Specifies the scroll bar parameters to set or retrieve.
442 /// </summary>
443 public ScrollBarFlags fMask;
444 /// <summary>
445 /// Specifies the minimum scrolling position.
446 /// </summary>
447 public int nMin;
448 /// <summary>
449 /// Specifies the maximum scrolling position.
450 /// </summary>
451 public int nMax;
452 /// <summary>
453 /// Specifies the page size, in device units.
454 /// A scroll bar uses this value to determine the appropriate size of the proportional scroll box.
455 /// </summary>
456 public uint nPage;
457 /// <summary>
458 /// Specifies the position of the scroll box.
459 /// </summary>
460 public int nPos;
461 /// <summary>
462 /// Specifies the immediate position of a scroll box that the user is dragging.
463 /// An application cannot set the immediate scroll position; the SetScrollInfo function ignores this member.
464 /// </summary>
465 public int nTrackPos;
468 /// <summary>
469 /// Contains the flash status for a window and the number of times the system should flash the window.
470 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms679348(v=vs.85).aspx</para>
471 /// </summary>
472 [StructLayout(LayoutKind.Sequential)]
473 public struct FLASHWINFO
475 /// <summary>
476 /// The size of the structure, in bytes.
477 /// </summary>
478 public uint cbSize;
479 /// <summary>
480 /// A Handle to the Window to be Flashed. The window can be either opened or minimized.
481 /// </summary>
482 public IntPtr hwnd;
483 /// <summary>
484 /// The Flash Status.
485 /// </summary>
486 public FlashWindowFlags dwFlags;
487 /// <summary>
488 /// The number of times to Flash the window.
489 /// </summary>
490 public uint uCount;
491 /// <summary>
492 /// The rate at which the Window is to be flashed, in milliseconds.
493 /// If Zero, the function uses the default cursor blink rate.
494 /// </summary>
495 public uint dwTimeout;
498 /// <summary>
499 /// Creates a value for use as a wParam parameter in a message.
500 /// The macro concatenates the specified values.
501 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632664(v=vs.85).aspx</para>
502 /// </summary>
503 /// <param name="wLow">The low-order word of the new value.</param>
504 /// <param name="wHi">The high-order word of the new value.</param>
505 /// <returns>The return value is a WPARAM value.</returns>
506 public static UIntPtr MakeWParam(uint wLow, uint wHi)
508 return (UIntPtr)MakeLong(wLow, wHi);
511 /// <summary>
512 /// Creates a value for use as an lParam parameter in a message.
513 /// The macro concatenates the specified values.
514 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632661(v=vs.85).aspx</para>
515 /// </summary>
516 /// <param name="wLow">The low-order word of the new value.</param>
517 /// <param name="wHi">The high-order word of the new value. </param>
518 /// <returns>The return value is an LPARAM value. </returns>
519 public static IntPtr MakeLParam(uint wLow, uint wHi)
521 return (IntPtr)MakeLong(wLow, wHi);
524 #endregion
526 #region <WinDef.h>
528 /// <summary>
529 /// Retrieves the high-order word from the specified 32-bit value.
530 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632657(v=vs.85).aspx</para>
531 /// </summary>
532 /// <param name="ptr">The value to be converted.</param>
533 /// <returns>The return value is the high-order word of the specified value.</returns>
534 public static uint HiWord(IntPtr ptr)
536 return ((uint)ptr >> 16) & 0xFFFFu;
539 /// <summary>
540 /// Retrieves the low-order word from the specified value.
541 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632659(v=vs.85).aspx</para>
542 /// </summary>
543 /// <param name="ptr">The value to be converted.</param>
544 /// <returns>The return value is the low-order word of the specified value.</returns>
545 public static uint LoWord(IntPtr ptr)
547 return (uint)ptr & 0xFFFFu;
550 /// <summary>
551 /// Creates a LONG value by concatenating the specified values.
552 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms632660(v=vs.85).aspx</para>
553 /// </summary>
554 /// <param name="wLow">The low-order word of the new value.</param>
555 /// <param name="wHi">The high-order word of the new value.</param>
556 /// <returns>The return value is a LONG value.</returns>
557 public static uint MakeLong(uint wLow, uint wHi)
559 return (wLow & 0xFFFFu) | ((wHi & 0xFFFFu) << 16);
562 #endregion
564 /// <summary>
565 /// The GetScrollInfo function retrieves the parameters of a scroll bar,
566 /// including the minimum and maximum scrolling positions, the page size,
567 /// and the position of the scroll box (thumb).
568 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/bb787583(v=vs.85).aspx</para>
569 /// </summary>
570 /// <param name="hWnd">Handle to a scroll bar control or a window with a standard scroll bar,
571 /// depending on the value of the fnBar parameter. </param>
572 /// <param name="fnBar">Specifies the type of scroll bar for which to retrieve parameters.</param>
573 /// <param name="lpsi">Pointer to a SCROLLINFO structure. Before calling GetScrollInfo, set the cbSize member to sizeof(SCROLLINFO),
574 /// and set the fMask member to specify the scroll bar parameters to retrieve.
575 /// Before returning, the function copies the specified parameters to the appropriate members of the structure.</param>
576 /// <returns>If the function retrieved any values, the return value is nonzero.</returns>
577 [DllImport("user32.dll")]
578 [return: MarshalAs(UnmanagedType.Bool)]
579 public static extern bool GetScrollInfo(
580 IntPtr hWnd,
581 ScrollBarConsts fnBar,
582 ref SCROLLINFO lpsi);
584 /// <summary>
585 /// The SetScrollInfo function sets the parameters of a scroll bar, including the minimum and maximum scrolling positions,
586 /// the page size, and the position of the scroll box (thumb). The function also redraws the scroll bar, if requested.
587 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/bb787595(v=vs.85).aspx</para>
588 /// </summary>
589 /// <param name="hWnd">Handle to a scroll bar control or a window with a standard scroll bar,
590 /// depending on the value of the fnBar parameter. </param>
591 /// <param name="fnBar">Specifies the type of scroll bar for which to set parameters.</param>
592 /// <param name="lpsi">Pointer to a SCROLLINFO structure. Before calling SetScrollInfo, set the cbSize member of the structure to sizeof(SCROLLINFO),
593 /// set the fMask member to indicate the parameters to set, and specify the new parameter values in the appropriate members.</param>
594 /// <param name="fRedraw">Specifies whether the scroll bar is redrawn to reflect the changes to the scroll bar.
595 /// If this parameter is TRUE, the scroll bar is redrawn, otherwise, it is not redrawn. </param>
596 /// <returns>The current position of the scroll box.</returns>
597 [DllImport("user32.dll")]
598 public static extern int SetScrollInfo(
599 IntPtr hWnd,
600 ScrollBarConsts fnBar,
601 ref SCROLLINFO lpsi,
602 [MarshalAs(UnmanagedType.Bool)] bool fRedraw);
604 /// <summary>
605 /// Flashes the specified window. It does not change the active state of the window.
606 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms679347(v=vs.85).aspx</para>
607 /// </summary>
608 /// <param name="pwfi">A pointer to a FLASHWINFO structure.</param>
609 /// <returns>The return value specifies the window's state before the call to the FlashWindowEx function.
610 /// If the window caption was drawn as active before the call, the return value is nonzero. Otherwise, the return value is zero.</returns>
611 [DllImport("user32.dll")]
612 public static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
614 /// <summary>
615 /// Places (posts) a message in the message queue associated with the thread that created
616 /// the specified window and returns without waiting for the thread to process the message.
617 /// <para>http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx</para>
618 /// </summary>
619 /// <param name="hWnd">A handle to the window whose window procedure is to receive the message.</param>
620 /// <param name="Msg">The message to be posted.</param>
621 /// <param name="wParam">Additional message-specific information.</param>
622 /// <param name="lParam">Additional message-specific information.</param>
623 /// <returns>If the function succeeds, the return value is nonzero.</returns>
624 [DllImport("user32.dll")]
625 [return: MarshalAs(UnmanagedType.Bool)]
626 public static extern bool PostMessage(
627 IntPtr hWnd,
628 Win32Msgs Msg,
629 [MarshalAs(UnmanagedType.SysUInt)] UIntPtr wParam,
630 [MarshalAs(UnmanagedType.SysInt)] IntPtr lParam);
632 #endregion
634 #region UI Methods
636 private void WorkingForm_Load(object sender, EventArgs e)
638 // save commands into a batch file
639 batPath = System.IO.Path.Combine(
640 Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.User),
641 "xiaowan" + DateTime.Now.ToFileTimeUtc().ToString() + ".bat");
642 var encoder = Encoding.GetEncoding(0);
643 var sw = new System.IO.StreamWriter(batPath, false, encoder);
644 sw.WriteLine(Commands);
645 sw.Close();
646 // synchronize UI
647 workCompleted = -1;
648 richTextBoxOutput.Select();
649 // check win7 supported
650 win7supported = (Environment.OSVersion.Version.Major > 6 ||
651 (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1));
652 if (win7supported)
653 TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal);
654 // validate the command string
655 if (Commands.Equals(encoder.GetString(encoder.GetBytes(Commands))))
656 ProcStart();
657 else
659 MessageBox.Show("Path or filename contains invalid characters, please rename and retry."
660 + Environment.NewLine +
661 "路径或文件名含有不可识别的字符,请重命名后重试。");
662 this.Close();
664 notifyIcon.Visible = true;
665 MainForm main = (MainForm)this.Owner;
666 workPath = main.workPath;
667 autoscroll = true;
670 private void WorkingForm_FormClosing(object sender, FormClosingEventArgs e)
672 // check remaining works
673 if (!(proc == null || proc.HasExited))
675 // ask if user hit close button on any accident
676 var result = MessageBox.Show("Processing is still undergoing, confirm exit?",
677 "Xiaowan", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
678 if (result == System.Windows.Forms.DialogResult.No)
680 // cancel event and return
681 e.Cancel = true;
682 return;
684 else
685 ProcAbort();
687 // clean up temp batch file
688 System.IO.File.Delete(batPath);
691 private void buttonAbort_Click(object sender, EventArgs e)
693 ProcAbort();
696 private void buttonSave_Click(object sender, EventArgs e)
698 // prohibit saving before work completion
699 if (!proc.HasExited)
700 MessageBox.Show("Saving before completion is not recommended." +
701 Environment.NewLine + "Please manually abort the process or wait patiently.");
702 else
704 var savDlg = new SaveFileDialog();
705 savDlg.FileName = "log_" + DateTime.Now.ToShortDateString().Replace('/', '-');
706 savDlg.Filter = "Log files|*.log|All files|*.*";
707 savDlg.FilterIndex = 1;
708 savDlg.RestoreDirectory = true;
709 if (savDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
710 System.IO.File.WriteAllText(savDlg.FileName, internellog.ToString());
714 private void richTextBoxOutput_VScroll(object sender, EventArgs e)
716 var info = new NativeMethods.SCROLLINFO();
717 info.cbSize = Convert.ToUInt32(Marshal.SizeOf(info));
718 info.fMask = NativeMethods.ScrollBarFlags.SIF_ALL;
719 NativeMethods.GetScrollInfo(richTextBoxOutput.Handle, NativeMethods.ScrollBarConsts.SB_VERT, ref info);
720 autoscroll = (info.nTrackPos + richTextBoxOutput.Font.Height
721 > info.nMax - richTextBoxOutput.ClientSize.Height);
724 private void WorkingForm_Resize(object sender, EventArgs e)
726 if (this.WindowState == FormWindowState.Minimized)
727 BalloonTip("丸子跑到这里了喔~", 75);
730 private void notifyIcon_MouseClick(object sender, MouseEventArgs e)
732 this.Visible = true;
733 this.WindowState = FormWindowState.Normal;
734 this.Activate();
737 private void richTextBoxOutput_Enter(object sender, EventArgs e)
739 this.ActiveControl = null;
742 #endregion
744 /// <summary>
745 /// Start the working process.
746 /// </summary>
747 private void ProcStart()
749 // setup the process
750 CheckFileExist(batPath);
751 var processInfo = new System.Diagnostics.ProcessStartInfo(batPath, "2>&1");
752 processInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
753 processInfo.CreateNoWindow = true;
754 processInfo.UseShellExecute = false;
755 processInfo.RedirectStandardOutput = true;
756 proc = System.Diagnostics.Process.Start(processInfo);
757 // attach exit event handler
758 proc.EnableRaisingEvents = true;
759 proc.Exited += new EventHandler(ProcExit);
760 // setup asynchronous reading
761 proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(OutputHandler);
762 proc.BeginOutputReadLine();
765 /// <summary>
766 /// Process Exit event handler. Automatically called when process exit normally.
767 /// </summary>
768 private void ProcExit(object sender, EventArgs e)
770 // synchronize UI
771 UpdateWorkCountUI();
772 buttonAbort.InvokeIfRequired(() =>
773 buttonAbort.Enabled = false);
774 UpdateProgress(1);
775 if (win7supported)
776 TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress);
777 // wait a little bit for the last asynchronous reading
778 System.Threading.Thread.Sleep(75);
779 // append finish tag
780 Print(Environment.NewLine + "Work Complete!");
781 // fire a warning if something went wrong
782 // this feature need %ERRORLEVEL% support in batch commands
783 if (proc.ExitCode != 0)
785 Print(Environment.NewLine +
786 "Potential Error detected. Please double check the log.");
787 Print(Environment.NewLine +
788 "Exit code is: " + proc.ExitCode.ToString());
790 // flash form and show balloon tips
791 FlashForm();
792 BalloonTip(@"完成了喵~ (= ω =)");
794 // shutdown the system if required
795 MainForm main = (MainForm)this.Owner;
796 if (main.shutdownState)
798 System.Diagnostics.Process.Start("shutdown", "-s");
799 // wait a bit to ensure synchronization
800 System.Threading.Thread.Sleep(75);
801 if (System.Windows.Forms.DialogResult.Cancel == MessageBox.Show(
802 "System will shutdown in 20 seconds. Click \"Cancel\" to stop countdown."
803 + Environment.NewLine +
804 "系统将在20秒后自动关机。点击“取消”停止倒计时。",
805 "Warning", MessageBoxButtons.OKCancel))
807 System.Diagnostics.Process.Start("shutdown", "-a");
812 /// <summary>
813 /// Manually call this method to abort all workings.
814 /// </summary>
815 private void ProcAbort()
817 // return value is useless when force aborted
818 proc.CancelOutputRead();
819 // exit process should be omitted too
820 proc.Exited -= new EventHandler(ProcExit);
821 // terminate threads
822 killProcTree(proc.Id);
823 // reset taskbar progress
824 if (win7supported)
825 TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress);
826 // Print abort message to log
827 Print(Environment.NewLine + "Work is aborted by user.");
830 /// <summary>
831 /// Asynchronous reading DataReceived event handler.
832 /// </summary>
833 private void OutputHandler(object sender, System.Diagnostics.DataReceivedEventArgs e)
835 if (!String.IsNullOrEmpty(e.Data))
837 // convert and log first
838 Print(e.Data + Environment.NewLine);
839 // test if it is command
840 Match result = Patterns.fileReg.Match(e.Data);
841 if (result.Success)
843 UpdateWorkCountUI();
844 progressBarX264.InvokeIfRequired(() =>
845 progressBarX264.Style = ProgressBarStyle.Blocks
847 UpdateProgress(0);
848 frameCount = EstimateFrame(workPath, result.Groups["fileIn"].Value);
850 // try ffms pattern
851 result = Patterns.ffmsReg.Match(e.Data);
852 if (result.Success)
853 UpdateProgress(Double.Parse(result.Groups["percent"].Value) / 100);
854 else
856 // try lavf pattern
857 result = Patterns.lavfReg.Match(e.Data);
858 if (result.Success)
859 UpdateProgress(Double.Parse(result.Groups["frame"].Value) / frameCount);
860 else
862 // try nero pattern
863 result = Patterns.neroReg.Match(e.Data);
864 if (!result.Success)
865 internellog.AppendLine(e.Data);
871 /// <summary>
872 /// Kill a process as well as all childs by PID.
873 /// </summary>
874 /// <param name="pid">The PID of the target process.</param>
875 private void killProcTree(int pid)
877 // recursive first
878 foreach (var m in new System.Management.ManagementObjectSearcher(
879 "Select * From Win32_Process Where ParentProcessID=" + pid).Get())
881 killProcTree(Convert.ToInt32(m["ProcessID"]));
883 // then suicide, usually threads throw exceptions when killed. Dump them silently.
886 System.Diagnostics.Process.GetProcessById(pid).Kill();
888 catch (ArgumentException) { }
891 /// <summary>
892 /// Update Work Count UI on call. Each call will bump up completed work count by 1.
893 /// </summary>
894 private void UpdateWorkCountUI()
896 ++workCompleted;
897 this.InvokeIfRequired(() =>
898 this.Text = "Xiaowan (" + workCompleted + '/' + WorkQueued + ')');
899 this.labelworkCount.InvokeIfRequired(() =>
900 labelworkCount.Text = workCompleted.ToString() + '/' + WorkQueued + " Completed");
903 /// <summary>
904 /// Update Progress Bar as well as the number on it.
905 /// </summary>
906 /// <param name="value">Progress expressed in decimal (0.00-1.00).</param>
907 private void UpdateProgress(double value)
909 // Some kind of safeguard
910 if (value < 0)
911 value = 0;
912 if (value > 1)
913 value = 1;
914 // Update UI
915 progressBarX264.InvokeIfRequired(() =>
916 progressBarX264.Value = Convert.ToInt32(value * progressBarX264.Maximum));
917 labelProgress.InvokeIfRequired(() =>
918 labelProgress.Text = value.ToString("P"));
919 if (win7supported)
920 TaskbarManager.Instance.SetProgressValue(
921 Convert.ToInt32(value * progressBarX264.Maximum), progressBarX264.Maximum);
922 notifyIcon.Text = "小丸工具箱" + Environment.NewLine +
923 labelworkCount.Text + " - " + labelProgress.Text;
926 /// <summary>
927 /// Get a rough estimation on frame counts via FFmpeg.
928 /// If failed, return <see cref="Int32.MaxValue"/> instead.
929 /// </summary>
930 /// <param name="workPath">Path to ffmpeg binary.</param>
931 /// <param name="filePath">Path to target file.</param>
932 /// <returns>Estimated frame count. 1% tolerance added.</returns>
933 private int EstimateFrame(string workPath, string filePath)
935 string ffmpegPath = System.IO.Path.Combine(workPath, "ffmpeg.exe");
936 CheckFileExist(ffmpegPath);
937 var processInfo = new System.Diagnostics.ProcessStartInfo(ffmpegPath, "-i \"" + filePath + '"');
938 processInfo.CreateNoWindow = true;
939 processInfo.UseShellExecute = false;
940 processInfo.RedirectStandardError = true;
941 var ffproc = System.Diagnostics.Process.Start(processInfo);
942 // log and append
943 string mediaInfo = ffproc.StandardError.ReadToEnd();
944 Print("Input file: " + filePath + Environment.NewLine);
945 Print(mediaInfo + Environment.NewLine);
946 ffproc.WaitForExit();
947 var result = Patterns.ffmpegReg.Match(mediaInfo);
948 if (!result.Success)
950 Print("Warning: Error detected on previous file. Estimatation may not work."
951 + Environment.NewLine);
952 return Int32.MaxValue;
954 else
955 // add a 1% tolerance to avoid unintentional overflow on progress bar
956 return Convert.ToInt32(TimeSpan.Parse(result.Groups["duration"].Value).TotalSeconds
957 * Double.Parse(result.Groups["tbr"].Value) * 1.01);
960 /// <summary>
961 /// Flash the current form.
962 /// </summary>
963 /// <returns>Whether the form need flash or not.</returns>
964 private bool FlashForm()
966 var info = new NativeMethods.FLASHWINFO();
967 info.cbSize = Convert.ToUInt32(Marshal.SizeOf(info));
968 this.InvokeIfRequired(() =>
969 info.hwnd = this.Handle);
970 info.dwFlags = NativeMethods.FlashWindowFlags.FLASHW_ALL |
971 NativeMethods.FlashWindowFlags.FLASHW_TIMERNOFG;
972 // Flash 3 times
973 info.uCount = 3;
974 info.dwTimeout = 0;
975 return NativeMethods.FlashWindowEx(ref info);
978 /// <summary>
979 /// Pop a balloon tip.
980 /// </summary>
981 /// <param name="notes">The content to show.</param>
982 /// <param name="timeout">Tip timeout.</param>
983 private void BalloonTip(string notes, int timeout = 500)
985 MainForm main = (MainForm)this.Owner;
986 if (main.trayMode)
988 notifyIcon.Visible = true;
989 notifyIcon.ShowBalloonTip(timeout, "小丸工具箱", notes, ToolTipIcon.Info);
993 /// <summary>
994 /// Append and scroll Textbox if needed.
995 /// </summary>
996 /// <param name="str">String to append.</param>
997 private void Print(string str)
999 richTextBoxOutput.InvokeIfRequired(() =>
1001 richTextBoxOutput.AppendText(str);
1002 if (autoscroll)
1004 var info = new NativeMethods.SCROLLINFO();
1005 info.cbSize = Convert.ToUInt32(Marshal.SizeOf(info));
1006 info.fMask = NativeMethods.ScrollBarFlags.SIF_RANGE;
1007 NativeMethods.GetScrollInfo(richTextBoxOutput.Handle, NativeMethods.ScrollBarConsts.SB_VERT, ref info);
1008 NativeMethods.PostMessage(richTextBoxOutput.Handle, NativeMethods.Win32Msgs.WM_VSCROLL,
1009 NativeMethods.MakeWParam((uint)NativeMethods.ScrollBarCmds.SB_THUMBPOSITION, (uint)info.nMax), IntPtr.Zero);