|
| 1 | +using System.Diagnostics; |
1 | 2 | using System.Runtime.InteropServices; |
2 | 3 |
|
3 | 4 | static partial class Program |
@@ -35,6 +36,17 @@ static int Main(string[] args) |
35 | 36 | return 1; |
36 | 37 | } |
37 | 38 |
|
| 39 | + // Create a job object that kills child processes when this process exits |
| 40 | + var job = CreateJobObject(IntPtr.Zero, null); |
| 41 | + var info = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION |
| 42 | + { |
| 43 | + BasicLimitInformation = new JOBOBJECT_BASIC_LIMIT_INFORMATION |
| 44 | + { |
| 45 | + LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE |
| 46 | + } |
| 47 | + }; |
| 48 | + SetInformationJobObject(job, JobObjectExtendedLimitInformation, ref info, (uint)Marshal.SizeOf(info)); |
| 49 | + |
38 | 50 | Console.CancelKeyPress += OnCancelKeyPress; |
39 | 51 | AppDomain.CurrentDomain.ProcessExit += OnProcessExit; |
40 | 52 | SetConsoleCtrlHandler(_consoleCtrlHandler, true); |
@@ -79,16 +91,19 @@ static int Main(string[] args) |
79 | 91 |
|
80 | 92 | word.Visible = true; |
81 | 93 |
|
82 | | - // Get process from Word's window handle |
| 94 | + // Get process from Word's window handle and assign to job |
83 | 95 | var hwnd = (IntPtr)word.ActiveWindow.Hwnd; |
84 | 96 | GetWindowThreadProcessId(hwnd, out var processId); |
85 | 97 | using var process = Process.GetProcessById(processId); |
| 98 | + AssignProcessToJobObject(job, process.Handle); |
| 99 | + |
86 | 100 | process.WaitForExit(); |
87 | 101 |
|
88 | 102 | // Release COM objects |
89 | 103 | Marshal.ReleaseComObject(comparedDoc); |
90 | 104 | Marshal.ReleaseComObject(word); |
91 | 105 | _word = null; |
| 106 | + CloseHandle(job); |
92 | 107 |
|
93 | 108 | return 0; |
94 | 109 | } |
@@ -125,12 +140,66 @@ static void CloseWord() |
125 | 140 | _word = null; |
126 | 141 | } |
127 | 142 |
|
| 143 | + const uint JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000; |
| 144 | + const int JobObjectExtendedLimitInformation = 9; |
| 145 | + |
| 146 | + [StructLayout(LayoutKind.Sequential)] |
| 147 | + struct JOBOBJECT_BASIC_LIMIT_INFORMATION |
| 148 | + { |
| 149 | + public long PerProcessUserTimeLimit; |
| 150 | + public long PerJobUserTimeLimit; |
| 151 | + public uint LimitFlags; |
| 152 | + public nuint MinimumWorkingSetSize; |
| 153 | + public nuint MaximumWorkingSetSize; |
| 154 | + public uint ActiveProcessLimit; |
| 155 | + public nuint Affinity; |
| 156 | + public uint PriorityClass; |
| 157 | + public uint SchedulingClass; |
| 158 | + } |
| 159 | + |
| 160 | + [StructLayout(LayoutKind.Sequential)] |
| 161 | + struct IO_COUNTERS |
| 162 | + { |
| 163 | + public ulong ReadOperationCount; |
| 164 | + public ulong WriteOperationCount; |
| 165 | + public ulong OtherOperationCount; |
| 166 | + public ulong ReadTransferCount; |
| 167 | + public ulong WriteTransferCount; |
| 168 | + public ulong OtherTransferCount; |
| 169 | + } |
| 170 | + |
| 171 | + [StructLayout(LayoutKind.Sequential)] |
| 172 | + struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION |
| 173 | + { |
| 174 | + public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; |
| 175 | + public IO_COUNTERS IoInfo; |
| 176 | + public nuint ProcessMemoryLimit; |
| 177 | + public nuint JobMemoryLimit; |
| 178 | + public nuint PeakProcessMemoryUsed; |
| 179 | + public nuint PeakJobMemoryUsed; |
| 180 | + } |
| 181 | + |
128 | 182 | [LibraryImport("user32.dll")] |
129 | 183 | internal static partial uint GetWindowThreadProcessId(IntPtr hWnd, out int processId); |
130 | 184 |
|
131 | 185 | [LibraryImport("kernel32.dll")] |
132 | 186 | [return: MarshalAs(UnmanagedType.Bool)] |
133 | 187 | private static partial bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, [MarshalAs(UnmanagedType.Bool)] bool add); |
134 | 188 |
|
| 189 | + [LibraryImport("kernel32.dll", EntryPoint = "CreateJobObjectW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] |
| 190 | + private static partial IntPtr CreateJobObject(IntPtr lpJobAttributes, string? lpName); |
| 191 | + |
| 192 | + [LibraryImport("kernel32.dll", SetLastError = true)] |
| 193 | + [return: MarshalAs(UnmanagedType.Bool)] |
| 194 | + private static partial bool SetInformationJobObject(IntPtr hJob, int jobObjectInfoClass, ref JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo, uint cbJobObjectInfoLength); |
| 195 | + |
| 196 | + [LibraryImport("kernel32.dll", SetLastError = true)] |
| 197 | + [return: MarshalAs(UnmanagedType.Bool)] |
| 198 | + private static partial bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess); |
| 199 | + |
| 200 | + [LibraryImport("kernel32.dll", SetLastError = true)] |
| 201 | + [return: MarshalAs(UnmanagedType.Bool)] |
| 202 | + private static partial bool CloseHandle(IntPtr hObject); |
| 203 | + |
135 | 204 | delegate bool ConsoleCtrlDelegate(int ctrlType); |
136 | 205 | } |
0 commit comments