Updated SafeMoveableHGlobalHandle class and added tests.

pull/363/head
David Hall 2023-01-04 17:08:36 -07:00
parent 7c0c7fe66c
commit 63203f558e
3 changed files with 242 additions and 175 deletions

View File

@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
namespace Vanara.PInvoke
namespace Vanara.PInvoke;
public static partial class Kernel32
{
public static partial class Kernel32
{
/// <summary>A <see cref="SafeHandle"/> for memory allocated as moveable HGLOBAL.</summary>
/// <seealso cref="SafeHandle"/>
public class SafeMoveableHGlobalHandle : SafeMemoryHandleExt<MoveableHGlobalMemoryMethods>
@ -80,6 +80,11 @@ namespace Vanara.PInvoke
public static SafeMoveableHGlobalHandle CreateFromStructure<T>(in T value = default) =>
new(InteropExtensions.MarshalToPtr(value, mm.AllocMem, out _, 0, mm.LockMem, mm.UnlockMem), true);
/// <summary>Converts an <see cref="SafeMoveableHGlobalHandle"/> to a <see cref="HGLOBAL"/> where it owns the reference.</summary>
/// <param name="ptr">The <see cref="SafeMoveableHGlobalHandle"/>.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator HGLOBAL(SafeMoveableHGlobalHandle ptr) => ptr.handle;
/// <summary>Converts an <see cref="IntPtr"/> to a <see cref="SafeMoveableHGlobalHandle"/> where it owns the reference.</summary>
/// <param name="ptr">The <see cref="IntPtr"/>.</param>
/// <returns>The result of the conversion.</returns>
@ -91,5 +96,4 @@ namespace Vanara.PInvoke
/// <returns>The output from the action.</returns>
public new T CallLocked<T>(Func<IntPtr, T> action) => base.CallLocked(action);
}
}
}

View File

@ -44,13 +44,8 @@ namespace Vanara.PInvoke
GMEM_NOT_BANKED = 0x1000,
/// <summary>Allocate sharable memory.</summary>
[Obsolete("Value is obsolete, but is provided for compatibility with 16-bit Windows.")]
GMEM_SHARE = 0x2000,
/// <summary>Allocate sharable memory.</summary>
[Obsolete("Value is obsolete, but is provided for compatibility with 16-bit Windows.")]
GMEM_DDESHARE = 0x2000,
/// <summary>Notify upon discarding</summary>
[Obsolete("Value is obsolete, but is provided for compatibility with 16-bit Windows.")]
GMEM_NOTIFY = 0x4000,
@ -844,6 +839,9 @@ namespace Vanara.PInvoke
/// <seealso cref="MemoryMethodsBase" />
public sealed class MoveableHGlobalMemoryMethods : MemoryMethodsBase
{
/// <inheritdoc/>
public override bool AllocZeroes => true;
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
public override bool Lockable => true;
@ -858,12 +856,12 @@ namespace Vanara.PInvoke
/// <summary>Frees the memory associated with a handle.</summary>
/// <param name="hMem">A memory handle.</param>
public override void FreeMem(IntPtr hMem) => GlobalFree(hMem);
public override void FreeMem(IntPtr hMem) => Win32Error.ThrowLastErrorIfNull((IntPtr)GlobalFree(hMem));
/// <summary>Locks the memory of a specified handle and gets a pointer to it.</summary>
/// <param name="hMem">A memory handle.</param>
/// <returns>A pointer to the locked memory.</returns>
public override IntPtr LockMem(IntPtr hMem) => GlobalLock(hMem);
public override IntPtr LockMem(IntPtr hMem) => Win32Error.ThrowLastErrorIfNull(GlobalLock(hMem));
/// <summary>Gets the reallocation method.</summary>
/// <param name="hMem">A memory handle.</param>
@ -875,9 +873,6 @@ namespace Vanara.PInvoke
/// <param name="hMem">A memory handle.</param>
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
public override bool UnlockMem(IntPtr hMem) => GlobalUnlock(hMem);
/// <inheritdoc/>
protected override bool AllocZeroes => true;
}
}
}

View File

@ -1,22 +1,21 @@
using NUnit.Framework;
using System;
using System.Runtime.InteropServices;
using System.Text;
using Vanara.Extensions;
using Vanara.InteropServices;
using static Vanara.PInvoke.Kernel32;
namespace Vanara.PInvoke.Tests
namespace Vanara.PInvoke.Tests;
[TestFixture]
public class MemoryApiTests
{
[TestFixture]
public class MemoryApiTests
{
// From https://docs.microsoft.com/en-us/windows/desktop/Memory/awe-example
[Test]
public void AWETest()
{
const uint MEMORY_REQUESTED = 1024 * 1024;
GetSystemInfo(out var sSysInfo); // fill the system information structure
GetSystemInfo(out SYSTEM_INFO sSysInfo); // fill the system information structure
TestContext.Write("This computer has page size {0}.\n", sSysInfo.dwPageSize);
@ -25,13 +24,13 @@ namespace Vanara.PInvoke.Tests
TestContext.Write("Requesting {0} pages of memory.\n", NumberOfPages);
// Enable the privilege.
using (var hProc = SafeHPROCESS.Current)
using (SafeHPROCESS hProc = SafeHPROCESS.Current)
using (new ElevPriv("SeLockMemoryPrivilege", hProc))
{
// Allocate the physical memory.
var aPFNs = new IntPtr[NumberOfPages];
IntPtr[] aPFNs = new IntPtr[NumberOfPages];
var NumberOfPagesInitial = NumberOfPages;
SizeT NumberOfPagesInitial = NumberOfPages;
Assert.That(AllocateUserPhysicalPages(hProc, ref NumberOfPages, aPFNs), ResultIs.Successful);
@ -69,38 +68,107 @@ namespace Vanara.PInvoke.Tests
[Test]
public void FillMemoryTest()
{
using (var mem = new SafeHGlobalHandle(128))
{
using SafeHGlobalHandle mem = new(128);
FillMemory((IntPtr)mem, 128, 0xFF);
Assert.That(mem.ToArray<byte>(128), Has.All.EqualTo(0xFF));
}
}
[Test]
public void GetLargePageMinimumTest()
{
Assert.That((uint)GetLargePageMinimum(), Is.GreaterThan(0));
}
public void GetLargePageMinimumTest() => Assert.That((uint)GetLargePageMinimum(), Is.GreaterThan(0));
[Test]
public void GetMemoryErrorHandlingCapabilitiesTest()
{
Assert.That(GetMemoryErrorHandlingCapabilities(out var cap), Is.True);
Assert.That(GetMemoryErrorHandlingCapabilities(out uint cap), Is.True);
TestContext.WriteLine(cap);
}
[Test]
public void GetProcessWorkingSetSizeExTest()
{
Assert.That(GetProcessWorkingSetSizeEx(GetCurrentProcess(), out var min, out var max, out var flg), Is.True);
Assert.That(GetProcessWorkingSetSizeEx(GetCurrentProcess(), out SizeT min, out SizeT max, out QUOTA_LIMITS_HARDWS flg), Is.True);
TestContext.WriteLine($"{min} : {max} : {flg}");
}
[Test]
public void GetSystemFileCacheSizeTest()
{
Assert.That(GetSystemFileCacheSize(out var min, out var max, out var flg), Is.True);
Assert.That(GetSystemFileCacheSize(out SizeT min, out SizeT max, out FILE_CACHE_LIMITS flg), Is.True);
TestContext.WriteLine($"{min} : {max} : {flg}");
}
[Test]
public void SafeMoveableHGlobleFlagsTest()
{
using SafeMoveableHGlobalHandle h = new(32);
Assert.AreEqual(GlobalFlags(h), GMEM.GMEM_SHARE);
}
[Test, Repeat(50)]
public void SafeMoveableHGlobalCreateFromHGLOBALTest()
{
var val = new RECT(8, 16, 32, 64);
var ptr = InteropExtensions.MarshalToPtr(val, i => (IntPtr)GlobalAlloc(GMEM.GHND | GMEM.GMEM_SHARE, i), out _, memLock: p => GlobalLock(p), memUnlock: p => GlobalUnlock(p));
using SafeMoveableHGlobalHandle h = new(ptr, true);
Assert.AreEqual(val, h.ToStructure<RECT>());
}
[Test]
public void SafeMoveableHGlobalCreateFromStringListTest()
{
var strings = new[] { "AAAA", "BBBB", "CCCC" };
using SafeMoveableHGlobalHandle h = SafeMoveableHGlobalHandle.CreateFromStringList(strings);
CollectionAssert.AreEqual(strings, h.ToStringEnum());
}
[Test]
public void SafeMoveableHGlobalCreateFromStructTest()
{
var val = new RECT(8, 16, 32, 64);
using SafeMoveableHGlobalHandle h = SafeMoveableHGlobalHandle.CreateFromStructure(val);
Assert.AreEqual(val, h.ToStructure<RECT>());
}
[Test]
public void SafeMoveableHGlobalCreateFromBytesTest()
{
const string txt = @"“00©0è0”";
using SafeMoveableHGlobalHandle h = new(System.Text.Encoding.Unicode.GetBytes(txt + '\0'));
Assert.AreEqual(txt, h.ToString(-1));
}
[Test, Repeat(50)]
public void SafeMoveableHGlobalTakeOwnershipTest()
{
var val = new RECT(8, 16, 32, 64);
using SafeMoveableHGlobalHandle h = SafeMoveableHGlobalHandle.CreateFromStructure(val);
HGLOBAL myh = h.TakeOwnership();
try
{
var p = GlobalLock(myh);
Assert.AreEqual(val, p.ToStructure<RECT>());
GlobalUnlock(myh);
}
finally { Assert.AreEqual(GlobalFree(myh), HGLOBAL.NULL); }
}
[Test]
public void SafeMoveableHGlobalWriteStructTest()
{
var val = new RECT(8, 16, 32, 64);
using SafeMoveableHGlobalHandle h = new(32);
h.Write(val);
Assert.AreEqual(val, h.ToStructure<RECT>());
}
[Test]
public void SafeMoveableHGlobalWriteStringListTest()
{
var strings = new[] { "AAAA", "BBBB", "CCCC" };
using SafeMoveableHGlobalHandle h = new(256);
h.CallLocked(p => p.Write(strings, StringListPackMethod.Concatenated, offset: 64, allocatedBytes: h.Size));
CollectionAssert.AreEqual(strings, h.ToStringEnum(prefixBytes: 64));
Assert.That(() => h.CallLocked(p => p.Write(strings, StringListPackMethod.Concatenated, offset: 250, allocatedBytes: h.Size)), Throws.Exception);
CollectionAssert.AreEqual(strings, h.ToStringEnum(prefixBytes: 64));
}
}