Created
November 7, 2024 12:47
-
-
Save davepermen/7047b61c5312ec764b9972011e1b832e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
namespace ProcessTracking; | |
// From https://stackoverflow.com/a/37034966/1528847 | |
class ProcessTracker(bool closeOnExit = true) | |
{ | |
ChildProcessTracker? current = closeOnExit ? new() : null; | |
readonly HashSet<Process> tracked = []; | |
public void Track(Process p) | |
{ | |
tracked.Add(p); | |
current?.Track(p); | |
} | |
public bool CloseTrackedOnExit | |
{ | |
get => current != null; | |
set | |
{ | |
if (current != null && value == false) | |
{ | |
current.DeactivateTracking(); | |
current.Dispose(); | |
current = null; | |
} | |
if (current == null && value == true) | |
{ | |
current = new(); | |
foreach (var p in tracked) | |
{ | |
current.Track(p); | |
} | |
} | |
} | |
} | |
} | |
class ChildProcessTracker : IDisposable | |
{ | |
private readonly nint s_jobHandle; | |
public ChildProcessTracker() | |
{ | |
if (Environment.OSVersion.Version < new Version(6, 2)) | |
return; | |
s_jobHandle = CreateJobObject(nint.Zero, "ChildProcessTracker" + Process.GetCurrentProcess().Id); | |
SetTracking(true); | |
} | |
public Process? Track(Process? process) | |
{ | |
if (s_jobHandle != nint.Zero && process != null) | |
{ | |
if (!AssignProcessToJobObject(s_jobHandle, process.Handle) && !process.HasExited) | |
throw new Win32Exception(); | |
} | |
return process; | |
} | |
private void SetTracking(bool tracking) | |
{ | |
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION | |
{ | |
BasicLimitInformation = new() | |
{ | |
LimitFlags = tracking ? JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE : 0 | |
} | |
}; | |
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); | |
nint extendedInfoPtr = Marshal.AllocHGlobal(length); | |
try | |
{ | |
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); | |
if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) | |
{ | |
throw new Win32Exception(); | |
} | |
} | |
finally | |
{ | |
Marshal.FreeHGlobal(extendedInfoPtr); | |
} | |
} | |
public void DeactivateTracking() => SetTracking(false); | |
public void Dispose() => CloseHandle(s_jobHandle); | |
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] | |
static extern nint CreateJobObject(nint lpJobAttributes, string name); | |
[DllImport("kernel32.dll")] | |
static extern bool SetInformationJobObject(nint job, JobObjectInfoType infoType, | |
nint lpJobObjectInfo, uint cbJobObjectInfoLength); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool AssignProcessToJobObject(nint job, nint process); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool CloseHandle(nint handle); | |
enum JobObjectInfoType | |
{ | |
AssociateCompletionPortInformation = 7, | |
BasicLimitInformation = 2, | |
BasicUIRestrictions = 4, | |
EndOfJobTimeInformation = 6, | |
ExtendedLimitInformation = 9, | |
SecurityLimitInformation = 5, | |
GroupInformation = 11 | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
struct JOBOBJECT_BASIC_LIMIT_INFORMATION | |
{ | |
public long PerProcessUserTimeLimit; | |
public long PerJobUserTimeLimit; | |
public JOBOBJECTLIMIT LimitFlags; | |
public nuint MinimumWorkingSetSize; | |
public nuint MaximumWorkingSetSize; | |
public uint ActiveProcessLimit; | |
public long Affinity; | |
public uint PriorityClass; | |
public uint SchedulingClass; | |
} | |
[Flags] | |
enum JOBOBJECTLIMIT : uint | |
{ | |
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000 | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
struct IO_COUNTERS | |
{ | |
public ulong ReadOperationCount; | |
public ulong WriteOperationCount; | |
public ulong OtherOperationCount; | |
public ulong ReadTransferCount; | |
public ulong WriteTransferCount; | |
public ulong OtherTransferCount; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION | |
{ | |
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; | |
public IO_COUNTERS IoInfo; | |
public nuint ProcessMemoryLimit; | |
public nuint JobMemoryLimit; | |
public nuint PeakProcessMemoryUsed; | |
public nuint PeakJobMemoryUsed; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment