From c06ba48923ffa0ebd792f43fe1c0b602cfa4721e Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Sat, 6 Nov 2021 21:59:38 +1300 Subject: [PATCH] Partial fix for process start CreateProcessWithPriority doesn't always work. Need to troubleshoot why, but at least have a workaround currently. --- DisplayMagician/GameLibraries/GOGLibrary.cs | 6 +- DisplayMagician/ProcessUtils.cs | 32 ++++++-- DisplayMagician/Properties/AssemblyInfo.cs | 4 +- DisplayMagician/ShortcutRepository.cs | 89 +++++++++++++++------ 4 files changed, 97 insertions(+), 34 deletions(-) diff --git a/DisplayMagician/GameLibraries/GOGLibrary.cs b/DisplayMagician/GameLibraries/GOGLibrary.cs index 2269732..12fb8e6 100644 --- a/DisplayMagician/GameLibraries/GOGLibrary.cs +++ b/DisplayMagician/GameLibraries/GOGLibrary.cs @@ -560,10 +560,10 @@ namespace DisplayMagician.GameLibraries args += gameArguments; } Process gameProcess = null; - uint processID = 0; - if (ProcessUtils.LaunchProcessWithPriority(_gogExe, args, processPriority, out processID)) + ProcessUtils.PROCESS_INFORMATION processInfo; + if (ProcessUtils.CreateProcessWithPriority(_gogExe, args, processPriority, out processInfo)) { - gameProcess = Process.GetProcessById((int)processID); + gameProcess = Process.GetProcessById(processInfo.dwProcessId); } return gameProcess; } diff --git a/DisplayMagician/ProcessUtils.cs b/DisplayMagician/ProcessUtils.cs index 22b67e8..6a09a55 100644 --- a/DisplayMagician/ProcessUtils.cs +++ b/DisplayMagician/ProcessUtils.cs @@ -2,10 +2,11 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Threading.Tasks; namespace DisplayMagician { - public class ProcessCreator + public class ProcessUtils { [Flags] @@ -94,6 +95,14 @@ namespace DisplayMagician IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CreateProcess( + string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, + IntPtr lpThreadAttributes, bool bInheritHandles, PROCESS_CREATION_FLAGS dwCreationFlags, + IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, + out PROCESS_INFORMATION lpProcessInformation); + [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UpdateProcThreadAttribute( @@ -174,7 +183,7 @@ namespace DisplayMagician public static bool CreateProcessWithPriority(string exeName, string cmdLine, ProcessPriorityClass priorityClass, out PROCESS_INFORMATION processInfo) { - PROCESS_CREATION_FLAGS processFlags = TranslatePriorityClassToFlags(priorityClass) | PROCESS_CREATION_FLAGS.CREATE_SUSPENDED; + PROCESS_CREATION_FLAGS processFlags = TranslatePriorityClassToFlags(priorityClass); bool success = false; PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION(); var pSec = new SECURITY_ATTRIBUTES(); @@ -189,16 +198,27 @@ namespace DisplayMagician } catch (Exception ex) { - // This is + // This is a problem + } + if (!success) + { + try + { + success = CreateProcess(exeName, cmdLine, IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo); + } + catch (Exception ex) + { + // This is a problem too + } } processInfo = pInfo; - return true; + return success; } - public static void ResumeProcess(IntPtr threadHandle) + public static void ResumeProcess(PROCESS_INFORMATION processInfo) { - ResumeThread(threadHandle); + ResumeThread(processInfo.hThread); } public static bool CreateProcessWithParent(int parentProcessId) diff --git a/DisplayMagician/Properties/AssemblyInfo.cs b/DisplayMagician/Properties/AssemblyInfo.cs index 23eaf15..e4457d3 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.164")] -[assembly: AssemblyFileVersion("2.1.0.164")] +[assembly: AssemblyVersion("2.1.0.174")] +[assembly: AssemblyFileVersion("2.1.0.174")] [assembly: NeutralResourcesLanguageAttribute( "en" )] [assembly: CLSCompliant(true)] diff --git a/DisplayMagician/ShortcutRepository.cs b/DisplayMagician/ShortcutRepository.cs index b392aab..3da3cfa 100644 --- a/DisplayMagician/ShortcutRepository.cs +++ b/DisplayMagician/ShortcutRepository.cs @@ -891,17 +891,42 @@ namespace DisplayMagician } // Start the executable - logger.Info($"ShortcutRepository/RunShortcut: Starting process {processToStart.Executable}"); + logger.Info($"ShortcutRepository/RunShortcut: Starting Start Program process {processToStart.Executable}"); Process process = null; try { - ProcessUtils.ScanProcesses(); - uint processID = 0; - if (ProcessUtils.LaunchProcessWithPriority(processToStart.Executable, processToStart.Arguments, ProcessUtils.TranslatePriorityToClass(processToStart.ProcessPriority), out processID)) + //ProcessUtils.ScanProcesses(); + ProcessUtils.PROCESS_INFORMATION processInfo; + if (ProcessUtils.CreateProcessWithPriority(processToStart.Executable, processToStart.Arguments, ProcessUtils.TranslatePriorityToClass(processToStart.ProcessPriority), out processInfo)) { - process = Process.GetProcessById((int)processID); + if (processInfo.dwProcessId > 0) + { + process = Process.GetProcessById(processInfo.dwProcessId); + } + else + { + logger.Warn($"ShortcutRepository/RunShortcut: CreateProcessWithPriority returned a process with PID 0 when trying to start process {processToStart.Executable}. This indicates that the process was not started."); + } } - + else + { + ProcessStartInfo psi = new ProcessStartInfo(); + psi.FileName = processToStart.Executable; + psi.Arguments = processToStart.Arguments; + psi.WorkingDirectory = Path.GetDirectoryName(processToStart.Executable); + process = Process.Start(psi); + processInfo.hProcess = process.Handle; + processInfo.dwProcessId = process.Id; + processInfo.dwThreadId = process.Threads[0].Id; + //pInfo.dwThreadId = process.Threads[0].Id; + Task.Delay(500); + if (!process.HasExited) + { + process.PriorityClass = ProcessUtils.TranslatePriorityToClass(processToStart.ProcessPriority); + } + + } + /*if (processToStart.ExecutableArgumentsRequired) { process = System.Diagnostics.Process.Start(processToStart.Executable, processToStart.Arguments); @@ -922,7 +947,7 @@ namespace DisplayMagician { logger.Warn(ex, $"ShortcutRepository/RunShortcut: Exception setting the start program process priority of start program we started to {shortcutToUse.ProcessPriority.ToString("G")}"); }*/ - + // Record the program we started so we can close it later if (processToStart.CloseOnFinish) @@ -1027,18 +1052,35 @@ namespace DisplayMagician try { Process process = null; - /*if (shortcutToUse.ExecutableArgumentsRequired) + ProcessUtils.PROCESS_INFORMATION processInfo; + if (ProcessUtils.CreateProcessWithPriority(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments, ProcessUtils.TranslatePriorityToClass(shortcutToUse.ProcessPriority), out processInfo)) { - process = System.Diagnostics.Process.Start(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments); + process = Process.GetProcessById(processInfo.dwProcessId); + Task.Delay(500); + if (process != null) + { + if (process.HasExited) + { + // Then we need to find what processes are running now with a parent of processInfo.process + logger.Error($"ShortcutRepository/RunShortcut: Main executable process {shortcutToUse.ExecutableNameAndPath} has exited after a short delay. It is likely to be a launcher process. We're going to look for it's children."); + } + } + else + { + logger.Error($"ShortcutRepository/RunShortcut: Main executable process {shortcutToUse.ExecutableNameAndPath} didn't start for some reason. Process still = null."); + } } else { - process = System.Diagnostics.Process.Start(shortcutToUse.ExecutableNameAndPath); - }*/ - uint processID = 0; - if (ProcessUtils.LaunchProcessWithPriority(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments, ProcessUtils.TranslatePriorityToClass(shortcutToUse.ProcessPriority), out processID)) - { - process = Process.GetProcessById((int)processID); + logger.Error($"ShortcutRepository/RunShortcut: CreateProcessWithPriority couldn't create Main executable process {shortcutToUse.ExecutableNameAndPath}. Going to try to start it the old way without priority."); + if (shortcutToUse.ExecutableArgumentsRequired) + { + process = Process.Start(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments); + } + else + { + process = Process.Start(shortcutToUse.ExecutableNameAndPath); + } } } @@ -1811,7 +1853,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.ScanProcesses(); + //ProcessUtils.ScanProcesses(); // Stop the programs in the reverse order we started them foreach (Process processToStop in startProgramsToStop.Reverse()) @@ -1823,11 +1865,11 @@ namespace DisplayMagician if (!processToStop.HasExited) { logger.Debug($"ShortcutRepository/RunShortcut: Stopping process {processToStop.StartInfo.FileName}"); - if (ProcessUtils.StopProcess(processToStop)) + /*if (ProcessUtils.StopProcess(processToStop)) { logger.Debug($"ShortcutRepository/RunShortcut: Successfully stopped process {processToStop.StartInfo.FileName}"); stoppedMainProcess = true; - } + }*/ } } catch (Exception ex) @@ -1838,7 +1880,7 @@ namespace DisplayMagician // Next, check whether it had any other processes it started itself // (copes with loader processes that perform the initial start, then run the main exe) // If so, we need to go through and find and close all subprocesses - try + /*try { List childProcesses = ProcessUtils.FindChildProcesses(processToStop); if (childProcesses.Count > 0) @@ -1863,7 +1905,7 @@ namespace DisplayMagician catch (Exception ex) { logger.Error(ex, $"ShortcutRepository/RunShortcut: Exception while checking if processToStop has any child processes"); - } + }*/ // if the only main process has already exited (e.g. the user exited it themselves) // then we try to stop any processes with the same name as the application we started @@ -1879,11 +1921,11 @@ namespace DisplayMagician // If we have found one or more processes then we should be good to go if (namedProcessesToStop.Count > 0) { - logger.Warn($"ShortcutRepository/RunShortcut: We couldn't find any children processes so we've looked for named processes with the name '{processToStop.StartInfo.FileName}' and we found {namedProcessesToStop.Count}. Closing them."); + /*logger.Warn($"ShortcutRepository/RunShortcut: We couldn't find any children processes so we've looked for named processes with the name '{processToStop.StartInfo.FileName}' and we found {namedProcessesToStop.Count}. Closing them."); foreach (Process namedProcessToStop in namedProcessesToStop) { ProcessUtils.StopProcess(namedProcessToStop); - } + }*/ } else { @@ -2001,7 +2043,8 @@ namespace DisplayMagician uint processID = 0; try { - if (ProcessUtils.LaunchProcessWithPriority(stopProg.Executable, stopProg.Arguments, ProcessUtils.TranslatePriorityToClass(stopProg.ProcessPriority), out processID)) + ProcessUtils.PROCESS_INFORMATION processInfo; + if (ProcessUtils.CreateProcessWithPriority(stopProg.Executable, stopProg.Arguments, ProcessUtils.TranslatePriorityToClass(stopProg.ProcessPriority), out processInfo)) { logger.Trace($"ShortcutRepository/RunShortcut: Successfully started Stop Program {stopProg.Executable} {stopProg.Arguments}"); }