Skip to content

Commit

Permalink
Implemented "start minimized, no focus" for ffmpeg process start
Browse files Browse the repository at this point in the history
  • Loading branch information
nakov committed Dec 18, 2020
1 parent eeabfcf commit ddc304e
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 36 deletions.
8 changes: 8 additions & 0 deletions CDN-Video-Uploader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentFTP" Version="33.0.2" />
</ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions CDN-Video-Uploader.csproj.user
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@
<Compile Update="Forms\FormViewJob.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Utils\BackgroundProcess.cs">
<SubType>Component</SubType>
</Compile>
</ItemGroup>
</Project>
67 changes: 34 additions & 33 deletions Forms/FormViewJob.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Jobs/TranscodeAction.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CDN_Video_Uploader.Properties;
using CDN_Video_Uploader.Utils;
using System;
using System.Diagnostics;
using System.IO;
Expand All @@ -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; }
Expand Down Expand Up @@ -62,7 +63,7 @@ public override void Start()
return;
}

this.transcodeProcess = new Process
this.transcodeProcess = new BackgroundProcess
{
StartInfo =
{
Expand All @@ -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)
Expand Down
151 changes: 151 additions & 0 deletions Utils/BackgroundProcess.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

0 comments on commit ddc304e

Please sign in to comment.