0

I'm running a program in C#, and I want to know the list of JobObjects that are assigned to the current process. Is there a way to do this?

To be more specific about my use-case, I'm trying to find the memory limit of the current process. The problem is that the function that returns the limits, returns the limits of the last one. So, if 1 JobObject is assigned to the current process, it is simple, but otherwise I don't understand how do it. It is demonstrated in the example below:

    [DllImport("kernel32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity]
    public static extern IntPtr CreateJobObjectW(IntPtr securityAttrs, [MarshalAs(UnmanagedType.LPWStr)] string name);


    [DllImport("kernel32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity]
    public static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);

    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool QueryInformationJobObject(IntPtr hJob, JobObjectInfoType infoClass, IntPtr info, UInt32 infoLength, IntPtr returnLength);

    public static IntPtr CreateAndAssignJobObject(string name)
    {
        var jobObjectHandle = CreateJobObjectW(securityAttrs: IntPtr.Zero, name);
        var processHandle = Process.GetCurrentProcess().Handle;
        AssignProcessToJobObject(jobObjectHandle, processHandle);
        return jobObjectHandle;
    }

    public static void LimitJobObjectMemory(IntPtr jobObjectHandle, ulong memoryLimit)
    {
        // Set the memory limit of the JobObject to 'memoryLimit'
    }

    /// <summary>
    /// Gets a struct containing the extended limit information for the job object.
    /// </summary>
    private static JOBOBJECT_EXTENDED_LIMIT_INFORMATION GetExtendedLimitInformation()
    {
        var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
        int infoSize = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        IntPtr extendedInfoPtr = Marshal.AllocHGlobal(infoSize);

        try
        {
            Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

            if (!QueryInformationJobObject(
                     IntPtr.Zero,
                     JobObjectInfoType.ExtendedLimitInformation,
                     extendedInfoPtr,
                     (UInt32)infoSize,
                     IntPtr.Zero))
            {
                throw new UtilsException($"QueryInformationJobObject failed; err={Marshal.GetLastWin32Error()}");
            }

            extendedInfo = (JOBOBJECT_EXTENDED_LIMIT_INFORMATION)Marshal.PtrToStructure(extendedInfoPtr, typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        }
        finally
        {
            Marshal.FreeHGlobal(extendedInfoPtr);
        }

        return extendedInfo;
    }

    public static void Main()
    {
        var job1Handle = CreateAndAssignJobObject("job1");
        LimitJobObjectMemory(job1Handle, 4_000_000_000);

        var job2Handle = CreateAndAssignJobObject("job2");
    }

I'll explain what happens in the code: I create the first JobObject (job1Handle), limit its memory, and then create the second JobObject. Now, when I request the limits of the current process, I get the limits defined in the last JobObject. How can I get an aggregation (minimum, for instance) of the memory limit in all JobObjects of the process? Or more generally, how to enumerate the JobObjects assigned to the process?

Yahav
  • 1
  • 1
  • Are you aware of [this restriction](https://learn.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-assignprocesstojobobject) "If the process is already associated with a job, the job specified by `hJob` must be empty or it must be in the hierarchy of nested jobs to which the process already belongs"? – Ben Voigt Feb 06 '23 at 19:27
  • You really should check the return value of `AssignProcessToJobObject`. – Ben Voigt Feb 06 '23 at 19:28
  • @BenVoigt 1. I did not know, thanks. I wonder what actually happens then, since it does not throw an exception. 2. Let's make this simpler - assume I create one assign one JobObject on some process, and then this process spawns a child process, that automatically inherits the father's JobObject. How can it locate the JobObject handle? – Yahav Feb 07 '23 at 17:55
  • You'll definitely want to read https://learn.microsoft.com/en-us/windows/win32/procthread/nested-jobs . As far as getting the job handle(s), it sounds like a royal pain: https://stackoverflow.com/a/13502329/103167 – Ben Voigt Feb 07 '23 at 18:45
  • If the parent and child are cooperating, then the parent can pass the numeric value of the handle to the child (for example on the command-line or in an environment variable) and one of them can use `DuplicateHandle` to get a handle the child can use (if passing a value at launch time, e.g. command-line, the value has to be the handle in parent context, and the child will have to call `DuplicateHandle`) – Ben Voigt Feb 07 '23 at 18:48

0 Answers0