mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
Added stronger start program closing
Any start program that is started then tries to doubly make sure that the program is also closed. Now checks for something to close in the following order: - tries to close the process it opened - if that was closed it tries to find any child processes started by the original process in case it was a 'launcher' process - If there are no children with the same parent process then it just tries to close all processes with the same name as the one it opened. That final close also is a forced kill, as I've found programs like SimHub resist closing....
This commit is contained in:
parent
abb2a3b905
commit
e31bbbd4aa
@ -116,6 +116,7 @@
|
||||
<Compile Include="IconFromFile.cs" />
|
||||
<Compile Include="IconUtils.cs" />
|
||||
<Compile Include="ImageUtils.cs" />
|
||||
<Compile Include="ProcessUtils.cs" />
|
||||
<Compile Include="ProgramSettings.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
|
122
DisplayMagician/ProcessUtils.cs
Normal file
122
DisplayMagician/ProcessUtils.cs
Normal file
@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DisplayMagician
|
||||
{
|
||||
|
||||
class ProcessInfo : IComparable<ProcessInfo>
|
||||
{
|
||||
public Process TheProcess;
|
||||
public ProcessInfo Parent;
|
||||
public List<ProcessInfo> Children = new List<ProcessInfo>();
|
||||
|
||||
public ProcessInfo(Process the_process)
|
||||
{
|
||||
TheProcess = the_process;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} [{1}]",
|
||||
TheProcess.ProcessName, TheProcess.Id);
|
||||
}
|
||||
|
||||
public int CompareTo(ProcessInfo other)
|
||||
{
|
||||
return TheProcess.ProcessName.CompareTo(
|
||||
other.TheProcess.ProcessName);
|
||||
}
|
||||
}
|
||||
|
||||
static class ProcessUtils
|
||||
{
|
||||
private static Dictionary<int, ProcessInfo> allProcessInfosDict;
|
||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1608,16 +1608,123 @@ 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();
|
||||
|
||||
// Stop the programs in the reverse order we started them
|
||||
foreach (Process processToStop in startProgramsToStop.Reverse<Process>())
|
||||
{
|
||||
if (processToStop.HasExited)
|
||||
{
|
||||
// if the process has already exited (e.g. the user exited it themselves)
|
||||
// then stop trying to stop the process, and instead log the fact it already stopped.
|
||||
Console.WriteLine($"Stopping process {processToStop.StartInfo.FileName} but was already stopped by user or another process.");
|
||||
logger.Debug($"ShortcutRepository/RunShortcut: Stopping process {processToStop.StartInfo.FileName} but was already stopped by user or another process.");
|
||||
continue;
|
||||
// First check whether it was a loader process that started a subprocess
|
||||
// If so, we need to go through and find and close all subprocesses
|
||||
List<Process> childProcesses = ProcessUtils.FindChildProcesses(processToStop);
|
||||
if (childProcesses.Count > 0)
|
||||
{
|
||||
foreach (Process childProcessToStop in childProcesses)
|
||||
{
|
||||
if (processToStop.HasExited)
|
||||
{
|
||||
// if there were no child processes, and the only process has already exited (e.g. the user exited it themselves)
|
||||
// then stop trying to stop the process, and instead log the fact it already stopped.
|
||||
Console.WriteLine($"Stopping child process {childProcessToStop.StartInfo.FileName} but was already stopped by user or another process.");
|
||||
logger.Warn($"ShortcutRepository/RunShortcut: Stopping child process {childProcessToStop.StartInfo.FileName} but was already stopped by user or another process.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Stopping child process {childProcessToStop.StartInfo.FileName} of parent process {processToStop.StartInfo.FileName}");
|
||||
logger.Debug($"ShortcutRepository/RunShortcut: Stopping child process {childProcessToStop.StartInfo.FileName} of parent process {processToStop.StartInfo.FileName}");
|
||||
try
|
||||
{
|
||||
// Stop the program
|
||||
childProcessToStop.CloseMainWindow();
|
||||
childProcessToStop.WaitForExit(5000);
|
||||
if (!childProcessToStop.HasExited)
|
||||
{
|
||||
Console.WriteLine($"- Process {childProcessToStop.StartInfo.FileName} wouldn't stop cleanly. Forcing program close.");
|
||||
logger.Warn($"ShortcutRepository/RunShortcut: Process {childProcessToStop.StartInfo.FileName} wouldn't stop cleanly. Forcing program close.");
|
||||
childProcessToStop.Kill();
|
||||
childProcessToStop.WaitForExit(5000);
|
||||
}
|
||||
childProcessToStop.Close();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't access the wait status for a child process we're trying to stop.");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't kill the child process as the child process appears to have closed already. This can be caused if your {childProcessToStop.StartInfo.FileName} loaded another exe then closed itself. DisplayMagician cannot track that sort of behaviour.");
|
||||
}
|
||||
catch (SystemException ex)
|
||||
{
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't WaitForExit the child process as there is no child process associated with the Process object (or cannot get the ID from the process handle).");
|
||||
}
|
||||
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
logger.Error(ae, $"ShortcutRepository/RunShortcut: Got an AggregateException.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there were no child processes found, and the only 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
|
||||
// Look for the processes with the ProcessName we sorted out earlier
|
||||
string processName = Path.GetFileNameWithoutExtension(processToStop.StartInfo.FileName);
|
||||
List<Process> namedProcessesToStop = Process.GetProcessesByName(processName).ToList();
|
||||
|
||||
// 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.");
|
||||
foreach (Process namedProcessToStop in namedProcessesToStop)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Stop the named process
|
||||
namedProcessToStop.CloseMainWindow();
|
||||
namedProcessToStop.WaitForExit(5000);
|
||||
namedProcessToStop.Kill();
|
||||
/*if (!namedProcessToStop.HasExited)
|
||||
{
|
||||
logger.Warn($"ShortcutRepository/RunShortcut: Named process {namedProcessToStop.StartInfo.FileName} wouldn't stop cleanly. Forcing program close.");
|
||||
namedProcessToStop.Kill();
|
||||
namedProcessToStop.WaitForExit(5000);
|
||||
}*/
|
||||
namedProcessToStop.Close();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't access the wait status for a named process we're trying to stop.");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't kill the named process as the process appears to have closed already.");
|
||||
}
|
||||
catch (SystemException ex)
|
||||
{
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: 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, $"ShortcutRepository/RunShortcut: Got an AggregateException.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// then stop trying to stop the process, and instead log the fact it already stopped.
|
||||
Console.WriteLine($"Stopping only process {processToStop.StartInfo.FileName} but was already stopped by user or another process.");
|
||||
logger.Debug($"ShortcutRepository/RunShortcut: Stopping only process {processToStop.StartInfo.FileName} but was already stopped by user or another process.");
|
||||
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
Console.WriteLine($"Stopping process {processToStop.StartInfo.FileName}");
|
||||
logger.Debug($"ShortcutRepository/RunShortcut: Stopping process {processToStop.StartInfo.FileName}");
|
||||
@ -1639,7 +1746,7 @@ namespace DisplayMagician
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't access the wait status for a process we're trying to stop.");
|
||||
}
|
||||
catch (InvalidOperationException ex) {
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't kill the process as the proicess appears to have closed already. This can be caused if your {processToStop.ProcessName}.exe loaded another exe then closed itself. DisplayMagician cannot track that sort of behaviour.");
|
||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Couldn't kill the process as the proicess appears to have closed already. This can be caused if your {processToStop.StartInfo.FileName} loaded another exe then closed itself. DisplayMagician cannot track that sort of behaviour.");
|
||||
}
|
||||
catch (SystemException ex)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user