diff --git a/PInvoke/NtDll/Winternl.UnicodeString.cs b/PInvoke/NtDll/Winternl.UnicodeString.cs
new file mode 100644
index 00000000..61360113
--- /dev/null
+++ b/PInvoke/NtDll/Winternl.UnicodeString.cs
@@ -0,0 +1,254 @@
+using System;
+using System.Runtime.InteropServices;
+using Vanara.Extensions;
+using Vanara.InteropServices;
+using static Vanara.PInvoke.Kernel32;
+
+namespace Vanara.PInvoke
+{
+ public static partial class NtDll
+ {
+ /// Initializes a counted Unicode string.
+ ///
+ /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified.
+ ///
+ /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string.
+ /// None
+ // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString(
+ // PUNICODE_STRING DestinationString, PCWSTR SourceString );
+ [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
+ [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")]
+ public static extern void RtlInitUnicodeString(ref UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr), Optional] string SourceString);
+
+ /// Initializes a counted Unicode string.
+ ///
+ /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified.
+ ///
+ /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string.
+ /// None
+ // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString(
+ // PUNICODE_STRING DestinationString, PCWSTR SourceString );
+ [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
+ [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")]
+ public static extern void RtlInitUnicodeString(ref UNICODE_STRING_WOW64 DestinationString, [MarshalAs(UnmanagedType.LPWStr), Optional] string SourceString);
+
+ /// Initializes a counted Unicode string.
+ ///
+ /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified.
+ ///
+ /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string.
+ /// None
+ // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString(
+ // PUNICODE_STRING DestinationString, PCWSTR SourceString );
+ [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
+ [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")]
+ public static extern void RtlInitUnicodeString([In, Out] IntPtr DestinationString, [MarshalAs(UnmanagedType.LPWStr), Optional] string SourceString);
+
+ /// Initializes a counted Unicode string.
+ ///
+ /// The buffer for a counted Unicode string to be initialized. The length is initialized to zero if the SourceString is not specified.
+ ///
+ /// Optional pointer to a null-terminated Unicode string with which to initialize the counted string.
+ /// None
+ // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-rtlinitunicodestring void RtlInitUnicodeString(
+ // PUNICODE_STRING DestinationString, PCWSTR SourceString );
+ [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)]
+ [PInvokeData("winternl.h", MSDNShortId = "NF:winternl.RtlInitUnicodeString")]
+ public static extern void RtlInitUnicodeString([In, Out] IntPtr DestinationString, [In, Optional] IntPtr SourceString);
+
+ /// The UNICODE_STRING structure is used to define Unicode strings.
+ ///
+ ///
+ /// The UNICODE_STRING structure is used to pass Unicode strings. Use RtlUnicodeStringInit or RtlUnicodeStringInitEx to
+ /// initialize a UNICODE_STRING structure.
+ ///
+ /// If the string is null-terminated, Length does not include the trailing null character.
+ ///
+ /// The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine
+ /// such as RtlAnsiStringToUnicodeString the returned string does not exceed the buffer size.
+ ///
+ ///
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wudfwdm/ns-wudfwdm-_unicode_string typedef struct
+ // _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWCH Buffer; } UNICODE_STRING;
+ [PInvokeData("wudfwdm.h", MSDNShortId = "b02f29a9-1049-4e29-aac3-72bf0c70a21e")]
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct UNICODE_STRING
+ {
+ /// The length, in bytes, of the string stored in Buffer.
+ public ushort Length;
+
+ /// The length, in bytes, of Buffer.
+ public ushort MaximumLength;
+
+ /// Pointer to a wide-character string.
+ public IntPtr Buffer;
+
+ ///
+ public override string ToString() => StringHelper.GetString(Buffer, CharSet.Unicode, MaximumLength) ?? string.Empty;
+
+ /// Extracts the string value from this structure by reading process specific memory.
+ /// The process handle of the process from which to read the memory.
+ /// A that has the value.
+ public string ToString(HPROCESS hProc)
+ {
+ using var mem = new SafeCoTaskMemString(MaximumLength);
+ return ReadProcessMemory(hProc, Buffer, mem, mem.Size, out _) ? mem : string.Empty;
+ }
+ }
+
+ /// The UNICODE_STRING structure is used to define Unicode strings.
+ ///
+ ///
+ /// The UNICODE_STRING structure is used to pass Unicode strings. Use RtlUnicodeStringInit or RtlUnicodeStringInitEx to
+ /// initialize a UNICODE_STRING structure.
+ ///
+ /// If the string is null-terminated, Length does not include the trailing null character.
+ ///
+ /// The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine
+ /// such as RtlAnsiStringToUnicodeString the returned string does not exceed the buffer size.
+ ///
+ ///
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wudfwdm/ns-wudfwdm-_unicode_string typedef struct
+ // _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWCH Buffer; } UNICODE_STRING;
+ [PInvokeData("wudfwdm.h", MSDNShortId = "b02f29a9-1049-4e29-aac3-72bf0c70a21e")]
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct UNICODE_STRING_WOW64
+ {
+ /// The length, in bytes, of the string stored in Buffer.
+ public ushort Length;
+
+ /// The length, in bytes, of Buffer.
+ public ushort MaximumLength;
+
+ /// Pointer to a wide-character string.
+ public long Buffer;
+
+ /// Extracts the string value from this structure by reading process specific memory.
+ /// The process handle of the process from which to read the memory.
+ /// A that has the value.
+ 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;
+ }
+ }
+
+ ///
+ /// Provides an abstraction for both and that converts easily with
+ /// .
+ ///
+ ///
+ public class SafeUNICODE_STRING : SafeHANDLE
+ {
+ /// Initializes a new instance of the class with a string value.
+ /// The string value.
+ public SafeUNICODE_STRING(string value) : base(IntPtr.Zero, true)
+ {
+ var structLen = GetStructSize();
+
+ if (string.IsNullOrEmpty(value))
+ SetHandle(Marshal.AllocCoTaskMem(structLen));
+ else
+ SetHandle(InitMemForString(value, structLen));
+ }
+
+ internal SafeUNICODE_STRING(IntPtr ptr, bool own) : base(ptr, own)
+ {
+ }
+
+ private SafeUNICODE_STRING() : base()
+ {
+ }
+
+ /// Performs an implicit conversion from to .
+ /// The string value.
+ /// The resulting instance from the conversion.
+ public static implicit operator SafeUNICODE_STRING(string value) => new SafeUNICODE_STRING(value);
+
+ /// Performs an implicit conversion from to .
+ /// The value.
+ /// The resulting instance from the conversion.
+ public static implicit operator string(SafeUNICODE_STRING value) => value?.ToString(default);
+
+ /// Extracts the string value from this structure by reading process specific memory.
+ /// The process handle of the process from which to read the memory.
+ /// A that has the value.
+ public string ToString(HPROCESS hProc)
+ {
+ if (IsInvalid) return null;
+ var len = unchecked((ushort)Marshal.ReadInt16(handle, 2));
+ hProc = hProc == default ? GetCurrentProcess() : hProc;
+ using var mem = new SafeCoTaskMemString(len);
+ if (IsWow64Process(hProc))
+ return NtWow64ReadVirtualMemory64(hProc, handle.Offset(Marshal.OffsetOf(typeof(UNICODE_STRING_WOW64), "Buffer").ToInt64()).ToInt64(), ((IntPtr)mem).ToInt32(), mem.Size, out _).Succeeded ? mem : string.Empty;
+ return ReadProcessMemory(hProc, handle.Offset(Marshal.OffsetOf(typeof(UNICODE_STRING), "Buffer").ToInt64()), mem, mem.Size, out _) ? mem : string.Empty;
+ }
+
+ /// Extracts the string value from this structure by reading process specific memory.
+ /// A that has the value.
+ public override string ToString() => ToString(default);
+
+ internal static int GetStructSize() => Marshal.SizeOf(IsWow64Process() ? typeof(UNICODE_STRING_WOW64) : typeof(UNICODE_STRING));
+
+ internal static IntPtr InitMemForString(string value, int structLen)
+ {
+ // Collect lengths
+ var strLen = (ushort)StringHelper.GetByteCount(value, true, CharSet.Unicode);
+
+ // Create mem and append string after struct
+ IntPtr mem = Marshal.AllocCoTaskMem(structLen + strLen);
+ IntPtr strOffset = mem.Offset(structLen);
+ StringHelper.Write(value, strOffset, out _, true, CharSet.Unicode, strLen);
+ RtlInitUnicodeString(mem, strOffset);
+ return mem;
+ }
+
+ ///
+ protected override bool InternalReleaseHandle() { Marshal.FreeCoTaskMem(handle); return true; }
+
+ private static bool IsWow64Process(HPROCESS hProc = default) => Kernel32.IsWow64Process(hProc == default ? GetCurrentProcess() : hProc, out var w) && w;
+ }
+
+ /// A custom marshaler for functions using UNICODE_STRING so that managed strings can be used.
+ ///
+ internal class UnicodeStringMarshaler : ICustomMarshaler
+ {
+ public static ICustomMarshaler GetInstance(string _) => new UnicodeStringMarshaler();
+
+ void ICustomMarshaler.CleanUpManagedData(object ManagedObj)
+ {
+ }
+
+ void ICustomMarshaler.CleanUpNativeData(IntPtr pNativeData) => Marshal.FreeCoTaskMem(pNativeData);
+
+ int ICustomMarshaler.GetNativeDataSize() => SafeUNICODE_STRING.GetStructSize();
+
+ IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj) => ManagedObj is string s ? SafeUNICODE_STRING.InitMemForString(s, SafeUNICODE_STRING.GetStructSize()) : IntPtr.Zero;
+
+ object ICustomMarshaler.MarshalNativeToManaged(IntPtr pNativeData) => new SafeUNICODE_STRING(pNativeData, false).ToString();
+ }
+
+ /*
+ RtlAppendUnicodeStringToString
+ RtlCompareUnicodeString
+ RtlConvertSidToUnicodeString
+ RtlCopyUnicodeString
+ RtlCreateUnicodeString
+ RtlDowncaseUnicodeString
+ RtlEqualUnicodeString
+ RtlFreeUnicodeString
+ RtlHashUnicodeString
+ RtlInt64ToUnicodeString
+ RtlIntegerToUnicodeString
+ RtlOemStringToUnicodeString
+ RtlPrefixUnicodeString
+ RtlUnicodeStringToAnsiString
+ RtlUnicodeStringToCountedOemString
+ RtlUnicodeStringToInteger
+ RtlUnicodeStringToOemString
+ RtlUpcaseUnicodeString
+ RtlUpcaseUnicodeStringToCountedOemString
+ RtlUpcaseUnicodeStringToOemString
+ */
+ }
+}
\ No newline at end of file