| File: Helpers\TestWindowsProcessJob.cs | Web Access |
| Project: src\tests\Aspire.Tray.Tests\Aspire.Tray.Tests.csproj (Aspire.Tray.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Runtime.InteropServices; using System.Runtime.Versioning; using Microsoft.Win32.SafeHandles; namespace Aspire.Tray.Tests.Helpers; [SupportedOSPlatform("windows")] internal sealed partial class TestWindowsProcessJob : IDisposable { private readonly SafeFileHandle _handle; public static bool HasDesktopShell => GetShellWindow() != 0; public unsafe TestWindowsProcessJob(uint limitFlags) { _handle = CreateJobObject(0, null); try { if (_handle.IsInvalid) { throw new Win32Exception(Marshal.GetLastPInvokeError()); } var limits = new ExtendedLimitInformation { BasicLimits = new BasicLimitInformation { Flags = limitFlags } }; if (!SetInformationJobObject(_handle, 9, &limits, (uint)sizeof(ExtendedLimitInformation))) { throw new Win32Exception(Marshal.GetLastPInvokeError()); } } catch { _handle.Dispose(); throw; } } public void Assign(SafeProcessHandle process) { if (!AssignProcessToJobObject(_handle, process)) { throw new Win32Exception(Marshal.GetLastPInvokeError()); } } public bool Contains(SafeProcessHandle process) { if (!IsProcessInJob(process, _handle, out var result)) { throw new Win32Exception(Marshal.GetLastPInvokeError()); } return result; } public static int GetProcessId(SafeProcessHandle process) { var id = GetNativeProcessId(process); if (id == 0) { throw new Win32Exception(Marshal.GetLastPInvokeError()); } return checked((int)id); } public void Dispose() => _handle.Dispose(); [LibraryImport("user32.dll")] private static partial nint GetShellWindow(); [LibraryImport("kernel32.dll", EntryPoint = "GetProcessId", SetLastError = true)] private static partial uint GetNativeProcessId(SafeProcessHandle process); [LibraryImport("kernel32.dll", EntryPoint = "CreateJobObjectW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] private static partial SafeFileHandle CreateJobObject(nint attributes, string? name); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static unsafe partial bool SetInformationJobObject(SafeFileHandle job, int informationClass, void* information, uint size); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool AssignProcessToJobObject(SafeFileHandle job, SafeProcessHandle process); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool IsProcessInJob(SafeProcessHandle process, SafeFileHandle job, [MarshalAs(UnmanagedType.Bool)] out bool result); [StructLayout(LayoutKind.Sequential)] private struct BasicLimitInformation { public long ProcessTimeLimit; public long JobTimeLimit; public uint Flags; public nuint MinimumWorkingSet; public nuint MaximumWorkingSet; public uint ActiveProcessLimit; public nuint Affinity; public uint PriorityClass; public uint SchedulingClass; } [StructLayout(LayoutKind.Sequential)] private struct IoCounters { public ulong ReadOperations; public ulong WriteOperations; public ulong OtherOperations; public ulong ReadBytes; public ulong WriteBytes; public ulong OtherBytes; } [StructLayout(LayoutKind.Sequential)] private struct ExtendedLimitInformation { public BasicLimitInformation BasicLimits; public IoCounters Io; public nuint ProcessMemoryLimit; public nuint JobMemoryLimit; public nuint PeakProcessMemory; public nuint PeakJobMemory; } }