Loading TestV/Playgrounds/SubProgram.cs 0 → 100644 +131 −0 Original line number Diff line number Diff line using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; namespace TestV.Playgrounds; public class SubProgram : IPlayground { public string LaunchCmd { get; } = "subp"; public string? Name { get; } = "sub program"; public string? Description { get; } = "Create sub program."; // Job Object 句柄,生命周期应跟随父进程 private static IntPtr _jobHandle; private static readonly object _lock = new(); public async Task Run(string[] args) { EnsureJobObject(); var psi = new ProcessStartInfo("worker.exe") { UseShellExecute = false, // 必须为 false 才能获取进程句柄 }; using var process = Process.Start(psi); if (process == null) throw new InvalidOperationException("Failed to start worker.exe"); // 将子进程分配到 Job Object if (!AssignProcessToJobObject(_jobHandle, process.Handle)) { var error = Marshal.GetLastWin32Error(); process.Kill(); throw new Win32Exception(error, "AssignProcessToJobObject failed"); } // 注意:不要在这里 Dispose process,否则句柄关闭后 Job 关联可能失效 // 如果需要等待子进程结束,可以 await process.WaitForExitAsync() } private static void EnsureJobObject() { if (_jobHandle != IntPtr.Zero) return; lock (_lock) { if (_jobHandle != IntPtr.Zero) return; // 创建 Job Object,设置 KILL_ON_JOB_CLOSE 标志 _jobHandle = CreateJobObject(IntPtr.Zero, null); if (_jobHandle == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); var info = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION { BasicLimitInformation = new JOBOBJECT_BASIC_LIMIT_INFORMATION { LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE } }; if (!SetInformationJobObject( _jobHandle, JobObjectExtendedLimitInformation, ref info, Marshal.SizeOf<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>())) { CloseHandle(_jobHandle); _jobHandle = IntPtr.Zero; throw new Win32Exception(Marshal.GetLastWin32Error()); } } } #region P/Invoke private const uint JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000; private const int JobObjectExtendedLimitInformation = 9; [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string? name); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetInformationJobObject( IntPtr hJob, int infoClass, ref JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo, int cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); [StructLayout(LayoutKind.Sequential)] private struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public long PerProcessUserTimeLimit; public long PerJobUserTimeLimit; public uint LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public uint ActiveProcessLimit; public UIntPtr Affinity; public uint PriorityClass; public uint SchedulingClass; } [StructLayout(LayoutKind.Sequential)] private struct IO_COUNTERS { public ulong ReadOperationCount; public ulong WriteOperationCount; public ulong OtherOperationCount; public ulong ReadTransferCount; public ulong WriteTransferCount; public ulong OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] private struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public IO_COUNTERS IoInfo; public UIntPtr ProcessMemoryLimit; public UIntPtr JobMemoryLimit; public UIntPtr PeakProcessMemoryUsed; public UIntPtr PeakJobMemoryUsed; } #endregion } No newline at end of file TestV/TestV.csproj +4 −0 Original line number Diff line number Diff line Loading @@ -43,4 +43,8 @@ </None> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Worker\Worker.csproj" /> </ItemGroup> </Project> Worker/obj/Debug/net10.0/Worker.csproj.CoreCompileInputs.cache 0 → 100644 +1 −0 Original line number Diff line number Diff line d4f9ca4888caa2bfb80e0513552d203ddb461e3d36d30352663c8a7b6898ceb5 Loading
TestV/Playgrounds/SubProgram.cs 0 → 100644 +131 −0 Original line number Diff line number Diff line using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; namespace TestV.Playgrounds; public class SubProgram : IPlayground { public string LaunchCmd { get; } = "subp"; public string? Name { get; } = "sub program"; public string? Description { get; } = "Create sub program."; // Job Object 句柄,生命周期应跟随父进程 private static IntPtr _jobHandle; private static readonly object _lock = new(); public async Task Run(string[] args) { EnsureJobObject(); var psi = new ProcessStartInfo("worker.exe") { UseShellExecute = false, // 必须为 false 才能获取进程句柄 }; using var process = Process.Start(psi); if (process == null) throw new InvalidOperationException("Failed to start worker.exe"); // 将子进程分配到 Job Object if (!AssignProcessToJobObject(_jobHandle, process.Handle)) { var error = Marshal.GetLastWin32Error(); process.Kill(); throw new Win32Exception(error, "AssignProcessToJobObject failed"); } // 注意:不要在这里 Dispose process,否则句柄关闭后 Job 关联可能失效 // 如果需要等待子进程结束,可以 await process.WaitForExitAsync() } private static void EnsureJobObject() { if (_jobHandle != IntPtr.Zero) return; lock (_lock) { if (_jobHandle != IntPtr.Zero) return; // 创建 Job Object,设置 KILL_ON_JOB_CLOSE 标志 _jobHandle = CreateJobObject(IntPtr.Zero, null); if (_jobHandle == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); var info = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION { BasicLimitInformation = new JOBOBJECT_BASIC_LIMIT_INFORMATION { LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE } }; if (!SetInformationJobObject( _jobHandle, JobObjectExtendedLimitInformation, ref info, Marshal.SizeOf<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>())) { CloseHandle(_jobHandle); _jobHandle = IntPtr.Zero; throw new Win32Exception(Marshal.GetLastWin32Error()); } } } #region P/Invoke private const uint JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000; private const int JobObjectExtendedLimitInformation = 9; [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string? name); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetInformationJobObject( IntPtr hJob, int infoClass, ref JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo, int cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); [StructLayout(LayoutKind.Sequential)] private struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public long PerProcessUserTimeLimit; public long PerJobUserTimeLimit; public uint LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public uint ActiveProcessLimit; public UIntPtr Affinity; public uint PriorityClass; public uint SchedulingClass; } [StructLayout(LayoutKind.Sequential)] private struct IO_COUNTERS { public ulong ReadOperationCount; public ulong WriteOperationCount; public ulong OtherOperationCount; public ulong ReadTransferCount; public ulong WriteTransferCount; public ulong OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] private struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public IO_COUNTERS IoInfo; public UIntPtr ProcessMemoryLimit; public UIntPtr JobMemoryLimit; public UIntPtr PeakProcessMemoryUsed; public UIntPtr PeakJobMemoryUsed; } #endregion } No newline at end of file
TestV/TestV.csproj +4 −0 Original line number Diff line number Diff line Loading @@ -43,4 +43,8 @@ </None> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Worker\Worker.csproj" /> </ItemGroup> </Project>
Worker/obj/Debug/net10.0/Worker.csproj.CoreCompileInputs.cache 0 → 100644 +1 −0 Original line number Diff line number Diff line d4f9ca4888caa2bfb80e0513552d203ddb461e3d36d30352663c8a7b6898ceb5