diff --git a/CDN-Video-Uploader.csproj b/CDN-Video-Uploader.csproj index a0fb28a..4fc1112 100644 --- a/CDN-Video-Uploader.csproj +++ b/CDN-Video-Uploader.csproj @@ -7,6 +7,14 @@ true + + true + + + + true + + diff --git a/CDN-Video-Uploader.csproj.user b/CDN-Video-Uploader.csproj.user index 9ecb14a..c62d3da 100644 --- a/CDN-Video-Uploader.csproj.user +++ b/CDN-Video-Uploader.csproj.user @@ -14,5 +14,8 @@ Form + + Component + \ No newline at end of file diff --git a/Forms/FormViewJob.Designer.cs b/Forms/FormViewJob.Designer.cs index eb05814..98aabd3 100644 --- a/Forms/FormViewJob.Designer.cs +++ b/Forms/FormViewJob.Designer.cs @@ -42,8 +42,6 @@ private void InitializeComponent() this.textBoxJobProgress = new System.Windows.Forms.TextBox(); this.textBoxJobDescription = new System.Windows.Forms.TextBox(); this.dataGridViewActions = new System.Windows.Forms.DataGridView(); - this.textBoxLogs = new System.Windows.Forms.TextBox(); - this.timerRefreshUI = new System.Windows.Forms.Timer(this.components); this.ColumnAction = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.ColumnActionType = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.ColumnState = new System.Windows.Forms.DataGridViewTextBoxColumn(); @@ -51,6 +49,8 @@ private void InitializeComponent() this.ColumnFinished = new System.Windows.Forms.DataGridViewCheckBoxColumn(); this.ColumnTime = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.ColumnLogs = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.textBoxLogs = new System.Windows.Forms.TextBox(); + this.timerRefreshUI = new System.Windows.Forms.Timer(this.components); groupBoxJobDetails = new System.Windows.Forms.GroupBox(); labelState = new System.Windows.Forms.Label(); labelProgress = new System.Windows.Forms.Label(); @@ -213,37 +213,6 @@ private void InitializeComponent() this.dataGridViewActions.Text = "dataGridView1"; this.dataGridViewActions.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridViewActions_CellClick); // - // groupBoxLogs - // - groupBoxLogs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - groupBoxLogs.Controls.Add(this.textBoxLogs); - groupBoxLogs.Location = new System.Drawing.Point(12, 377); - groupBoxLogs.Name = "groupBoxLogs"; - groupBoxLogs.Size = new System.Drawing.Size(1372, 284); - groupBoxLogs.TabIndex = 2; - groupBoxLogs.TabStop = false; - groupBoxLogs.Text = "Logs"; - // - // textBoxLogs - // - this.textBoxLogs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.textBoxLogs.Location = new System.Drawing.Point(16, 26); - this.textBoxLogs.Multiline = true; - this.textBoxLogs.Name = "textBoxLogs"; - this.textBoxLogs.ReadOnly = true; - this.textBoxLogs.Size = new System.Drawing.Size(1340, 241); - this.textBoxLogs.TabIndex = 0; - // - // timerRefreshUI - // - this.timerRefreshUI.Enabled = true; - this.timerRefreshUI.Interval = 500; - this.timerRefreshUI.Tick += new System.EventHandler(this.timerRefreshUI_Tick); - // // ColumnAction // this.ColumnAction.DataPropertyName = "Description"; @@ -309,6 +278,38 @@ private void InitializeComponent() this.ColumnLogs.Name = "ColumnLogs"; this.ColumnLogs.ReadOnly = true; // + // groupBoxLogs + // + groupBoxLogs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBoxLogs.Controls.Add(this.textBoxLogs); + groupBoxLogs.Location = new System.Drawing.Point(12, 377); + groupBoxLogs.Name = "groupBoxLogs"; + groupBoxLogs.Size = new System.Drawing.Size(1372, 284); + groupBoxLogs.TabIndex = 2; + groupBoxLogs.TabStop = false; + groupBoxLogs.Text = "Logs"; + // + // textBoxLogs + // + this.textBoxLogs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxLogs.Location = new System.Drawing.Point(16, 26); + this.textBoxLogs.Multiline = true; + this.textBoxLogs.Name = "textBoxLogs"; + this.textBoxLogs.ReadOnly = true; + this.textBoxLogs.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBoxLogs.Size = new System.Drawing.Size(1340, 241); + this.textBoxLogs.TabIndex = 0; + // + // timerRefreshUI + // + this.timerRefreshUI.Enabled = true; + this.timerRefreshUI.Interval = 500; + this.timerRefreshUI.Tick += new System.EventHandler(this.timerRefreshUI_Tick); + // // FormViewJob // this.ClientSize = new System.Drawing.Size(1396, 673); diff --git a/Jobs/TranscodeAction.cs b/Jobs/TranscodeAction.cs index d178d3e..ceca956 100644 --- a/Jobs/TranscodeAction.cs +++ b/Jobs/TranscodeAction.cs @@ -1,4 +1,5 @@ using CDN_Video_Uploader.Properties; +using CDN_Video_Uploader.Utils; using System; using System.Diagnostics; using System.IO; @@ -13,7 +14,7 @@ public class TranscodeAction : ExecutableAction public string TranscodingCommand { get; set; } public string OutputFile { get; set; } - private Process transcodeProcess; + private BackgroundProcess transcodeProcess; private static object ActiveTranscodingActionsLock = new object(); public static int ActiveTranscodingActions { get; private set; } @@ -62,7 +63,7 @@ public override void Start() return; } - this.transcodeProcess = new Process + this.transcodeProcess = new BackgroundProcess { StartInfo = { @@ -81,7 +82,7 @@ public override void Start() this.AppendToLog($"{cmdExecutable} {cmdParams}"); try { - this.transcodeProcess.Start(); + this.transcodeProcess.StartMinimizedNoFocus(); this.ExecutionState = ExecutionState.Running; } catch (Exception ex) diff --git a/Utils/BackgroundProcess.cs b/Utils/BackgroundProcess.cs new file mode 100644 index 0000000..e096352 --- /dev/null +++ b/Utils/BackgroundProcess.cs @@ -0,0 +1,151 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using System.Threading; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace CDN_Video_Uploader.Utils +{ + public class BackgroundProcess : Process + { + public unsafe bool StartMinimizedNoFocus() + { + if (string.IsNullOrEmpty(this.StartInfo.FileName)) + throw new InvalidOperationException("Missing StartInfo.FileName"); + + if (!this.StartInfo.UseShellExecute) + throw new InvalidOperationException( + "Cannot start `minimized no focus` process without ShellExecute"); + + // Stop the process if it is currently running + this.Close(); + + fixed (char* fileName = this.StartInfo.FileName.Length > 0 ? this.StartInfo.FileName : null) + fixed (char* verb = this.StartInfo.Verb.Length > 0 ? this.StartInfo.Verb : null) + fixed (char* args = this.StartInfo.Arguments.Length > 0 ? this.StartInfo.Arguments : null) + fixed (char* directory = this.StartInfo.WorkingDirectory.Length > 0 ? this.StartInfo.WorkingDirectory : null) + { + Shell32.SHELLEXECUTEINFO shellExecuteInfo = new Shell32.SHELLEXECUTEINFO() + { + cbSize = (uint)sizeof(Shell32.SHELLEXECUTEINFO), + lpFile = fileName, + lpVerb = verb, + lpParameters = args, + lpDirectory = directory, + fMask = Shell32.SEE_MASK_NOCLOSEPROCESS | Shell32.SEE_MASK_FLAG_DDEWAIT + }; + + if (this.StartInfo.ErrorDialog) + shellExecuteInfo.hwnd = this.StartInfo.ErrorDialogParentHandle; + else + shellExecuteInfo.fMask |= Shell32.SEE_MASK_FLAG_NO_UI; + + shellExecuteInfo.nShow = Shell32.SW_SHOWMINNOACTIVE; + + ShellExecuteHelper executeHelper = new ShellExecuteHelper(&shellExecuteInfo); + if (!executeHelper.ShellExecuteOnSTAThread()) + { + int error = executeHelper.ErrorCode; + if (error == 0) + error = (int)(long)error; + throw new Win32Exception(error); + } + + if (shellExecuteInfo.hProcess != IntPtr.Zero) + { + // SetProcessHandle(new SafeProcessHandle(shellExecuteInfo.hProcess)); + // Invoke the above private emthod through reflection + SafeProcessHandle safeHandle = + new SafeProcessHandle(shellExecuteInfo.hProcess, true); + Type typeProcess = typeof(Process); + MethodInfo setProcessHandleMethod = typeProcess.GetMethod("SetProcessHandle", + BindingFlags.NonPublic | BindingFlags.Instance); + setProcessHandleMethod.Invoke(this, new object[] { safeHandle }); + + return true; + } + } + + return false; + } + + internal unsafe class ShellExecuteHelper + { + private Shell32.SHELLEXECUTEINFO* _executeInfo; + private bool _succeeded; + private int _errorCode; + + public ShellExecuteHelper(Shell32.SHELLEXECUTEINFO* executeInfo) + { + this._executeInfo = executeInfo; + } + + public void ShellExecuteFunction() + { + if (!(this._succeeded = Shell32.ShellExecuteEx(this._executeInfo))) + { + this._errorCode = Marshal.GetLastWin32Error(); + } + } + + public bool ShellExecuteOnSTAThread() + { + if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) + { + ThreadStart start = new ThreadStart(this.ShellExecuteFunction); + Thread thread = new Thread(start); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + } + else + { + this.ShellExecuteFunction(); + } + return this._succeeded; + } + + public int ErrorCode => this._errorCode; + } + + internal class Shell32 + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal unsafe struct SHELLEXECUTEINFO + { + public uint cbSize; + public uint fMask; + public IntPtr hwnd; + public char* lpVerb; + public char* lpFile; + public char* lpParameters; + public char* lpDirectory; + public int nShow; + public IntPtr hInstApp; + public IntPtr lpIDList; + public IntPtr lpClass; + public IntPtr hkeyClass; + public uint dwHotKey; + // This is a union of hIcon and hMonitor + public IntPtr hIconMonitor; + public IntPtr hProcess; + } + + internal const int SW_HIDE = 0; + internal const int SW_SHOWNORMAL = 1; + internal const int SW_SHOWMINIMIZED = 2; + internal const int SW_SHOWMAXIMIZED = 3; + internal const int SW_SHOWNOACTIVATE = 4; + internal const int SW_SHOWMINNOACTIVE = 7; + + internal const uint SEE_MASK_FLAG_DDEWAIT = 0x00000100; + internal const uint SEE_MASK_NOCLOSEPROCESS = 0x00000040; + internal const uint SEE_MASK_FLAG_NO_UI = 0x00000400; + + [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static unsafe extern bool ShellExecuteEx(SHELLEXECUTEINFO* lpExecInfo); + } + } +}