From a80349f31a7ca9f149436929b6fa3fa99253d5b9 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Sat, 6 Nov 2021 18:38:37 +1300 Subject: [PATCH] Initial ProcessUtils update ProcessUtils functions don't work at the moment, so I need to fix them! --- DisplayMagician/ProcessUtils.cs | 461 ++++++++++----------- DisplayMagician/Properties/AssemblyInfo.cs | 4 +- DisplayMagician/ShortcutRepository.cs | 3 +- DisplayMagician/UIForms/ShortcutForm.cs | 6 +- 4 files changed, 219 insertions(+), 255 deletions(-) diff --git a/DisplayMagician/ProcessUtils.cs b/DisplayMagician/ProcessUtils.cs index cb2fa64..22b67e8 100644 --- a/DisplayMagician/ProcessUtils.cs +++ b/DisplayMagician/ProcessUtils.cs @@ -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 - { - public Process TheProcess; - public ProcessInfo Parent; - public List Children = new List(); + [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 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(); - - 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 FindChildProcesses(Process parentProcess) - { - List childProcesses = new List() { }; - - 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); + } + } + } + + } +} + diff --git a/DisplayMagician/Properties/AssemblyInfo.cs b/DisplayMagician/Properties/AssemblyInfo.cs index 120e6a2..23eaf15 100644 --- a/DisplayMagician/Properties/AssemblyInfo.cs +++ b/DisplayMagician/Properties/AssemblyInfo.cs @@ -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)] diff --git a/DisplayMagician/ShortcutRepository.cs b/DisplayMagician/ShortcutRepository.cs index a38581d..b392aab 100644 --- a/DisplayMagician/ShortcutRepository.cs +++ b/DisplayMagician/ShortcutRepository.cs @@ -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()) diff --git a/DisplayMagician/UIForms/ShortcutForm.cs b/DisplayMagician/UIForms/ShortcutForm.cs index afa8a74..b7e3b8c 100644 --- a/DisplayMagician/UIForms/ShortcutForm.cs +++ b/DisplayMagician/UIForms/ShortcutForm.cs @@ -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)