.net 中 CreateJobObject/SetInformationJobObject pinvoke 的工作示例?

发布于 2024-11-14 13:18:49 字数 4126 浏览 0 评论 0原文

我正在努力整理一个 pinvoke CreateJobObject 和 SetInformationJobObject 的工作示例。通过各种谷歌搜索(包括俄语和中文帖子!),我拼凑了以下代码。我认为 JOBOBJECT_BASIC_LIMIT_INFORMATION 的定义根据平台(32/64 位)而变化。 CreateJobObject/AssignProcessToJobObject似乎可以工作。 SetInformationJobObject 失败 - 错误为 24 或 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;
        }

I'm struggling to put together a working example of pinvoke'ing CreateJobObject and SetInformationJobObject. Through various google searches (including Russian and Chinese posts!) I've cobbled together the following code. I think the definition of JOBOBJECT_BASIC_LIMIT_INFORMATION changes based on platform (32/64-bit). The CreateJobObject/AssignProcessToJobObject seems to work. SetInformationJobObject fails - either with error 24 or 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;
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

荒芜了季节 2024-11-21 13:18:49

这可能有点晚了,但仍然如此。

我尝试了这里的所有示例,但没有人同时在 32 位和 64 位模式下为我工作。最后,我需要亲自检查所有签名并创建相应的 PInvoke 例程。我想,其他人可能会发现这很有帮助。

免责声明:该解决方案基于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

}

This can be little bit late, but still.

I tried all of the examples here, but no one was working for me in 32 and 64 bit mode simultaneously. Finally, I was required to examine all the signatures myself and create corresponding PInvoke routines. I think, somebody else could find this helpful.

Disclaimer: the solution is based on Matt Howells' answer.

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

}
朱染 2024-11-21 13:18:49

改进亚历山大的答案,这里有一个使用 SafeHandle 的版本。这是一个 CriticalFinalizerObject,它使句柄的使用更加安全。 .NET API(例如 Process 类)始终将 SafeHandle 与 P/Invoke 结合使用,而不是 IntPtr

using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

public 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", new Win32Exception());
        }
    }

    public void Dispose()
    {
        if (_disposed) return;

        _handle?.Dispose();
        _handle = null;
        _disposed = true;
    }

    [MemberNotNull(nameof(_handle))]
    private void ValidateDisposed()
    {
        ObjectDisposedException.ThrowIf(_disposed || _handle is null, this);
    }

    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);
    }

    private sealed class SafeJobHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public SafeJobHandle(IntPtr handle) : base(true)
        {
            SetHandle(handle);
        }

        protected override bool ReleaseHandle()
        {
            return CloseHandle(handle);
        }

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

    [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)]
    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_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 SECURITY_ATTRIBUTES
    {
        public uint nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }

    [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;
    }

    private enum JobObjectInfoType
    {
        AssociateCompletionPortInformation = 7,
        BasicLimitInformation = 2,
        BasicUIRestrictions = 4,
        EndOfJobTimeInformation = 6,
        ExtendedLimitInformation = 9,
        SecurityLimitInformation = 5,
        GroupInformation = 11
    }
}

Improving on Alexander's answer, here's a version that uses a SafeHandle. This is a CriticalFinalizerObject that makes working with handles much safer. .NET APIs (such as the Process class) always use SafeHandles with P/Invoke instead of IntPtrs.

using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

public 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", new Win32Exception());
        }
    }

    public void Dispose()
    {
        if (_disposed) return;

        _handle?.Dispose();
        _handle = null;
        _disposed = true;
    }

    [MemberNotNull(nameof(_handle))]
    private void ValidateDisposed()
    {
        ObjectDisposedException.ThrowIf(_disposed || _handle is null, this);
    }

    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);
    }

    private sealed class SafeJobHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public SafeJobHandle(IntPtr handle) : base(true)
        {
            SetHandle(handle);
        }

        protected override bool ReleaseHandle()
        {
            return CloseHandle(handle);
        }

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

    [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)]
    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_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 SECURITY_ATTRIBUTES
    {
        public uint nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }

    [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;
    }

    private enum JobObjectInfoType
    {
        AssociateCompletionPortInformation = 7,
        BasicLimitInformation = 2,
        BasicUIRestrictions = 4,
        EndOfJobTimeInformation = 6,
        ExtendedLimitInformation = 9,
        SecurityLimitInformation = 5,
        GroupInformation = 11
    }
}
我不在是我 2024-11-21 13:18:49

总而言之,Alexander Yezutov 提出的签名可以在 x86 和 x64 下工作。当应使用 UIntPtr 时,Matt Howells 签名使用许多 UInt32。我对 CloseHandle 使用了以下 P/Invoke 签名,它似乎工作正常:

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

必须将以下内容添加到 Mas 发布的 app.manifest 中:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
  <!-- A list of all Windows versions that this application is designed to work with. Windows will automatically select the most compatible environment.-->

    <!--The ID below indicates application support for Windows Vista -->
    <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>

    <!--The ID below indicates application support for Windows 7 -->
    <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>

</application>

最后,当从 Visual Studio 启动时,这将不起作用(至少在 Win 7 下)。父进程必须从 Windows 资源管理器启动。

To summarize, the signatures posed by Alexander Yezutov work under both x86 and x64. Matt Howells signatures use a number of UInt32's when UIntPtr should be used instead. I used the following P/Invoke signature for the CloseHandle which seems to work fine:

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

The following must be added to the app.manifest as posted by Mas:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
  <!-- A list of all Windows versions that this application is designed to work with. Windows will automatically select the most compatible environment.-->

    <!--The ID below indicates application support for Windows Vista -->
    <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>

    <!--The ID below indicates application support for Windows 7 -->
    <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>

</application>

and lastly, this won't work (at least under Win 7) when launched from Visual Studio. The parent process must be started from Windows Explorer.

燃情 2024-11-21 13:18:49

有一篇文章使用了您正在尝试使用的 api。
也许您可以从中获得一些见解。

当父进程被终止时终止子进程

There is a post that uses apis that you are trying to use.
Perhaps you can get some insight from there.

Kill child process when parent process is killed

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文