Initial ProcessUtils update

ProcessUtils functions don't work at the moment, so I need to fix them!
This commit is contained in:
Terry MacDonald 2021-11-06 18:38:37 +13:00
parent 712f0333f5
commit a80349f31a
4 changed files with 219 additions and 255 deletions

View File

@ -1,253 +1,124 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace DisplayMagician
{
[Flags]
public enum ProcessCreationFlags : uint
public class ProcessCreator
{
ZERO_FLAG = 0x00000000,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_NO_WINDOW = 0x08000000,
CREATE_PROTECTED_PROCESS = 0x00040000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_SEPARATE_WOW_VDM = 0x00001000,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_SUSPENDED = 0x00000004,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
DEBUG_PROCESS = 0x00000001,
DETACHED_PROCESS = 0x00000008,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
INHERIT_PARENT_AFFINITY = 0x00010000,
// Process creations flags
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000,
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000,
HIGH_PRIORITY_CLASS = 0x00000080,
IDLE_PRIORITY_CLASS = 0x00000040,
NORMAL_PRIORITY_CLASS = 0x00000020,
REALTIME_PRIORITY_CLASS = 0x00000100,
}
[Flags]
public enum PROCESS_CREATION_FLAGS : uint
{
ZERO_FLAG = 0x00000000,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_NO_WINDOW = 0x08000000,
CREATE_PROTECTED_PROCESS = 0x00040000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_SEPARATE_WOW_VDM = 0x00001000,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_SUSPENDED = 0x00000004,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
DEBUG_PROCESS = 0x00000001,
DETACHED_PROCESS = 0x00000008,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
INHERIT_PARENT_AFFINITY = 0x00010000,
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
// Process creations flags
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000,
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000,
HIGH_PRIORITY_CLASS = 0x00000080,
IDLE_PRIORITY_CLASS = 0x00000040,
NORMAL_PRIORITY_CLASS = 0x00000020,
REALTIME_PRIORITY_CLASS = 0x00000100,
}
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
public static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,
bool bInheritHandles, ProcessCreationFlags dwCreationFlags, IntPtr lpEnvironment,
string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern uint ResumeThread(IntPtr hThread);
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CreateProcess(
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, PROCESS_CREATION_FLAGS dwCreationFlags,
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern uint SuspendThread(IntPtr hThread);
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
class ProcessInfo : IComparable<ProcessInfo>
{
public Process TheProcess;
public ProcessInfo Parent;
public List<ProcessInfo> Children = new List<ProcessInfo>();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InitializeProcThreadAttributeList(
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
public ProcessInfo(Process the_process)
{
TheProcess = the_process;
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
public override string ToString()
{
return string.Format("{0} [{1}]",
TheProcess.ProcessName, TheProcess.Id);
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
public int CompareTo(ProcessInfo other)
{
return TheProcess.ProcessName.CompareTo(
other.TheProcess.ProcessName);
}
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint ResumeThread(IntPtr hThread);
static class ProcessUtils
{
private static Dictionary<int, ProcessInfo> allProcessInfosDict;
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private static IntPtr ThreadHandle = IntPtr.Zero;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint SuspendThread(IntPtr hThread);
public static void Initialise()
{
allProcessInfosDict = new Dictionary<int, ProcessInfo>();
try
{
// Get the parent/child info.
ManagementObjectSearcher searcher = new ManagementObjectSearcher(
"SELECT ProcessId, ParentProcessId FROM Win32_Process");
ManagementObjectCollection collection = searcher.Get();
// Get the processes.
foreach (Process process in Process.GetProcesses())
{
allProcessInfosDict.Add(process.Id, new ProcessInfo(process));
}
// Create the child lists.
foreach (var item in collection)
{
// Find the parent and child in the dictionary.
int child_id = Convert.ToInt32(item["ProcessId"]);
int parent_id = Convert.ToInt32(item["ParentProcessId"]);
ProcessInfo child_info = null;
ProcessInfo parent_info = null;
if (allProcessInfosDict.ContainsKey(child_id))
child_info = allProcessInfosDict[child_id];
if (allProcessInfosDict.ContainsKey(parent_id))
parent_info = allProcessInfosDict[parent_id];
if (child_info == null)
Console.WriteLine(
"Cannot find child " + child_id.ToString() +
" for parent " + parent_id.ToString());
if (parent_info == null)
Console.WriteLine(
"Cannot find parent " + parent_id.ToString() +
" for child " + child_id.ToString());
if ((child_info != null) && (parent_info != null))
{
parent_info.Children.Add(child_info);
child_info.Parent = parent_info;
}
}
}
catch(Exception ex)
{
logger.Error(ex,$"ProcessUtils/Initialise: Exception (re)initialising the process information to figure out process hierarchy");
}
}
public static List<Process> FindChildProcesses(Process parentProcess)
{
List<Process> childProcesses = new List<Process>() { };
try
{
int parentId = parentProcess.Id;
// TODO: We *possibly* could walk the tree to find the program hierarchy, to get the full list of the
// process tree, but this seems like the best way at this stage. I'm expecting I'll find an edge case in the future
// that requires some sort of modification, but this is working well in 2 days of testing so far!
if (allProcessInfosDict.ContainsKey(parentId))
{
foreach (ProcessInfo childProcess in allProcessInfosDict[parentId].Children)
{
childProcesses.Add(childProcess.TheProcess);
}
}
}
catch (Exception ex)
{
logger.Error(ex, $"ProcessUtils/FindChildProcesses: Exception finding the child processes of the parentProcess");
}
return childProcesses;
}
public static bool StopProcess(Process processToStop)
{
try
{
// Stop the process
processToStop.CloseMainWindow();
if (!processToStop.WaitForExit(5000))
{
logger.Trace($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} wouldn't stop cleanly. Forcing program close.");
processToStop.Kill();
if (!processToStop.WaitForExit(5000))
{
logger.Error($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} couldn't be killed! It seems like something is actively preventing us from stopping the process");
return false;
}
logger.Trace($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} was successfully killed.");
}
processToStop.Close();
return true;
}
catch (Win32Exception ex)
{
logger.Warn(ex, $"ProcessUtils/StopProcess: Win32Exception! Couldn't access the wait status for a named process we're trying to stop. So now just killing the process.");
processToStop.Kill();
if (!processToStop.WaitForExit(5000))
{
logger.Error($"ProcessUtils/StopProcess: Win32Exception! Process {processToStop.StartInfo.FileName} couldn't be killed! It seems like something is actively preventing us from stopping the process");
return false;
}
logger.Trace($"ProcessUtils/StopProcess: Win32Exception! Process {processToStop.StartInfo.FileName} was successfully killed.");
processToStop.Close();
return true;
}
catch (InvalidOperationException ex)
{
logger.Error(ex, $"ProcessUtils/StopProcess: Couldn't kill the named process as the process appears to have closed already.");
}
catch (SystemException ex)
{
logger.Error(ex, $"ProcessUtils/StopProcess: Couldn't WaitForExit the named process as there is no process associated with the Process object (or cannot get the ID from the named process handle).");
}
catch (AggregateException ae)
{
logger.Error(ae, $"ProcessUtils/StopProcess: Got an AggregateException.");
}
return false;
}
public static ProcessPriorityClass TranslatePriorityToClass(ProcessPriority processPriorityClass)
public static ProcessPriorityClass TranslatePriorityToClass(ProcessPriority processPriorityClass)
{
ProcessPriorityClass wantedPriorityClass = ProcessPriorityClass.Normal;
switch (processPriorityClass)
@ -274,49 +145,137 @@ namespace DisplayMagician
return wantedPriorityClass;
}
public static ProcessCreationFlags TranslatePriorityClassToFlags(ProcessPriorityClass processPriorityClass)
public static PROCESS_CREATION_FLAGS TranslatePriorityClassToFlags(ProcessPriorityClass processPriorityClass)
{
ProcessCreationFlags wantedPriorityClass = ProcessCreationFlags.NORMAL_PRIORITY_CLASS;
PROCESS_CREATION_FLAGS wantedPriorityClass = PROCESS_CREATION_FLAGS.NORMAL_PRIORITY_CLASS;
switch (processPriorityClass)
{
case ProcessPriorityClass.High:
wantedPriorityClass = ProcessCreationFlags.HIGH_PRIORITY_CLASS;
wantedPriorityClass = PROCESS_CREATION_FLAGS.HIGH_PRIORITY_CLASS;
break;
case ProcessPriorityClass.AboveNormal:
wantedPriorityClass = ProcessCreationFlags.ABOVE_NORMAL_PRIORITY_CLASS;
wantedPriorityClass = PROCESS_CREATION_FLAGS.ABOVE_NORMAL_PRIORITY_CLASS;
break;
case ProcessPriorityClass.Normal:
wantedPriorityClass = ProcessCreationFlags.NORMAL_PRIORITY_CLASS;
wantedPriorityClass = PROCESS_CREATION_FLAGS.NORMAL_PRIORITY_CLASS;
break;
case ProcessPriorityClass.BelowNormal:
wantedPriorityClass = ProcessCreationFlags.BELOW_NORMAL_PRIORITY_CLASS;
wantedPriorityClass = PROCESS_CREATION_FLAGS.BELOW_NORMAL_PRIORITY_CLASS;
break;
case ProcessPriorityClass.Idle:
wantedPriorityClass = ProcessCreationFlags.IDLE_PRIORITY_CLASS;
wantedPriorityClass = PROCESS_CREATION_FLAGS.IDLE_PRIORITY_CLASS;
break;
default:
wantedPriorityClass = ProcessCreationFlags.NORMAL_PRIORITY_CLASS;
wantedPriorityClass = PROCESS_CREATION_FLAGS.NORMAL_PRIORITY_CLASS;
break;
}
return wantedPriorityClass;
}
public static bool LaunchProcessWithPriority(string exeName, string cmdLine, ProcessPriorityClass priorityClass, out uint PID)
public static bool CreateProcessWithPriority(string exeName, string cmdLine, ProcessPriorityClass priorityClass, out PROCESS_INFORMATION processInfo)
{
ProcessCreationFlags processFlags = TranslatePriorityClassToFlags(priorityClass);
PROCESS_CREATION_FLAGS processFlags = TranslatePriorityClassToFlags(priorityClass) | PROCESS_CREATION_FLAGS.CREATE_SUSPENDED;
bool success = false;
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
try
{
success = CreateProcess(exeName, cmdLine, ref pSec, ref tSec, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo);
}
catch (Exception ex)
{
// This is
}
processInfo = pInfo;
STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
bool success = NativeMethods.CreateProcess(exeName, cmdLine, IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref si, out pi);
ThreadHandle = pi.hThread;
PID = pi.dwProcessId;
return success;
return true;
}
public static void ResumeProcess()
public static void ResumeProcess(IntPtr threadHandle)
{
NativeMethods.ResumeThread(ThreadHandle);
ResumeThread(threadHandle);
}
public static bool CreateProcessWithParent(int parentProcessId)
{
const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
var pInfo = new PROCESS_INFORMATION();
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
IntPtr lpValue = IntPtr.Zero;
try
{
if (parentProcessId > 0)
{
var lpSize = IntPtr.Zero;
var success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
if (success || lpSize == IntPtr.Zero)
{
return false;
}
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
success = InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize);
if (!success)
{
return false;
}
var parentHandle = Process.GetProcessById(parentProcessId).Handle;
// This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(lpValue, parentHandle);
success = UpdateProcThreadAttribute(
sInfoEx.lpAttributeList,
0,
(IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
lpValue,
(IntPtr)IntPtr.Size,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
return false;
}
}
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
var lpApplicationName = Path.Combine(Environment.SystemDirectory, "notepad.exe");
return CreateProcess(lpApplicationName, null, ref pSec, ref tSec, false, PROCESS_CREATION_FLAGS.EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
}
finally
{
// Free the attribute list
if (sInfoEx.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
}
Marshal.FreeHGlobal(lpValue);
// Close process and thread handles
if (pInfo.hProcess != IntPtr.Zero)
{
CloseHandle(pInfo.hProcess);
}
if (pInfo.hThread != IntPtr.Zero)
{
CloseHandle(pInfo.hThread);
}
}
}
}
}

View File

@ -26,8 +26,8 @@ using System.Resources;
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
// Version information
[assembly: AssemblyVersion("2.1.0.160")]
[assembly: AssemblyFileVersion("2.1.0.160")]
[assembly: AssemblyVersion("2.1.0.164")]
[assembly: AssemblyFileVersion("2.1.0.164")]
[assembly: NeutralResourcesLanguageAttribute( "en" )]
[assembly: CLSCompliant(true)]

View File

@ -895,6 +895,7 @@ namespace DisplayMagician
Process process = null;
try
{
ProcessUtils.ScanProcesses();
uint processID = 0;
if (ProcessUtils.LaunchProcessWithPriority(processToStart.Executable, processToStart.Arguments, ProcessUtils.TranslatePriorityToClass(processToStart.ProcessPriority), out processID))
{
@ -1810,7 +1811,7 @@ namespace DisplayMagician
logger.Debug($"ShortcutRepository/RunShortcut: We started {startProgramsToStart.Count} programs before the main executable or game, and now we want to stop {startProgramsToStop.Count } of them");
// Prepare the processInfos we need for finding child processes.
ProcessUtils.Initialise();
ProcessUtils.ScanProcesses();
// Stop the programs in the reverse order we started them
foreach (Process processToStop in startProgramsToStop.Reverse<Process>())

View File

@ -1375,7 +1375,11 @@ namespace DisplayMagician.UIForms
txt_alternative_executable.Text = _shortcutToEdit.DifferentExecutableToMonitor;
// Set the shortcut name
txt_shortcut_save_name.Text = _shortcutToEdit.Name;
if (_editingExistingShortcut)
{
txt_shortcut_save_name.Text = _shortcutToEdit.Name;
}
// Set the selected image and available images (originalBitmap is set during shortcut update)