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);
+ }
+ }
+}