diff --git a/PInvoke/NtDll/Winternl.UnicodeString.cs b/PInvoke/NtDll/Winternl.UnicodeString.cs
index 61360113..8123150e 100644
--- a/PInvoke/NtDll/Winternl.UnicodeString.cs
+++ b/PInvoke/NtDll/Winternl.UnicodeString.cs
@@ -140,16 +140,17 @@ namespace Vanara.PInvoke
///
public class SafeUNICODE_STRING : SafeHANDLE
{
+ private readonly int size;
+
/// 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();
-
+ var structLen = GetStructSize(GetCurrentProcess());
if (string.IsNullOrEmpty(value))
- SetHandle(Marshal.AllocCoTaskMem(structLen));
+ SetHandle(Marshal.AllocCoTaskMem(size = structLen));
else
- SetHandle(InitMemForString(value, structLen));
+ SetHandle(InitMemForString(value, structLen, out size));
}
internal SafeUNICODE_STRING(IntPtr ptr, bool own) : base(ptr, own)
@@ -160,10 +161,19 @@ namespace Vanara.PInvoke
{
}
+ /// The length, in bytes, of the string stored in Buffer.
+ public ushort Length => IsInvalid ? (ushort)0 : unchecked((ushort)Marshal.ReadInt16(handle, 0));
+
+ /// The length, in bytes, of Buffer.
+ public ushort MaximumLength => IsInvalid ? (ushort)0 : unchecked((ushort)Marshal.ReadInt16(handle, 2));
+
+ /// The size of the allocated memory holding the structure and the string.
+ public int Size => size;
+
/// 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);
+ public static implicit operator SafeUNICODE_STRING(string value) => new(value);
/// Performs an implicit conversion from to .
/// The value.
@@ -176,37 +186,43 @@ namespace Vanara.PInvoke
public string ToString(HPROCESS hProc)
{
if (IsInvalid) return null;
- var len = unchecked((ushort)Marshal.ReadInt16(handle, 2));
+ var maxlen = 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;
+ var bufOffset = GetBufferOffset(hProc);
+ var bufPtr = Marshal.ReadIntPtr(handle, bufOffset);
+ if (hProc == GetCurrentProcess())
+ 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;
}
/// 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 int GetStructSize(HPROCESS hProc) => Marshal.SizeOf(hProc.IsWow64() ? typeof(UNICODE_STRING_WOW64) : typeof(UNICODE_STRING));
- internal static IntPtr InitMemForString(string value, int structLen)
+ internal static int GetBufferOffset(HPROCESS hProc) => Marshal.OffsetOf(hProc.IsWow64() ? typeof(UNICODE_STRING_WOW64) : typeof(UNICODE_STRING), "Buffer").ToInt32();
+
+ internal static IntPtr InitMemForString(string value, int structLen, out int allocatedSize)
{
// Collect lengths
- var strLen = (ushort)StringHelper.GetByteCount(value, true, CharSet.Unicode);
+ var strLen = StringHelper.GetByteCount(value, true, CharSet.Unicode);
// Create mem and append string after struct
- IntPtr mem = Marshal.AllocCoTaskMem(structLen + strLen);
+ IntPtr mem = Marshal.AllocCoTaskMem(allocatedSize = structLen + strLen);
IntPtr strOffset = mem.Offset(structLen);
+ Marshal.WriteInt16(mem, 0, (short)(strLen - 2));
+ Marshal.WriteInt16(mem, 2, (short)strLen);
StringHelper.Write(value, strOffset, out _, true, CharSet.Unicode, strLen);
- RtlInitUnicodeString(mem, strOffset);
+ Marshal.WriteIntPtr(mem, GetBufferOffset(GetCurrentProcess()), 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.
@@ -221,9 +237,9 @@ namespace Vanara.PInvoke
void ICustomMarshaler.CleanUpNativeData(IntPtr pNativeData) => Marshal.FreeCoTaskMem(pNativeData);
- int ICustomMarshaler.GetNativeDataSize() => SafeUNICODE_STRING.GetStructSize();
+ int ICustomMarshaler.GetNativeDataSize() => SafeUNICODE_STRING.GetStructSize(GetCurrentProcess());
- IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj) => ManagedObj is string s ? SafeUNICODE_STRING.InitMemForString(s, SafeUNICODE_STRING.GetStructSize()) : IntPtr.Zero;
+ IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj) => ManagedObj is string s ? SafeUNICODE_STRING.InitMemForString(s, SafeUNICODE_STRING.GetStructSize(GetCurrentProcess()), out _) : IntPtr.Zero;
object ICustomMarshaler.MarshalNativeToManaged(IntPtr pNativeData) => new SafeUNICODE_STRING(pNativeData, false).ToString();
}
diff --git a/UnitTests/PInvoke/NtDll/NtDllTests.cs b/UnitTests/PInvoke/NtDll/NtDllTests.cs
index f888eb5c..ce8c5510 100644
--- a/UnitTests/PInvoke/NtDll/NtDllTests.cs
+++ b/UnitTests/PInvoke/NtDll/NtDllTests.cs
@@ -31,5 +31,21 @@ namespace Vanara.PInvoke.Tests
TestContext.WriteLine($"{bi.NumberOfProcessors} Cores; {pti.Count} Processes; {pti.Sum(t => t.Item2.Length)} Threads");
}
+
+ [Test]
+ public void SafeUNICODE_STRING_Test()
+ {
+ const string testStr = "Testing. 1. 2. 3.";
+ SafeUNICODE_STRING sstr = null;
+ try
+ {
+ Assert.That(() => sstr = testStr, Throws.Nothing);
+ Assert.That((string)sstr, Is.EqualTo(testStr));
+ }
+ finally
+ {
+ sstr?.Dispose();
+ }
+ }
}
}
\ No newline at end of file
diff --git a/UnitTests/PInvoke/NtDll/WinternlTests.cs b/UnitTests/PInvoke/NtDll/WinternlTests.cs
index e85d3603..900239b1 100644
--- a/UnitTests/PInvoke/NtDll/WinternlTests.cs
+++ b/UnitTests/PInvoke/NtDll/WinternlTests.cs
@@ -9,13 +9,6 @@ using static Vanara.PInvoke.NtDll;
namespace Vanara.PInvoke.Tests
{
- public static class ExtMeth
- {
- public static readonly Version minWowOSVer = new Version(5, 1);
-
- public static bool IsWow64(this HPROCESS hProc) => Environment.OSVersion.Version >= minWowOSVer && Kernel32.IsWow64Process(hProc, out var b) && b;
- }
-
[TestFixture]
public partial class WinternlTests
{