Added nullability to NtDll

nullableenabled
David Hall 2023-09-08 07:25:56 -06:00
parent c0dc3e92bd
commit 1bdbe60d3b
8 changed files with 48 additions and 85 deletions

View File

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Vanara.Extensions;
using Vanara.Extensions.Reflection;
using Vanara.InteropServices;
namespace Vanara.PInvoke;
@ -1571,19 +1566,19 @@ public static partial class NtDll
/// <exception cref="InvalidCastException">
/// The requested SystemInformationClass does not match the return type requested. or Reported size of object does not match query.
/// </exception>
public static T NtQuerySystemInformation<T>(SYSTEM_INFORMATION_CLASS SystemInformationClass)
public static T? NtQuerySystemInformation<T>(SYSTEM_INFORMATION_CLASS SystemInformationClass)
{
var tType = typeof(T);
if (CorrespondingTypeAttribute.GetCorrespondingTypes(SystemInformationClass).Any() && !CorrespondingTypeAttribute.CanGet(SystemInformationClass, tType))
throw new InvalidCastException("The requested SystemInformationClass does not match the return type requested.");
var status = NtQuerySystemInformation(SystemInformationClass, SafeHGlobalHandle.Null, 0, out var len);
if (status != NTStatus.STATUS_INFO_LENGTH_MISMATCH && status != NTStatus.STATUS_BUFFER_OVERFLOW && status != NTStatus.STATUS_BUFFER_TOO_SMALL)
throw status.GetException();
if ((int)status is not NTStatus.STATUS_INFO_LENGTH_MISMATCH and not NTStatus.STATUS_BUFFER_OVERFLOW and not NTStatus.STATUS_BUFFER_TOO_SMALL)
throw status.GetException()!;
var mem = new SafeHGlobalHandle(SystemInformationClass == SYSTEM_INFORMATION_CLASS.SystemProcessInformation ? (int)len * 2 : (int)len);
NtQuerySystemInformation(SystemInformationClass, mem, (uint)mem.Size, out len).ThrowIfFailed();
if (tType.IsArray)
{
var aType = tType.GetElementType();
var aType = tType.GetElementType()!;
if (aType == typeof(SYSTEM_PROCESS_INFORMATION) && SystemInformationClass == SYSTEM_INFORMATION_CLASS.SystemProcessInformation)
{
var retList = new List<SYSTEM_PROCESS_INFORMATION>();
@ -1599,7 +1594,7 @@ public static partial class NtDll
}
var cnt = Math.DivRem(len, Marshal.SizeOf(aType), out var res);
if (res != 0) throw new InvalidCastException("Reported size of object does not match query.");
return (T)mem.InvokeGenericMethod("ToArray", new[] { aType }, new[] { typeof(int), typeof(int) }, new object[] { (int)cnt, 0 });
return (T?)mem.InvokeGenericMethod("ToArray", new[] { aType }, new[] { typeof(int), typeof(int) }, new object[] { (int)cnt, 0 });
}
return mem.ToStructure<T>();
}
@ -1615,10 +1610,10 @@ public static partial class NtDll
public static IList<Tuple<SYSTEM_PROCESS_INFORMATION, SYSTEM_THREAD_INFORMATION[]>> NtQuerySystemInformation_Process()
{
var status = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessInformation, SafeHGlobalHandle.Null, 0, out var len);
if (status != NTStatus.STATUS_INFO_LENGTH_MISMATCH && status != NTStatus.STATUS_BUFFER_OVERFLOW && status != NTStatus.STATUS_BUFFER_TOO_SMALL)
throw status.GetException();
if ((int)status is not NTStatus.STATUS_INFO_LENGTH_MISMATCH and not NTStatus.STATUS_BUFFER_OVERFLOW and not NTStatus.STATUS_BUFFER_TOO_SMALL)
throw status.GetException()!;
var mem = new SafeHGlobalHandle((int)len * 2);
NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessInformation, mem, (uint)mem.Size, out len).ThrowIfFailed();
NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessInformation, mem, (uint)mem.Size, out _).ThrowIfFailed();
var retList = new List<Tuple<SYSTEM_PROCESS_INFORMATION, SYSTEM_THREAD_INFORMATION[]>>();
var pi = new SYSTEM_PROCESS_INFORMATION();
var ptr = mem.DangerousGetHandle();
@ -1626,7 +1621,7 @@ public static partial class NtDll
do
{
pi = ptr.ToStructure<SYSTEM_PROCESS_INFORMATION>();
retList.Add(new Tuple<SYSTEM_PROCESS_INFORMATION, SYSTEM_THREAD_INFORMATION[]>(pi, ptr.Offset(pSz).ToArray<SYSTEM_THREAD_INFORMATION>((int)pi.NumberOfThreads)));
retList.Add(new Tuple<SYSTEM_PROCESS_INFORMATION, SYSTEM_THREAD_INFORMATION[]>(pi, ptr.Offset(pSz).ToArray<SYSTEM_THREAD_INFORMATION>((int)pi.NumberOfThreads)!));
ptr = ptr.Offset(pi.NextEntryOffset);
} while (pi.NextEntryOffset > 0);
return retList;

View File

@ -1,6 +1,4 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.IO;
using static Vanara.PInvoke.Kernel32;
namespace Vanara.PInvoke;
@ -869,7 +867,7 @@ public static partial class NtDll
// POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, uint FileAttributes, uint
// ShareAccess, uint CreateDisposition, uint CreateOptions, IntPtr EaBuffer, uint EaLength);
public static extern NTStatus NtCreateFile(out SafeHFILE FileHandle, ACCESS_MASK DesiredAccess, in OBJECT_ATTRIBUTES ObjectAttributes, out IO_STATUS_BLOCK IoStatusBlock,
in long AllocationSize, FileFlagsAndAttributes FileAttributes, FileShare ShareAccess, NtFileMode CreateDisposition, NtFileCreateOptions CreateOptions, [In, Optional] IntPtr EaBuffer, uint EaLength);
in long AllocationSize, FileFlagsAndAttributes FileAttributes, FileShare ShareAccess, NtFileMode CreateDisposition, NtFileCreateOptions CreateOptions, IntPtr EaBuffer = default, uint EaLength = 0);
/// <summary>
/// <para>The <c>NtCreateFile</c> routine creates a new file or opens an existing file.</para>
@ -1349,7 +1347,7 @@ public static partial class NtDll
// ShareAccess, uint CreateDisposition, uint CreateOptions, IntPtr EaBuffer, uint EaLength);
public static extern NTStatus NtCreateFile(out SafeHFILE FileHandle, ACCESS_MASK DesiredAccess, in OBJECT_ATTRIBUTES ObjectAttributes, out IO_STATUS_BLOCK IoStatusBlock,
[In, Optional] IntPtr AllocationSize, FileFlagsAndAttributes FileAttributes, FileShare ShareAccess, NtFileMode CreateDisposition, NtFileCreateOptions CreateOptions,
[In, Optional] IntPtr EaBuffer, uint EaLength);
IntPtr EaBuffer = default, uint EaLength = 0);
/// <summary>
/// <para>The <c>NtCreateSection</c> routine creates a section object.</para>
@ -1848,7 +1846,7 @@ public static partial class NtDll
// Flags, PVOID HeapBase, SIZE_T ReserveSize, SIZE_T CommitSize, PVOID Lock, PRTL_HEAP_PARAMETERS Parameters );
[DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
[PInvokeData("ntifs.h", MSDNShortId = "NF:ntifs.RtlCreateHeap")]
public static extern IntPtr RtlCreateHeap(HeapFlags Flags, [In, Optional] IntPtr HeapBase, [In, Optional] SizeT ReserveSize,
public static extern IntPtr RtlCreateHeap([In, Optional] HeapFlags Flags, [In, Optional] IntPtr HeapBase, [In, Optional] SizeT ReserveSize,
[In, Optional] SizeT CommitSize, [In, Optional] IntPtr Lock, [In, Optional] IntPtr Parameters);
/// <summary>
@ -2001,7 +1999,7 @@ public static partial class NtDll
// Flags, PVOID HeapBase, SIZE_T ReserveSize, SIZE_T CommitSize, PVOID Lock, PRTL_HEAP_PARAMETERS Parameters );
[DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
[PInvokeData("ntifs.h", MSDNShortId = "NF:ntifs.RtlCreateHeap")]
public static extern IntPtr RtlCreateHeap(HeapFlags Flags, [In, Optional] IntPtr HeapBase, [In, Optional] SizeT ReserveSize,
public static extern IntPtr RtlCreateHeap([In, Optional] HeapFlags Flags, [In, Optional] IntPtr HeapBase, [In, Optional] SizeT ReserveSize,
[In, Optional] SizeT CommitSize, [In, Optional] IntPtr Lock, in RTL_HEAP_PARAMETERS Parameters);
/// <summary>
@ -2110,7 +2108,7 @@ public static partial class NtDll
/// <para>If HeapBase is NULL, CommitRoutine must also be NULL.</para>
/// </summary>
[MarshalAs(UnmanagedType.FunctionPtr)]
public PRTL_HEAP_COMMIT_ROUTINE CommitRoutine;
public PRTL_HEAP_COMMIT_ROUTINE? CommitRoutine;
/// <summary>Reserved for system use. Drivers must set this parameter to zero.</summary>
private SizeT Reserved1;

View File

@ -1,15 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Vanara.Extensions;
using Vanara.Extensions.Reflection;
using Vanara.InteropServices;
using static Vanara.PInvoke.Kernel32;
namespace Vanara.PInvoke;
namespace Vanara.PInvoke;
/// <summary>Platform invokable enumerated types, constants and functions from ntdll.h</summary>
public static partial class NtDll
@ -900,7 +889,7 @@ public static partial class NtDll
[DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
[PInvokeData("wdm.h", MSDNShortId = "5ffd8262-10b3-4c40-bd3e-050271338508")]
public static extern NTStatus NtCreateEnlistment(out SafeEnlistmentHandle EnlistmentHandle, ACCESS_MASK DesiredAccess, [In] SafeResourceManagerHandle ResourceManagerHandle,
[In] SafeTransactionHandle TransactionHandle, in OBJECT_ATTRIBUTES ObjectAttributes, [Optional] uint CreateOptions, NOTIFICATION_MASK NotificationMask, [In, Optional] IntPtr EnlistmentKey);
[In] SafeTransactionHandle TransactionHandle, in OBJECT_ATTRIBUTES ObjectAttributes, [Optional] uint CreateOptions, [In, Optional] NOTIFICATION_MASK NotificationMask, [In, Optional] IntPtr EnlistmentKey);
/// <summary>
/// <para>The <c>ZwCreateEnlistment</c> routine creates a new enlistment object for a transaction.</para>
@ -1103,7 +1092,7 @@ public static partial class NtDll
[DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
[PInvokeData("wdm.h", MSDNShortId = "5ffd8262-10b3-4c40-bd3e-050271338508")]
public static extern NTStatus NtCreateEnlistment(out SafeEnlistmentHandle EnlistmentHandle, ACCESS_MASK DesiredAccess, [In] SafeResourceManagerHandle ResourceManagerHandle,
[In] SafeTransactionHandle TransactionHandle, [In, Optional] IntPtr ObjectAttributes, [Optional] uint CreateOptions, NOTIFICATION_MASK NotificationMask, [In, Optional] IntPtr EnlistmentKey);
[In] SafeTransactionHandle TransactionHandle, [In, Optional] IntPtr ObjectAttributes, [Optional] uint CreateOptions, [In, Optional] NOTIFICATION_MASK NotificationMask, [In, Optional] IntPtr EnlistmentKey);
/// <summary>
/// <para>The <c>ZwCreateResourceManager</c> routine creates a resource manager object.</para>
@ -1476,7 +1465,7 @@ public static partial class NtDll
// RmGuid, POBJECT_ATTRIBUTES ObjectAttributes, ULONG CreateOptions, PUNICODE_STRING Description );
[DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
[PInvokeData("wdm.h", MSDNShortId = "4812eeb4-134f-4ecb-870b-dbab04c1137b")]
public static extern NTStatus NtCreateResourceManager(out SafeResourceManagerHandle ResourceManagerHandle, ACCESS_MASK DesiredAccess, SafeTransactionManagerHandle TmHandle, in Guid RmGuid,
public static extern NTStatus NtCreateResourceManager(out SafeResourceManagerHandle ResourceManagerHandle, ACCESS_MASK DesiredAccess, SafeTransactionManagerHandle TmHandle, [In, Optional] IntPtr RmGuid,
[In, Optional] IntPtr ObjectAttributes, [Optional] uint CreateOptions, [In, Optional, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UnicodeStringMarshaler))] string? Description);
/// <summary>

View File

@ -1,8 +1,4 @@
using System;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
using static Vanara.PInvoke.Kernel32;
using static Vanara.PInvoke.Kernel32;
namespace Vanara.PInvoke;
@ -92,7 +88,7 @@ public static partial class NtDll
public string ToString(HPROCESS hProc)
{
using var mem = new SafeCoTaskMemString(MaximumLength);
return ReadProcessMemory(hProc, Buffer, mem, mem.Size, out _) ? mem : string.Empty;
return ReadProcessMemory(hProc, Buffer, mem, mem.Size, out _) ? (string)mem! : string.Empty;
}
}
@ -129,7 +125,7 @@ public static partial class NtDll
public string ToString(HPROCESS hProc)
{
using var mem = new SafeCoTaskMemString(MaximumLength);
return NtWow64ReadVirtualMemory64(hProc, Buffer, ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? mem : string.Empty;
return NtWow64ReadVirtualMemory64(hProc, Buffer, ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? (string)mem! : string.Empty;
}
}
@ -144,13 +140,13 @@ public static partial class NtDll
/// <summary>Initializes a new instance of the <see cref="SafeUNICODE_STRING"/> class with a string value.</summary>
/// <param name="value">The string value.</param>
public SafeUNICODE_STRING(string value) : base(IntPtr.Zero, true)
public SafeUNICODE_STRING(string? value) : base(IntPtr.Zero, true)
{
var structLen = GetStructSize(GetCurrentProcess());
if (string.IsNullOrEmpty(value))
SetHandle(Marshal.AllocCoTaskMem(size = structLen));
else
SetHandle(InitMemForString(value, structLen, out size));
SetHandle(InitMemForString(value!, structLen, out size));
}
internal SafeUNICODE_STRING(IntPtr ptr, bool own) : base(ptr, own)
@ -173,19 +169,19 @@ public static partial class NtDll
/// <summary>Performs an implicit conversion from <see cref="string"/> to <see cref="SafeUNICODE_STRING"/>.</summary>
/// <param name="value">The string value.</param>
/// <returns>The resulting <see cref="SafeUNICODE_STRING"/> instance from the conversion.</returns>
public static implicit operator SafeUNICODE_STRING(string value) => new(value);
public static implicit operator SafeUNICODE_STRING(string? value) => new(value);
/// <summary>Performs an implicit conversion from <see cref="SafeUNICODE_STRING"/> to <see cref="string"/>.</summary>
/// <param name="value">The value.</param>
/// <returns>The resulting <see cref="string"/> instance from the conversion.</returns>
public static implicit operator string(SafeUNICODE_STRING value) => value?.ToString(default);
public static implicit operator string?(SafeUNICODE_STRING value) => value?.ToString(default);
/// <summary>Extracts the string value from this structure by reading process specific memory.</summary>
/// <param name="hProc">The process handle of the process from which to read the memory.</param>
/// <returns>A <see cref="string"/> that has the value.</returns>
public string ToString(HPROCESS hProc)
{
if (IsInvalid) return null;
if (IsInvalid) return string.Empty;
var maxlen = unchecked((ushort)Marshal.ReadInt16(handle, 2));
hProc = hProc == default ? GetCurrentProcess() : hProc;
var bufOffset = GetBufferOffset(hProc);
@ -194,8 +190,8 @@ public static partial class NtDll
return StringHelper.GetString(bufPtr, CharSet.Unicode, MaximumLength) ?? string.Empty;
using var mem = new SafeCoTaskMemString(maxlen);
if (hProc.IsWow64())
return NtWow64ReadVirtualMemory64(hProc, bufPtr.ToInt64(), ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? mem : string.Empty;
return ReadProcessMemory(hProc, bufPtr, mem, mem.Size, out _) ? mem : string.Empty;
return NtWow64ReadVirtualMemory64(hProc, bufPtr.ToInt64(), ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? (string)mem! : string.Empty;
return ReadProcessMemory(hProc, bufPtr, mem, mem.Size, out _) ? (string)mem! : string.Empty;
}
/// <summary>Extracts the string value from this structure by reading process specific memory.</summary>

View File

@ -1,7 +1,4 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Vanara.InteropServices;
using System.Linq;
namespace Vanara.PInvoke;
@ -548,7 +545,7 @@ public static partial class NtDll
NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out _).ThrowIfFailed();
return mem;
bool TypeIsWow() => typeof(T).Name.EndsWith("WOW64");
static bool TypeIsWow() => typeof(T).Name.EndsWith("WOW64");
}
/// <summary>A call to <c>NtQueryInformationProcess</c> for the supplied process requires WOW64 structs.</summary>

View File

@ -34,7 +34,7 @@ public class StaticFieldValueHash
/// <typeparam name="TEnum">The type of the enum to added.</typeparam>
/// <param name="lib">The optional library name.</param>
public static void AddFields<TType, TFieldType, TEnum>(string? lib = null) where TFieldType : struct, IComparable =>
AddFields<TType, TFieldType>(Enum.GetValues(typeof(TEnum)).Cast<TFieldType>().Select(v => (v, Enum.GetName(typeof(TEnum), v))), lib);
AddFields<TType, TFieldType>(Enum.GetValues(typeof(TEnum)).Cast<TFieldType>().Select(v => (v, Enum.GetName(typeof(TEnum), v)!)), lib);
/// <summary>Tries to get the name of a value's library.</summary>
/// <typeparam name="TType">The type of the type.</typeparam>

View File

@ -1,11 +1,5 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Vanara.InteropServices;
using static Vanara.PInvoke.NtDll;
namespace Vanara.PInvoke.Tests;
@ -23,11 +17,11 @@ public partial class NtDllTests
var qi = NtQuerySystemInformation<SYSTEM_REGISTRY_QUOTA_INFORMATION>(SYSTEM_INFORMATION_CLASS.SystemRegistryQuotaInformation);
Assert.That(qi.RegistryQuotaUsed, Is.Not.Zero);
var ppi = NtQuerySystemInformation<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[]>(SYSTEM_INFORMATION_CLASS.SystemProcessorPerformanceInformation);
Assert.That(ppi.Length, Is.EqualTo(bi.NumberOfProcessors));
Assert.That(ppi?.Length, Is.EqualTo(bi.NumberOfProcessors));
var arr = NtQuerySystemInformation<SYSTEM_PROCESS_INFORMATION[]>(SYSTEM_INFORMATION_CLASS.SystemProcessInformation);
var pti = NtQuerySystemInformation_Process();
Assert.That(arr.Length, Is.EqualTo(pti.Count));
Assert.That(arr?.Length, Is.EqualTo(pti.Count));
TestContext.WriteLine($"{bi.NumberOfProcessors} Cores; {pti.Count} Processes; {pti.Sum(t => t.Item2.Length)} Threads");
}
@ -36,11 +30,11 @@ public partial class NtDllTests
public void SafeUNICODE_STRING_Test()
{
const string testStr = "Testing. 1. 2. 3.";
SafeUNICODE_STRING sstr = null;
SafeUNICODE_STRING? sstr = null;
try
{
Assert.That(() => sstr = testStr, Throws.Nothing);
Assert.That((string)sstr, Is.EqualTo(testStr));
Assert.That((string?)sstr!, Is.EqualTo(testStr));
}
finally
{

View File

@ -1,10 +1,4 @@
using NUnit.Framework;
using NUnit.Framework.Constraints;
using System;
using System.Diagnostics;
using System.Linq;
using Vanara.Extensions;
using Vanara.InteropServices;
using static Vanara.PInvoke.NtDll;
namespace Vanara.PInvoke.Tests;
@ -36,27 +30,27 @@ public partial class WinternlTests
TestContext.WriteLine($"Img: {upp.ImagePathName.ToString(hProc)}; CmdLine: {upp.CommandLine.ToString(hProc)}");
}
NtQueryResult<IntPtr> pdp = null;
NtQueryResult<IntPtr>? pdp = null;
Assert.That(() => pdp = NtQueryInformationProcess<IntPtr>(hProc, PROCESSINFOCLASS.ProcessDebugPort), Throws.Nothing);
Assert.That(pdp, ResultIs.ValidHandle);
TestContext.WriteLine($"DbgPort: {pdp.Value.ToInt64()}");
TestContext.WriteLine($"DbgPort: {pdp?.Value.ToInt64()}");
NtQueryResult<BOOL> pwi = null;
NtQueryResult<BOOL>? pwi = null;
Assert.That(() => pwi = NtQueryInformationProcess<BOOL>(hProc, PROCESSINFOCLASS.ProcessWow64Information), Throws.Nothing);
Assert.That(pwi, ResultIs.ValidHandle);
Assert.That(pwi.Value.Value, Is.True);
Assert.That(pwi?.Value.Value, Is.True);
NtQueryResult<UNICODE_STRING> pfn = null;
NtQueryResult<UNICODE_STRING>? pfn = null;
Assert.That(() => pfn = NtQueryInformationProcess<UNICODE_STRING>(hProc, PROCESSINFOCLASS.ProcessImageFileName), Throws.Nothing);
Assert.That(pfn, ResultIs.ValidHandle);
TestContext.WriteLine($"Fn: {pfn.Value.ToString(hProc)}");
TestContext.WriteLine($"Fn: {pfn?.Value.ToString(hProc)}");
NtQueryResult<BOOL> pbt = null;
NtQueryResult<BOOL>? pbt = null;
Assert.That(() => pbt = NtQueryInformationProcess<BOOL>(hProc, PROCESSINFOCLASS.ProcessBreakOnTermination), Throws.Nothing);
Assert.That(pbt, ResultIs.ValidHandle);
Assert.That(pbt.Value.Value, Is.False);
Assert.That(pbt?.Value.Value, Is.False);
NtQueryResult<SUBSYSTEM_INFORMATION_TYPE> psi = null;
NtQueryResult<SUBSYSTEM_INFORMATION_TYPE>? psi = null;
// This is documented, but fails on Win10
Assert.That(() => psi = NtQueryInformationProcess<SUBSYSTEM_INFORMATION_TYPE>(hProc, PROCESSINFOCLASS.ProcessSubsystemInformation), Throws.ArgumentException);
//Assert.That(psi, ResultIs.ValidHandle);
@ -64,9 +58,9 @@ public partial class WinternlTests
//TestContext.WriteLine($"SubSys: {psi.Value}");
// Try undocumented fetch
NtQueryResult<uint> ppb = null;
NtQueryResult<uint>? ppb = null;
Assert.That(() => ppb = NtQueryInformationProcess<uint>(hProc, PROCESSINFOCLASS.ProcessPriorityBoost), Throws.Nothing);
TestContext.WriteLine($"Priority boost: {ppb.Value}");
TestContext.WriteLine($"Priority boost: {ppb?.Value}");
}
[Test]