¿Ejemplo de trabajo de CreateJobObject / SetInformationJobObject pinvoke en .net?

Estoy luchando por armar un ejemplo funcional de pinvokear CreateJobObject y SetInformationJobObject. A través de varias búsquedas en Google (incluidas las publicaciones rusas y chinas), he creado el siguiente código. Creo que la definición de JOBOBJECT_BASIC_LIMIT_INFORMATION cambia en función de la plataforma (32/64 bits). El CreateJobObject / AssignProcessToJobObject parece funcionar. SetInformationJobObject falla, ya sea con el error 24 o 87.

Process myProcess // POPULATED SOMEWHERE ELSE // Create Job & assign this process and another process to the job IntPtr jobHandle = CreateJobObject( null , null ); AssignProcessToJobObject( jobHandle , myProcess.Handle ); AssignProcessToJobObject( jobHandle , Process.GetCurrentProcess().Handle ); // Ensure that killing one process kills the others JOBOBJECT_BASIC_LIMIT_INFORMATION limits = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); limits.LimitFlags = (short)LimitFlags.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; IntPtr pointerToJobLimitInfo = Marshal.AllocHGlobal( Marshal.SizeOf( limits ) ); Marshal.StructureToPtr( limits , pointerToJobLimitInfo , false ); SetInformationJobObject( job , JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation , pionterToJobLimitInfo , ( uint )Marshal.SizeOf( limits ) ) ... [DllImport( "kernel32.dll" , EntryPoint = "CreateJobObjectW" , CharSet = CharSet.Unicode )] public static extern IntPtr CreateJobObject( SecurityAttributes JobAttributes , string lpName ); public class SecurityAttributes { public int nLength; //Useless field = 0 public IntPtr pSecurityDescriptor; //хз)) public bool bInheritHandle; //Возможность наследования public SecurityAttributes() { this.bInheritHandle = true; this.nLength = 0; this.pSecurityDescriptor = IntPtr.Zero; } } [DllImport( "kernel32.dll" )] static extern bool SetInformationJobObject( IntPtr hJob , JOBOBJECTINFOCLASS JobObjectInfoClass , IntPtr lpJobObjectInfo , uint cbJobObjectInfoLength ); public enum JOBOBJECTINFOCLASS { JobObjectAssociateCompletionPortInformation = 7 , JobObjectBasicLimitInformation = 2 , JobObjectBasicUIRestrictions = 4 , JobObjectEndOfJobTimeInformation = 6 , JobObjectExtendedLimitInformation = 9 , JobObjectSecurityLimitInformation = 5 } [StructLayout( LayoutKind.Sequential )] struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public Int16 LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public Int16 ActiveProcessLimit; public Int64 Affinity; public Int16 PriorityClass; public Int16 SchedulingClass; } public enum LimitFlags { JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008 , JOB_OBJECT_LIMIT_AFFINITY = 0x00000010 , JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 , JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400 , JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200 , JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004 , JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000 , JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040 , JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x00000020 , JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100 , JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002 , JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x00000080 , JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000 , JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001 } [DllImport( "kernel32.dll" )] [return: MarshalAs( UnmanagedType.Bool )] static extern bool AssignProcessToJobObject( IntPtr hJob , IntPtr hProcess ); [StructLayout( LayoutKind.Sequential )] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } 

Esto puede ser un poco tarde, pero aún así.

Intenté todos los ejemplos aquí, pero nadie estaba trabajando para mí en el modo de 32 y 64 bits simultáneamente. Finalmente, se me requirió examinar todas las firmas yo mismo y crear las correspondientes rutinas de PInvoke. Creo que alguien más podría encontrar esto útil.

Descargo de responsabilidad: la solución se basa en la respuesta de Matt Howells .

 using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace JobManagement { public class Job : IDisposable { [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject(IntPtr a, string lpName); [DllImport("kernel32.dll")] static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr hObject); private IntPtr handle; private bool disposed; public Job() { handle = CreateJobObject(IntPtr.Zero, null); var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION { LimitFlags = 0x2000 }; var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION { BasicLimitInformation = info }; int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) throw new Exception(string.Format("Unable to set information. Error: {0}", Marshal.GetLastWin32Error())); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposed) return; if (disposing) { } Close(); disposed = true; } public void Close() { CloseHandle(handle); handle = IntPtr.Zero; } public bool AddProcess(IntPtr processHandle) { return AssignProcessToJobObject(handle, processHandle); } public bool AddProcess(int processId) { return AddProcess(Process.GetProcessById(processId).Handle); } } #region Helper classes [StructLayout(LayoutKind.Sequential)] struct IO_COUNTERS { public UInt64 ReadOperationCount; public UInt64 WriteOperationCount; public UInt64 OtherOperationCount; public UInt64 ReadTransferCount; public UInt64 WriteTransferCount; public UInt64 OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] struct JOBOBJECT_BASIC_LIMIT_INFORMATION { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; public UInt32 LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public UInt32 ActiveProcessLimit; public UIntPtr Affinity; public UInt32 PriorityClass; public UInt32 SchedulingClass; } [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public UInt32 nLength; public IntPtr lpSecurityDescriptor; public Int32 bInheritHandle; } [StructLayout(LayoutKind.Sequential)] 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; } public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, BasicUIRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, GroupInformation = 11 } #endregion } 

Para resumir, las firmas planteadas por Alexander Yezutov funcionan tanto en x86 como en x64. Las firmas de Matt Howells usan una cantidad de UInt32 cuando UIntPtr se debe usar en su lugar. Usé la siguiente firma P / Invoke para CloseHandle que parece funcionar bien:

 [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr hObject); 

Se debe agregar lo siguiente a la aplicación. Manifiesto como fue publicado por Mas:

         

y, por último, esto no funcionará (al menos en Win 7) cuando se inicia desde Visual Studio. El proceso principal debe iniciarse desde el Explorador de Windows.

Hay una publicación que usa aplicaciones que estás tratando de usar. Quizás puedas obtener alguna información de allí.

Matar proceso hijo cuando se mata el proceso principal

Mejorando la respuesta de Alexander, aquí hay una versión que usa un SafeHandle . Este es un CriticalFinalizerObject que hace que trabajar con controladores sea mucho más seguro. Las API .NET (como la clase Process ) siempre usan SafeHandle s con P / Invoke en lugar de IntPtr s.

 internal sealed class ChildProcessManager : IDisposable { private SafeJobHandle _handle; private bool _disposed; public ChildProcessManager() { _handle = new SafeJobHandle(CreateJobObject(IntPtr.Zero, null)); var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION { LimitFlags = 0x2000 }; var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION { BasicLimitInformation = info }; var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); var extendedInfoPtr = Marshal.AllocHGlobal(length); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(_handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) { throw new InvalidOperationException($"Unable to set information. Error: {Marshal.GetLastWin32Error()}"); } } public void Dispose() { if (_disposed) return; _handle.Dispose(); _handle = null; _disposed = true; } private void ValidateDisposed() { if (_disposed) throw new ObjectDisposedException(nameof(ChildProcessManager)); } public void AddProcess(SafeProcessHandle processHandle) { ValidateDisposed(); if (!AssignProcessToJobObject(_handle, processHandle)) { throw new InvalidOperationException("Unable to add the process"); } } public void AddProcess(Process process) { AddProcess(process.SafeHandle); } public void AddProcess(int processId) { using (var process = Process.GetProcessById(processId)) { AddProcess(process); } } #region Safe Handle // ReSharper disable once ClassNeverInstantiated.Local private sealed class SafeJobHandle : SafeHandleZeroOrMinusOneIsInvalid { public SafeJobHandle(IntPtr handle) : base(true) { SetHandle(handle); } protected override bool ReleaseHandle() { return CloseHandle(handle); } [DllImport("kernel32", SetLastError = true)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static extern bool CloseHandle(IntPtr hObject); } #endregion #region Win32 // ReSharper disable InconsistentNaming [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern IntPtr CreateJobObject(IntPtr a, string lpName); [DllImport("kernel32")] private static extern bool SetInformationJobObject(SafeJobHandle hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32", SetLastError = true)] private static extern bool AssignProcessToJobObject(SafeJobHandle job, SafeProcessHandle process); [StructLayout(LayoutKind.Sequential)] internal struct IO_COUNTERS { public ulong ReadOperationCount; public ulong WriteOperationCount; public ulong OtherOperationCount; public ulong ReadTransferCount; public ulong WriteTransferCount; public ulong OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] internal 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)] public struct SECURITY_ATTRIBUTES { public uint nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } [StructLayout(LayoutKind.Sequential)] internal 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; } public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, BasicUIRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, GroupInformation = 11 } // ReSharper restre InconsistentNaming #endregion }