Added SafeMoveableHGlobalHandle class and added support to locking memory to all memory classes.

pull/279/head
dahall 2022-01-16 17:22:34 -07:00
parent 747da30302
commit 5f52aa7820
11 changed files with 512 additions and 147 deletions

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Vanara.Extensions;
@ -12,6 +13,10 @@ namespace Vanara.InteropServices
/// <seealso cref="IMemoryMethods"/>
public sealed class CoTaskMemoryMethods : IMemoryMethods
{
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
bool ISimpleMemoryMethods.Lockable => false;
/// <summary>Static instance to methods.</summary>
public static IMemoryMethods Instance { get; } = new CoTaskMemoryMethods();
@ -65,7 +70,8 @@ namespace Vanara.InteropServices
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <param name="hMem">A memory handle.</param>
public void UnlockMem(IntPtr hMem) { }
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
public bool UnlockMem(IntPtr hMem) => false;
}
/// <summary>A <see cref="SafeHandle"/> for memory allocated via COM.</summary>
@ -109,7 +115,7 @@ namespace Vanara.InteropServices
internal SafeCoTaskMemHandle() : base(0) { }
/// <summary>Represents a NULL memory pointer.</summary>
public static SafeCoTaskMemHandle Null => new SafeCoTaskMemHandle(IntPtr.Zero, 0, false);
public static SafeCoTaskMemHandle Null => new(IntPtr.Zero, 0, false);
/// <summary>
/// Allocates from unmanaged memory to represent a structure with a variable length array at the end and marshal these structure
@ -124,7 +130,8 @@ namespace Vanara.InteropServices
/// </param>
/// <param name="prefixBytes">Number of bytes preceding the trailing array of structures</param>
/// <returns><see cref="SafeCoTaskMemHandle"/> object to an native (unmanaged) structure with a trail array of structures</returns>
public static SafeCoTaskMemHandle CreateFromList<T>(IEnumerable<T> values, int count = -1, int prefixBytes = 0) => new SafeCoTaskMemHandle(InteropExtensions.MarshalToPtr(values, mm.AllocMem, out int s, prefixBytes), s);
public static SafeCoTaskMemHandle CreateFromList<T>(IEnumerable<T> values, int count = -1, int prefixBytes = 0) =>
new(InteropExtensions.MarshalToPtr(count < 0 ? values : values.Take(count), mm.AllocMem, out int s, prefixBytes), s);
/// <summary>Allocates from unmanaged memory sufficient memory to hold an array of strings.</summary>
/// <param name="values">The list of strings.</param>
@ -135,17 +142,18 @@ namespace Vanara.InteropServices
/// <see cref="SafeCoTaskMemHandle"/> object to an native (unmanaged) array of strings stored using the <paramref name="packing"/>
/// model and the character set defined by <paramref name="charSet"/>.
/// </returns>
public static SafeCoTaskMemHandle CreateFromStringList(IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated, CharSet charSet = CharSet.Auto, int prefixBytes = 0) => new SafeCoTaskMemHandle(InteropExtensions.MarshalToPtr(values, packing, mm.AllocMem, out int s, charSet, prefixBytes), s);
public static SafeCoTaskMemHandle CreateFromStringList(IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated,
CharSet charSet = CharSet.Auto, int prefixBytes = 0) => new(InteropExtensions.MarshalToPtr(values, packing, mm.AllocMem, out int s, charSet, prefixBytes), s);
/// <summary>Allocates from unmanaged memory sufficient memory to hold an object of type T.</summary>
/// <typeparam name="T">Native type</typeparam>
/// <param name="value">The value.</param>
/// <returns><see cref="SafeCoTaskMemHandle"/> object to an native (unmanaged) memory block the size of T.</returns>
public static SafeCoTaskMemHandle CreateFromStructure<T>(in T value = default) => new SafeCoTaskMemHandle(InteropExtensions.MarshalToPtr(value, mm.AllocMem, out int s), s);
public static SafeCoTaskMemHandle CreateFromStructure<T>(in T value = default) => new(InteropExtensions.MarshalToPtr(value, mm.AllocMem, out int s), s);
/// <summary>Converts an <see cref="IntPtr"/> to a <see cref="SafeCoTaskMemHandle"/> where it owns the reference.</summary>
/// <param name="ptr">The <see cref="IntPtr"/>.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator SafeCoTaskMemHandle(IntPtr ptr) => new SafeCoTaskMemHandle(ptr, 0, true);
public static implicit operator SafeCoTaskMemHandle(IntPtr ptr) => new(ptr, 0, true);
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Vanara.Extensions;
@ -12,6 +13,10 @@ namespace Vanara.InteropServices
/// <seealso cref="IMemoryMethods"/>
public sealed class HGlobalMemoryMethods : IMemoryMethods
{
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
bool ISimpleMemoryMethods.Lockable => false;
/// <summary>Static instance to methods.</summary>
public static IMemoryMethods Instance { get; } = new HGlobalMemoryMethods();
@ -65,7 +70,8 @@ namespace Vanara.InteropServices
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <param name="hMem">A memory handle.</param>
public void UnlockMem(IntPtr hMem) { }
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
public bool UnlockMem(IntPtr hMem) => false;
}
/// <summary>A <see cref="SafeHandle"/> for memory allocated via LocalAlloc.</summary>
@ -108,13 +114,13 @@ namespace Vanara.InteropServices
/// <summary>Converts an <see cref="IntPtr"/> to a <see cref="SafeHGlobalHandle"/> where it owns the reference.</summary>
/// <param name="ptr">The <see cref="IntPtr"/>.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator SafeHGlobalHandle(IntPtr ptr) => new SafeHGlobalHandle(ptr, 0, true);
public static implicit operator SafeHGlobalHandle(IntPtr ptr) => new(ptr, 0, true);
/// <summary>Allocates from unmanaged memory sufficient memory to hold an object of type T.</summary>
/// <typeparam name="T">Native type</typeparam>
/// <param name="value">The value.</param>
/// <returns><see cref="SafeHGlobalHandle"/> object to an native (unmanaged) memory block the size of T.</returns>
public static SafeHGlobalHandle CreateFromStructure<T>(in T value = default) => new SafeHGlobalHandle(InteropExtensions.MarshalToPtr(value, mm.AllocMem, out int s), s);
public static SafeHGlobalHandle CreateFromStructure<T>(in T value = default) => new(InteropExtensions.MarshalToPtr(value, mm.AllocMem, out int s), s);
/// <summary>
/// Allocates from unmanaged memory to represent a structure with a variable length array at the end and marshal these structure elements. It is the
@ -126,7 +132,8 @@ namespace Vanara.InteropServices
/// <param name="count">Number of items in <paramref name="values"/>.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing array of structures</param>
/// <returns><see cref="SafeHGlobalHandle"/> object to an native (unmanaged) structure with a trail array of structures</returns>
public static SafeHGlobalHandle CreateFromList<T>(IEnumerable<T> values, int count = -1, int prefixBytes = 0) => new SafeHGlobalHandle(InteropExtensions.MarshalToPtr(values, mm.AllocMem, out int s, prefixBytes), s);
public static SafeHGlobalHandle CreateFromList<T>(IEnumerable<T> values, int count = -1, int prefixBytes = 0) =>
new(InteropExtensions.MarshalToPtr(count < 0 ? values : values.Take(count), mm.AllocMem, out int s, prefixBytes), s);
/// <summary>Allocates from unmanaged memory sufficient memory to hold an array of strings.</summary>
/// <param name="values">The list of strings.</param>
@ -134,6 +141,7 @@ namespace Vanara.InteropServices
/// <param name="charSet">The character set to use for the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing strings.</param>
/// <returns><see cref="SafeHGlobalHandle"/> object to an native (unmanaged) array of strings stored using the <paramref name="packing"/> model and the character set defined by <paramref name="charSet"/>.</returns>
public static SafeHGlobalHandle CreateFromStringList(IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated, CharSet charSet = CharSet.Auto, int prefixBytes = 0) => new SafeHGlobalHandle(InteropExtensions.MarshalToPtr(values, packing, mm.AllocMem, out int s, charSet, prefixBytes), s);
public static SafeHGlobalHandle CreateFromStringList(IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated,
CharSet charSet = CharSet.Auto, int prefixBytes = 0) => new(InteropExtensions.MarshalToPtr(values, packing, mm.AllocMem, out int s, charSet, prefixBytes), s);
}
}

View File

@ -154,6 +154,10 @@ namespace Vanara.InteropServices
/// <summary>Interface to capture unmanaged simple (alloc/free) memory methods.</summary>
public interface ISimpleMemoryMethods
{
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
bool Lockable { get; }
/// <summary>Gets a handle to a memory allocation of the specified size.</summary>
/// <param name="size">The size, in bytes, of memory to allocate.</param>
/// <returns>A memory handle.</returns>
@ -170,13 +174,18 @@ namespace Vanara.InteropServices
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <param name="hMem">A memory handle.</param>
void UnlockMem(IntPtr hMem);
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
bool UnlockMem(IntPtr hMem);
}
/// <summary>Implementation of <see cref="IMemoryMethods"/> using just the methods from <see cref="ISimpleMemoryMethods"/>.</summary>
/// <seealso cref="Vanara.InteropServices.IMemoryMethods"/>
public abstract class MemoryMethodsBase : IMemoryMethods
{
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
public virtual bool Lockable => false;
/// <summary>
/// Gets a handle to a memory allocation of the specified size.
/// </summary>
@ -238,7 +247,7 @@ namespace Vanara.InteropServices
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <param name="hMem">A memory handle.</param>
public virtual void UnlockMem(IntPtr hMem) { }
public virtual bool UnlockMem(IntPtr hMem) => false;
}
/// <summary>
@ -258,6 +267,10 @@ namespace Vanara.InteropServices
public string Dump => Size == 0 ? "" : string.Join(" ", GetBytes(0, Size).Select(b => b.ToString("X2")).ToArray());
#endif
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
public virtual bool Lockable => false;
/// <summary>Gets or sets the size in bytes of the allocated memory block.</summary>
/// <value>The size in bytes of the allocated memory block.</value>
public abstract SizeT Size { get; set; }
@ -291,10 +304,20 @@ namespace Vanara.InteropServices
public virtual Span<byte> AsBytes() => AsSpan<byte>(Size);
#endif
/// <summary>Locks this instance.</summary>
public virtual void Lock()
{
}
/// <summary>Decrements the lock count.</summary>
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
public virtual bool Unlock() => false;
/// <summary>Releases the owned handle without releasing the allocated memory and returns a pointer to the current memory.</summary>
/// <returns>A pointer to the currently allocated memory. The caller now has the responsibility to free this memory.</returns>
public virtual IntPtr TakeOwnership()
{
while (Unlock()) ;
var h = handle;
SetHandleAsInvalid();
handle = IntPtr.Zero;
@ -308,12 +331,36 @@ namespace Vanara.InteropServices
/// <returns>A byte array with the copied bytes.</returns>
public byte[] GetBytes(int startIndex, int count)
{
if (startIndex < 0 || startIndex + count > Size) throw new ArgumentOutOfRangeException();
if (startIndex < 0 || startIndex + count > Size) throw new ArgumentOutOfRangeException(nameof(startIndex));
var ret = new byte[count];
Marshal.Copy(handle.Offset(startIndex), ret, 0, count);
CallLocked(p => Marshal.Copy(p.Offset(startIndex), ret, 0, count));
return ret;
}
/// <summary>Runs a delegate method while locking the memory.</summary>
/// <param name="action">The action to perform while memory is locked.</param>
protected void CallLocked(Action<IntPtr> action)
{
if (!Lockable)
action.Invoke(handle);
else
{
try { Lock(); action.Invoke(handle); }
finally { Unlock(); }
}
}
/// <summary>Runs a delegate method while locking the memory.</summary>
/// <param name="action">The action to perform while memory is locked.</param>
protected TOut CallLocked<TOut>(Func<IntPtr, TOut> action)
{
if (!Lockable)
return action.Invoke(handle);
try { Lock(); return action.Invoke(handle); }
finally { Unlock(); }
}
private class SafeBufferImpl : SafeBuffer
{
public SafeBufferImpl(SafeAllocatedMemoryHandleBase hMem) : base(false) => Initialize((ulong)hMem.Size);
@ -341,7 +388,7 @@ namespace Vanara.InteropServices
public virtual void Fill(byte value, int length)
{
if (length > Size) throw new ArgumentOutOfRangeException(nameof(length));
handle.FillMemory(value, length);
CallLocked(p => p.FillMemory(value, length));
}
/// <summary>Zero out all allocated memory.</summary>
@ -353,11 +400,13 @@ namespace Vanara.InteropServices
public abstract class SafeMemoryHandle<TMem> : SafeAllocatedMemoryHandle where TMem : IMemoryMethods, new()
{
/// <summary>The <see cref="IMemoryMethods"/> implementation instance.</summary>
protected static readonly TMem mm = new TMem();
protected static readonly TMem mm = new();
/// <summary>The number of bytes currently allocated.</summary>
protected SizeT sz;
private IntPtr unlockedHandle;
/// <summary>Initializes a new instance of the <see cref="SafeMemoryHandle{T}"/> class.</summary>
/// <param name="size">The size of memory to allocate, in bytes.</param>
/// <exception cref="System.ArgumentOutOfRangeException">size - The value of this argument must be non-negative</exception>
@ -384,7 +433,7 @@ namespace Vanara.InteropServices
{
if ((bytes?.Length ?? 0) == 0) return;
InitFromSize(bytes.Length);
Marshal.Copy(bytes, 0, handle, bytes.Length);
CallLocked(p => Marshal.Copy(bytes, 0, p, bytes.Length));
}
/// <summary>
@ -396,7 +445,7 @@ namespace Vanara.InteropServices
{
if (source is null) return;
InitFromSize(source.Size);
((IntPtr)source).CopyTo(handle, source.Size);
CallLocked(p => ((IntPtr)source).CopyTo(p, source.Size));
}
/// <summary>When overridden in a derived class, gets a value indicating whether the handle value is invalid.</summary>
@ -424,6 +473,38 @@ namespace Vanara.InteropServices
}
}
/// <summary>Locks this instance.</summary>
public override void Lock()
{
if (!Lockable) return;
if (unlockedHandle == default)
{
var hlocked = mm.LockMem(handle);
if (hlocked != handle)
{
unlockedHandle = handle;
SetHandle(hlocked);
}
}
else
mm.LockMem(unlockedHandle);
}
/// <summary>Decrements the lock count.</summary>
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
public override bool Unlock()
{
if (!Lockable || unlockedHandle == default)
return false;
var stillLocked = mm.UnlockMem(unlockedHandle);
if (!stillLocked)
{
SetHandle(unlockedHandle);
unlockedHandle = default;
}
return stillLocked;
}
/// <summary>When overridden in a derived class, executes the code required to free the handle.</summary>
/// <returns>
/// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it
@ -431,6 +512,7 @@ namespace Vanara.InteropServices
/// </returns>
protected override bool ReleaseHandle()
{
while (Unlock()) ;
mm.FreeMem(handle);
sz = 0;
handle = IntPtr.Zero;
@ -479,7 +561,7 @@ namespace Vanara.InteropServices
/// </summary>
/// <param name="values">Array of unmanaged pointers</param>
/// <returns>SafeMemoryHandleExt object to an native (unmanaged) array of pointers</returns>
protected SafeMemoryHandleExt(IntPtr[] values) : this(IntPtr.Size * values.Length) => Marshal.Copy(values, 0, handle, values.Length);
protected SafeMemoryHandleExt(IntPtr[] values) : this(IntPtr.Size * values.Length) => CallLocked(p => Marshal.Copy(values, 0, p, values.Length));
/// <summary>Allocates from unmanaged memory to represent a Unicode string (WSTR) and marshal this to a native PWSTR.</summary>
/// <param name="s">The string value.</param>
@ -491,6 +573,13 @@ namespace Vanara.InteropServices
SetHandle(StringHelper.GetCharSize(charSet) == 2 ? mm.AllocStringUni(s) : mm.AllocStringAnsi(s));
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeMemoryHandleExt{TMem}"/> class from a <see cref="SafeAllocatedMemoryHandle"/>
/// instance, copying all the memory.
/// </summary>
/// <param name="source">The source memory block.</param>
protected SafeMemoryHandleExt(SafeAllocatedMemoryHandle source) : base(source) { }
/// <summary>
/// Adds reference to other SafeMemoryHandle objects, the pointer to which are referred to by this object. This is to ensure that
/// such objects being referred to wouldn't be unreferenced until this object is active. For e.g. when this object is an array of
@ -499,8 +588,7 @@ namespace Vanara.InteropServices
/// <param name="children">Collection of SafeMemoryHandle objects referred to by this object.</param>
public void AddSubReference(IEnumerable<ISafeMemoryHandle> children)
{
if (references == null)
references = new List<ISafeMemoryHandle>();
references ??= new List<ISafeMemoryHandle>();
references.AddRange(children);
}
@ -518,8 +606,8 @@ namespace Vanara.InteropServices
//if (Size < Marshal.SizeOf(typeof(T)) * count + prefixBytes)
// throw new InsufficientMemoryException("Requested array is larger than the memory allocated.");
//if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
return handle.ToArray<T>(count, prefixBytes, sz);
//Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
return CallLocked(p => p.ToArray<T>(count, prefixBytes, sz));
}
/// <summary>
@ -532,12 +620,21 @@ namespace Vanara.InteropServices
/// <returns>An enumeration of structures of <typeparamref name="T"/>.</returns>
public IEnumerable<T> ToEnumerable<T>(int count, int prefixBytes = 0)
{
if (IsInvalid) return new T[0];
if (IsInvalid) yield break;
//if (Size < Marshal.SizeOf(typeof(T)) * count + prefixBytes)
// throw new InsufficientMemoryException("Requested array is larger than the memory allocated.");
//if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
return handle.ToIEnum<T>(count, prefixBytes, sz);
//Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
try
{
Lock();
foreach (var i in handle.ToIEnum<T>(count, prefixBytes, sz))
yield return i;
}
finally
{
Unlock();
}
}
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
@ -553,7 +650,7 @@ namespace Vanara.InteropServices
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
public string ToString(int len, int prefixBytes, CharSet charSet = CharSet.Unicode)
{
var str = StringHelper.GetString(handle.Offset(prefixBytes), charSet, sz == 0 ? long.MaxValue : sz - prefixBytes);
var str = CallLocked(p => StringHelper.GetString(p.Offset(prefixBytes), charSet, sz == 0 ? long.MaxValue : sz - prefixBytes));
return len == -1 ? str : str.Substring(0, Math.Min(len, str.Length));
}
@ -565,7 +662,8 @@ namespace Vanara.InteropServices
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
/// <returns>Enumeration of strings.</returns>
public IEnumerable<string> ToStringEnum(int count, CharSet charSet = CharSet.Auto, int prefixBytes = 0) => IsInvalid ? new string[0] : handle.ToStringEnum(count, charSet, prefixBytes, sz);
public IEnumerable<string> ToStringEnum(int count, CharSet charSet = CharSet.Auto, int prefixBytes = 0) =>
IsInvalid ? new string[0] : CallLocked(p => p.ToStringEnum(count, charSet, prefixBytes, sz));
/// <summary>
/// Gets an enumerated list of strings from a block of unmanaged memory where each string is separated by a single '\0' character
@ -574,7 +672,8 @@ namespace Vanara.InteropServices
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
/// <returns>An enumerated list of strings.</returns>
public IEnumerable<string> ToStringEnum(CharSet charSet = CharSet.Auto, int prefixBytes = 0) => IsInvalid ? new string[0] : handle.ToStringEnum(charSet, prefixBytes, sz);
public IEnumerable<string> ToStringEnum(CharSet charSet = CharSet.Auto, int prefixBytes = 0) =>
IsInvalid ? new string[0] : CallLocked(p => p.ToStringEnum(charSet, prefixBytes, sz));
/// <summary>
/// Marshals data from this block of memory to a newly allocated managed object of the type specified by a generic type parameter.
@ -585,7 +684,7 @@ namespace Vanara.InteropServices
public T ToStructure<T>(int prefixBytes = 0)
{
if (IsInvalid) return default;
return handle.ToStructure<T>(sz, prefixBytes);
return CallLocked(p => p.ToStructure<T>(sz, prefixBytes));
}
/// <summary>Marshals data from a managed list of specified type to an offset within this allocated memory.</summary>
@ -610,7 +709,7 @@ namespace Vanara.InteropServices
if (sz < reqSz)
Size = reqSz;
}
handle.Write(items, offset, sz);
CallLocked(p => p.Write(items, offset, sz));
}
/// <summary>Writes the specified value to an offset within this allocated memory.</summary>
@ -630,7 +729,8 @@ namespace Vanara.InteropServices
if (sz < reqSz)
Size = reqSz;
}
handle.Write(value, offset, sz);
try { Lock(); handle.Write(value, offset, sz); }
finally { Unlock(); }
}
/// <summary>Writes the specified value to an offset within this allocated memory.</summary>
@ -650,7 +750,7 @@ namespace Vanara.InteropServices
if (sz < reqSz)
Size = reqSz;
}
handle.Write(value, offset, sz);
CallLocked(p => p.Write(value, offset, sz));
}
/// <summary>When overridden in a derived class, executes the code required to free the handle.</summary>

View File

@ -1588,6 +1588,10 @@ namespace Vanara.PInvoke
{
private bool self = false;
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
bool ISimpleMemoryMethods.Lockable => false;
/// <summary>Gets a handle to a memory allocation of the specified size.</summary>
/// <param name="size">The size, in bytes, of memory to allocate.</param>
/// <returns>A memory handle.</returns>
@ -1604,7 +1608,7 @@ namespace Vanara.PInvoke
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <param name="hMem">A memory handle.</param>
public void UnlockMem(IntPtr hMem) { }
public bool UnlockMem(IntPtr hMem) => false;
}
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
using Vanara.PInvoke;
namespace Vanara.PInvoke
{
public static partial class Kernel32
{
/// <summary>A <see cref="SafeHandle"/> for memory allocated as moveable HGLOBAL.</summary>
/// <seealso cref="System.Runtime.InteropServices.SafeHandle"/>
public class SafeMoveableHGlobalHandle : SafeMemoryHandleExt<Kernel32.MoveableHGlobalMemoryMethods>
{
/// <summary>Initializes a new instance of the <see cref="SafeMoveableHGlobalHandle"/> class.</summary>
/// <param name="handle">The handle.</param>
/// <param name="size">The size of memory allocated to the handle, in bytes.</param>
/// <param name="ownsHandle">if set to <c>true</c> if this class is responsible for freeing the memory on disposal.</param>
public SafeMoveableHGlobalHandle(IntPtr handle, SizeT size, bool ownsHandle = true) : base(handle, size, ownsHandle) { }
/// <summary>Initializes a new instance of the <see cref="SafeMoveableHGlobalHandle"/> class.</summary>
/// <param name="size">The size of memory to allocate, in bytes.</param>
/// <exception cref="System.ArgumentOutOfRangeException">size - The value of this argument must be non-negative</exception>
public SafeMoveableHGlobalHandle(SizeT size) : base(size) { }
/// <summary>
/// Allocates from unmanaged memory to represent an array of pointers and marshals the unmanaged pointers (IntPtr) to the native
/// array equivalent.
/// </summary>
/// <param name="bytes">Array of unmanaged pointers</param>
/// <returns>SafeMoveableHGlobalHandle object to an native (unmanaged) array of pointers</returns>
public SafeMoveableHGlobalHandle(byte[] bytes) : base(bytes?.Length ?? 0)
{
if (Size == 0) return;
CallLocked(p => Marshal.Copy(bytes, 0, p, sz));
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeMoveableHGlobalHandle"/> class from a <see cref="SafeAllocatedMemoryHandle"/>
/// instance, copying all the memory.
/// </summary>
/// <param name="source">The source memory block.</param>
public SafeMoveableHGlobalHandle(SafeAllocatedMemoryHandle source) : base(source) { }
/// <summary>Initializes a new instance of the <see cref="SafeMoveableHGlobalHandle"/> class.</summary>
[ExcludeFromCodeCoverage]
internal SafeMoveableHGlobalHandle() : base(0) { }
/// <summary>Represents a NULL memory pointer.</summary>
public static SafeMoveableHGlobalHandle Null { get; } = new SafeMoveableHGlobalHandle(IntPtr.Zero, 0, false);
/// <summary>
/// Allocates from unmanaged memory to represent a structure with a variable length array at the end and marshal these structure
/// elements. It is the callers responsibility to marshal what precedes the trailing array into the unmanaged memory. ONLY
/// structures with attribute StructLayout of LayoutKind.Sequential are supported.
/// </summary>
/// <typeparam name="T">Type of the trailing array of structures</typeparam>
/// <param name="values">Collection of structure objects</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing array of structures</param>
/// <returns><see cref="SafeMoveableHGlobalHandle"/> object to an native (unmanaged) structure with a trail array of structures</returns>
public static SafeMoveableHGlobalHandle CreateFromList<T>(IEnumerable<T> values, int prefixBytes = 0) =>
new(InteropExtensions.MarshalToPtr(values, mm.AllocMem, out int s, prefixBytes, mm.LockMem, mm.UnlockMem), s);
/// <summary>Allocates from unmanaged memory sufficient memory to hold an array of strings.</summary>
/// <param name="values">The list of strings.</param>
/// <param name="packing">The packing type for the strings.</param>
/// <param name="charSet">The character set to use for the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing strings.</param>
/// <returns>
/// <see cref="SafeMoveableHGlobalHandle"/> object to an native (unmanaged) array of strings stored using the <paramref
/// name="packing"/> model and the character set defined by <paramref name="charSet"/>.
/// </returns>
public static SafeMoveableHGlobalHandle CreateFromStringList(IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated,
CharSet charSet = CharSet.Auto, int prefixBytes = 0) => new(InteropExtensions.MarshalToPtr(values, packing, mm.AllocMem, out int s, charSet, prefixBytes, mm.LockMem, mm.UnlockMem), s);
/// <summary>Allocates from unmanaged memory sufficient memory to hold an object of type T.</summary>
/// <typeparam name="T">Native type</typeparam>
/// <param name="value">The value.</param>
/// <returns><see cref="SafeMoveableHGlobalHandle"/> object to an native (unmanaged) memory block the size of T.</returns>
public static SafeMoveableHGlobalHandle CreateFromStructure<T>(in T value = default) =>
new(InteropExtensions.MarshalToPtr(value, mm.AllocMem, out int s, 0, mm.LockMem, mm.UnlockMem), s);
/// <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>
public static implicit operator SafeMoveableHGlobalHandle(IntPtr ptr) => new(ptr, 0, true);
}
}
}

View File

@ -723,14 +723,14 @@ namespace Vanara.PInvoke
[StructLayout(LayoutKind.Sequential)]
public struct HGLOBAL : IHandle
{
private IntPtr handle;
private readonly IntPtr handle;
/// <summary>Initializes a new instance of the <see cref="HGLOBAL"/> struct.</summary>
/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
public HGLOBAL(IntPtr preexistingHandle) => handle = preexistingHandle;
/// <summary>Returns an invalid handle by instantiating a <see cref="HGLOBAL"/> object with <see cref="IntPtr.Zero"/>.</summary>
public static HGLOBAL NULL => new HGLOBAL(IntPtr.Zero);
public static HGLOBAL NULL => new(IntPtr.Zero);
/// <summary>Gets a value indicating whether this instance is a null handle.</summary>
public bool IsNull => handle == IntPtr.Zero;
@ -743,7 +743,7 @@ namespace Vanara.PInvoke
/// <summary>Performs an implicit conversion from <see cref="IntPtr"/> to <see cref="HGLOBAL"/>.</summary>
/// <param name="h">The pointer to a handle.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator HGLOBAL(IntPtr h) => new HGLOBAL(h);
public static implicit operator HGLOBAL(IntPtr h) => new(h);
/// <summary>Implements the operator !=.</summary>
/// <param name="h1">The first handle.</param>
@ -758,7 +758,7 @@ namespace Vanara.PInvoke
public static bool operator ==(HGLOBAL h1, HGLOBAL h2) => h1.Equals(h2);
/// <inheritdoc/>
public override bool Equals(object obj) => obj is HGLOBAL h ? handle == h.handle : false;
public override bool Equals(object obj) => obj is HGLOBAL h && handle == h.handle;
/// <inheritdoc/>
public override int GetHashCode() => handle.GetHashCode();
@ -771,14 +771,14 @@ namespace Vanara.PInvoke
[StructLayout(LayoutKind.Sequential)]
public struct HLOCAL : IHandle
{
private IntPtr handle;
private readonly IntPtr handle;
/// <summary>Initializes a new instance of the <see cref="HLOCAL"/> struct.</summary>
/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
public HLOCAL(IntPtr preexistingHandle) => handle = preexistingHandle;
/// <summary>Returns an invalid handle by instantiating a <see cref="HLOCAL"/> object with <see cref="IntPtr.Zero"/>.</summary>
public static HLOCAL NULL => new HLOCAL(IntPtr.Zero);
public static HLOCAL NULL => new(IntPtr.Zero);
/// <summary>Gets a value indicating whether this instance is a null handle.</summary>
public bool IsNull => handle == IntPtr.Zero;
@ -791,7 +791,7 @@ namespace Vanara.PInvoke
/// <summary>Performs an implicit conversion from <see cref="IntPtr"/> to <see cref="HLOCAL"/>.</summary>
/// <param name="h">The pointer to a handle.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator HLOCAL(IntPtr h) => new HLOCAL(h);
public static implicit operator HLOCAL(IntPtr h) => new(h);
/// <summary>Implements the operator !=.</summary>
/// <param name="h1">The first handle.</param>
@ -806,7 +806,7 @@ namespace Vanara.PInvoke
public static bool operator ==(HLOCAL h1, HLOCAL h2) => h1.Equals(h2);
/// <inheritdoc/>
public override bool Equals(object obj) => obj is HLOCAL h ? handle == h.handle : false;
public override bool Equals(object obj) => obj is HLOCAL h && handle == h.handle;
/// <inheritdoc/>
public override int GetHashCode() => handle.GetHashCode();
@ -820,6 +820,10 @@ namespace Vanara.PInvoke
/// <seealso cref="MemoryMethodsBase" />
public sealed class MoveableHGlobalMemoryMethods : MemoryMethodsBase
{
/// <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;
/// <summary>Gets a static instance of these methods.</summary>
public static readonly IMemoryMethods Instance = new MoveableHGlobalMemoryMethods();
@ -843,9 +847,12 @@ namespace Vanara.PInvoke
/// <returns>A memory handle.</returns>
public override IntPtr ReAllocMem(IntPtr hMem, int size) => Win32Error.ThrowLastErrorIfNull((IntPtr)GlobalReAlloc(hMem, size, GMEM.GMEM_MOVEABLE | GMEM.GMEM_ZEROINIT));
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <summary>
/// Unlocks the memory of a specified handle.
/// </summary>
/// <param name="hMem">A memory handle.</param>
public override void UnlockMem(IntPtr hMem) => GlobalUnlock(hMem);
/// <returns></returns>
public override bool UnlockMem(IntPtr hMem) => GlobalUnlock(hMem);
}
}
}

View File

@ -31,7 +31,7 @@ namespace Vanara.InteropServices
/// <seealso cref="System.Runtime.InteropServices.ICustomMarshaler"/>
public abstract class GenericStringMarshalerBase<TMem> : ICustomMarshaler where TMem : ISimpleMemoryMethods, new()
{
private static TMem mem = new TMem();
private static readonly TMem mem = new();
private readonly CharSet charSet;
/// <summary>Initializes a new instance of the <see cref="GenericStringMarshalerBase{TMem}"/> class.</summary>

View File

@ -1,10 +1,13 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Vanara.Extensions;
using Vanara.InteropServices;
using static Vanara.PInvoke.Kernel32;
using static Vanara.PInvoke.Ole32;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
namespace Vanara.PInvoke
@ -80,6 +83,91 @@ namespace Vanara.PInvoke
FD_UNICODE = 0x80000000,
}
private static object GetData(this IDataObject dataObj, uint formatId, DVASPECT aspect = DVASPECT.DVASPECT_CONTENT, int index = -1)
{
TYMED tymed = 0;
switch (formatId)
{
default:
throw new NotImplementedException();
}
FORMATETC formatetc = new()
{
cfFormat = unchecked((short)(ushort)formatId),
dwAspect = aspect,
lindex = index,
tymed = tymed
};
dataObj.GetData(ref formatetc, out var medium);
throw new NotImplementedException();
}
/// <summary>Transfer a data stream to an object that contains a data source.</summary>
/// <param name="dataObj">The data object.</param>
/// <param name="formatId">Specifies the particular clipboard format of interest.</param>
/// <param name="obj">The object to add.</param>
/// <param name="aspect">
/// Indicates how much detail should be contained in the rendering. This parameter should be one of the DVASPECT enumeration
/// values. A single clipboard format can support multiple aspects or views of the object. Most data and presentation transfer
/// and caching methods pass aspect information. For example, a caller might request an object's iconic picture, using the
/// metafile clipboard format to retrieve it. Note that only one DVASPECT value can be used in dwAspect. That is, dwAspect cannot
/// be the result of a Boolean OR operation on several DVASPECT values.
/// </param>
/// <param name="index">
/// Part of the aspect when the data must be split across page boundaries. The most common value is -1, which identifies all of
/// the data. For the aspects DVASPECT_THUMBNAIL and DVASPECT_ICON, lindex is ignored.
/// </param>
public static void SetData(this IDataObject dataObj, uint formatId, object obj, DVASPECT aspect = DVASPECT.DVASPECT_CONTENT, int index = -1)
{
TYMED tymed = 0;
IntPtr mbr = default;
switch (obj)
{
case null:
tymed = TYMED.TYMED_NULL;
break;
case byte[] bytes:
tymed = TYMED.TYMED_HGLOBAL;
var hgmb = new SafeMoveableHGlobalHandle(bytes);
mbr = hgmb.TakeOwnership();
break;
case IStream str:
tymed = TYMED.TYMED_ISTREAM;
mbr = Marshal.GetIUnknownForObject(str);
break;
case IStorage store:
tymed = TYMED.TYMED_ISTORAGE;
mbr = Marshal.GetIUnknownForObject(store);
break;
case SafeAllocatedMemoryHandle h:
tymed = TYMED.TYMED_HGLOBAL;
var hgm = new SafeMoveableHGlobalHandle(h);
mbr = hgm.TakeOwnership();
break;
case HBITMAP hbmp:
tymed = TYMED.TYMED_GDI;
mbr = (IntPtr)hbmp;
break;
case HMETAFILE hmeta:
tymed = TYMED.TYMED_MFPICT;
mbr = (IntPtr)hmeta;
break;
case HENHMETAFILE henh:
tymed = TYMED.TYMED_ENHMF;
mbr = (IntPtr)henh;
break;
}
FORMATETC formatetc = new()
{
cfFormat = unchecked((short)(ushort)formatId),
dwAspect = aspect,
lindex = index,
tymed = tymed
};
STGMEDIUM medium = new() { tymed = tymed, unionmember = mbr };
dataObj.SetData(ref formatetc, ref medium, true);
}
/// <summary>
/// <para>
/// Used with the CFSTR_SHELLIDLIST clipboard format to transfer the pointer to an item identifier list (PIDL) of one or more Shell
@ -203,6 +291,18 @@ namespace Vanara.PInvoke
/// </summary>
[MarshalAs(UnmanagedType.Bool)]
public bool fWide;
/// <summary>
/// Gets the file name array appended to this struture. It consists of a series of strings, each containing one file's fully
/// qualified path. This method should only be called when the <see cref="DROPFILES"/> instance is a reference value pulled
/// from the clipboard's HGLOBAL allocation.
/// </summary>
/// <returns>The file list.</returns>
public string[] DangerousGetFileList()
{
using var pinned = new PinnedObject(this);
return ((IntPtr)pinned).ToStringEnum(fWide ? CharSet.Unicode : CharSet.Ansi, (int)pFiles).ToArray();
}
}
/// <summary>

View File

@ -26,7 +26,7 @@ namespace Vanara.PInvoke
{
SHIL_COUNT = Enum.GetValues(typeof(SHIL)).Length;
g_rgshil = new Dictionary<SHIL, ushort>(SHIL_COUNT); // new ushort[SHIL_COUNT];
var sysCxIco = GetSystemMetrics(SystemMetric.SM_CXICON);
int sysCxIco = GetSystemMetrics(SystemMetric.SM_CXICON);
g_rgshil[SHIL.SHIL_LARGE] = (ushort)(int)Microsoft.Win32.Registry.CurrentUser.GetValue($"{REGSTR_PATH_METRICS}\\Shell Icon Size", sysCxIco);
g_rgshil[SHIL.SHIL_SMALL] = (ushort)(int)Microsoft.Win32.Registry.CurrentUser.GetValue($"{REGSTR_PATH_METRICS}\\Shell Small Icon Size", sysCxIco / 2);
g_rgshil[SHIL.SHIL_EXTRALARGE] = (ushort)(3 * sysCxIco / 2);
@ -44,10 +44,10 @@ namespace Vanara.PInvoke
/// <param name="bindFlags">Flags that control aspects of moniker binding operations.</param>
public static IBindCtx CreateBindCtx(STGM openMode = STGM.STGM_READWRITE, TimeSpan timeout = default, BIND_FLAGS bindFlags = 0)
{
Ole32.CreateBindCtx(0, out var ctx).ThrowIfFailed();
Ole32.CreateBindCtx(0, out IBindCtx ctx).ThrowIfFailed();
if (openMode != STGM.STGM_READWRITE || timeout != TimeSpan.Zero || bindFlags != 0)
{
var opts = new BIND_OPTS { cbStruct = Marshal.SizeOf(typeof(BIND_OPTS)), grfMode = (int)openMode, dwTickCountDeadline = (int)timeout.TotalMilliseconds, grfFlags = (int)bindFlags };
BIND_OPTS opts = new() { cbStruct = Marshal.SizeOf(typeof(BIND_OPTS)), grfMode = (int)openMode, dwTickCountDeadline = (int)timeout.TotalMilliseconds, grfFlags = (int)bindFlags };
ctx.SetBindOptions(ref opts);
}
return ctx;
@ -65,8 +65,11 @@ namespace Vanara.PInvoke
public static KNOWNFOLDERID GetKnownFolderFromPath(string path)
{
if (Environment.OSVersion.Version.Major < 6)
{
return Enum.GetValues(typeof(KNOWNFOLDERID)).Cast<KNOWNFOLDERID>().Single(k => string.Equals(k.FullPath(), path, StringComparison.InvariantCultureIgnoreCase));
var ikfm = new IKnownFolderManager();
}
IKnownFolderManager ikfm = new();
return GetKnownFolderFromGuid(ikfm.FindFolderFromPath(path, FFFP_MODE.FFFP_EXACTMATCH).GetId());
}
@ -82,11 +85,16 @@ namespace Vanara.PInvoke
public static string GetPathForKnownFolder(Guid knownFolder)
{
if (knownFolder == default)
{
return null;
}
var pathBuilder = new StringBuilder(260);
StringBuilder pathBuilder = new(260);
if (SHGetFolderPathEx(knownFolder, 0, HTOKEN.NULL, pathBuilder, (uint)pathBuilder.Capacity).Failed)
{
return null;
}
return pathBuilder.ToString();
}
@ -103,7 +111,9 @@ namespace Vanara.PInvoke
public static IShellItem GetShellItemForPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return null;
}
// Handle case of a 'shell' URI and convert GUID to known folder
//if (path.StartsWith("shell:::", StringComparison.InvariantCultureIgnoreCase))
@ -117,11 +127,11 @@ namespace Vanara.PInvoke
// path = fullPath;
//}
var hr = SHCreateItemFromParsingName(path, null, typeof(IShellItem).GUID, out var unk);
HRESULT hr = SHCreateItemFromParsingName(path, null, typeof(IShellItem).GUID, out object unk);
if (hr == (HRESULT)(Win32Error)Win32Error.ERROR_FILE_NOT_FOUND)
{
using var ibc = InteropServices.ComReleaserFactory.Create(CreateBindCtx());
var bd = new IntFileSysBindData();
using InteropServices.ComReleaser<IBindCtx> ibc = InteropServices.ComReleaserFactory.Create(CreateBindCtx());
IntFileSysBindData bd = new();
ibc.Item.RegisterObjectParam(STR_FILE_SYS_BIND_DATA, bd);
return SHCreateItemFromParsingName<IShellItem>(path, ibc.Item);
}
@ -138,7 +148,7 @@ namespace Vanara.PInvoke
public static HRESULT LoadIconFromExtractIcon(IShellFolder psf, PIDL pidl, ref uint imgSz, out SafeHICON hico)
{
hico = default;
HRESULT hr = psf.GetUIObjectOf<IExtractIconW>((IntPtr)pidl, out var ieiw);
HRESULT hr = psf.GetUIObjectOf((IntPtr)pidl, out IExtractIconW ieiw);
if (hr.Succeeded)
{
try
@ -150,7 +160,7 @@ namespace Vanara.PInvoke
Marshal.ReleaseComObject(ieiw);
}
}
else if ((hr = psf.GetUIObjectOf<IExtractIconA>((IntPtr)pidl, out var iei)).Succeeded)
else if ((hr = psf.GetUIObjectOf((IntPtr)pidl, out IExtractIconA iei)).Succeeded)
{
try
{
@ -171,17 +181,24 @@ namespace Vanara.PInvoke
/// <returns>The result of function.</returns>
public static HRESULT LoadIconFromExtractIcon(IExtractIconW iei, ref uint imgSz, out SafeHICON hico)
{
var szIconFile = new StringBuilder(Kernel32.MAX_PATH);
var hr = iei.GetIconLocation(GetIconLocationFlags.GIL_FORSHELL, szIconFile, szIconFile.Capacity, out var iIdx, out _);
StringBuilder szIconFile = new(Kernel32.MAX_PATH);
HRESULT hr = iei.GetIconLocation(GetIconLocationFlags.GIL_FORSHELL, szIconFile, szIconFile.Capacity, out int iIdx, out _);
if (hr.Succeeded)
{
if (szIconFile.ToString() != "*")
{
hr = iei.Extract(szIconFile.ToString(), (uint)iIdx, (ushort)imgSz, out hico);
}
else
{
hr = LoadIconFromSystemImageList(iIdx, ref imgSz, out hico);
}
}
else
{
hico = null;
}
return hr;
}
@ -192,17 +209,24 @@ namespace Vanara.PInvoke
/// <returns>The result of function.</returns>
public static HRESULT LoadIconFromExtractIcon(IExtractIconA iei, ref uint imgSz, out SafeHICON hico)
{
var szIconFile = new StringBuilder(Kernel32.MAX_PATH);
var hr = iei.GetIconLocation(GetIconLocationFlags.GIL_FORSHELL, szIconFile, szIconFile.Capacity, out var iIdx, out _);
StringBuilder szIconFile = new(Kernel32.MAX_PATH);
HRESULT hr = iei.GetIconLocation(GetIconLocationFlags.GIL_FORSHELL, szIconFile, szIconFile.Capacity, out int iIdx, out _);
if (hr.Succeeded)
{
if (szIconFile.ToString() != "*")
{
hr = iei.Extract(szIconFile.ToString(), (uint)iIdx, (ushort)imgSz, out hico);
}
else
{
hr = LoadIconFromSystemImageList(iIdx, ref imgSz, out hico);
}
}
else
{
hico = null;
}
return hr;
}
@ -214,15 +238,17 @@ namespace Vanara.PInvoke
public static HRESULT LoadIconFromSystemImageList(int iIdx, ref uint imgSz, out SafeHICON hico)
{
HRESULT hr;
if ((hr = SHGetImageList(PixelsToSHIL((int)imgSz), typeof(IImageList).GUID, out var obj)).Succeeded)
if ((hr = SHGetImageList(PixelsToSHIL((int)imgSz), typeof(IImageList).GUID, out object obj)).Succeeded)
{
try
{
var il = (IImageList)obj;
IImageList il = (IImageList)obj;
hico = il.GetIcon(iIdx, IMAGELISTDRAWFLAGS.ILD_TRANSPARENT);
using var icoInfo = new ICONINFO();
using ICONINFO icoInfo = new();
if (GetIconInfo(hico, icoInfo))
{
imgSz = (uint)GetObject<BITMAP>(icoInfo.hbmColor).bmWidth;
}
}
catch (COMException e)
{
@ -235,7 +261,10 @@ namespace Vanara.PInvoke
}
}
else
{
hico = default;
}
return hr;
}
@ -247,19 +276,22 @@ namespace Vanara.PInvoke
/// <returns>The result of function.</returns>
public static HRESULT LoadImageFromExtractImage(IShellFolder psf, PIDL pidl, ref uint imgSz, out SafeHBITMAP hbmp)
{
HRESULT hr = psf.GetUIObjectOf<IExtractImage>((IntPtr)pidl, out var iei);
HRESULT hr = psf.GetUIObjectOf((IntPtr)pidl, out IExtractImage iei);
hbmp = default;
if (hr.Succeeded)
{
try
{
var szIconFile = new StringBuilder(Kernel32.MAX_PATH);
var sz = new SIZE((int)imgSz, (int)imgSz);
StringBuilder szIconFile = new(Kernel32.MAX_PATH);
SIZE sz = new((int)imgSz, (int)imgSz);
IEIFLAG flags = 0;
if ((hr = iei.GetLocation(szIconFile, (uint)szIconFile.Capacity, default, ref sz, 0, ref flags)).Succeeded &&
szIconFile.Length > 0 &&
(hr = iei.Extract(out hbmp)).Succeeded)
{
imgSz = (uint)sz.cx;
}
return hr;
}
finally
@ -279,7 +311,7 @@ namespace Vanara.PInvoke
{
try
{
var itp = psi.BindToHandler<IThumbnailProvider>(null, BHID.BHID_ThumbnailHandler);
IThumbnailProvider itp = psi.BindToHandler<IThumbnailProvider>(null, BHID.BHID_ThumbnailHandler);
return LoadImageFromThumbnailProvider(itp, ref imgSz, out hbmp);
}
catch (COMException e)
@ -297,11 +329,16 @@ namespace Vanara.PInvoke
/// <returns>The result of function.</returns>
public static HRESULT LoadImageFromThumbnailProvider(IShellFolder psf, PIDL pidl, ref uint imgSz, out SafeHBITMAP hbmp)
{
HRESULT hr = psf.GetUIObjectOf<IThumbnailProvider>((IntPtr)pidl, out var itp);
HRESULT hr = psf.GetUIObjectOf((IntPtr)pidl, out IThumbnailProvider itp);
if (hr.Succeeded)
{
hr = LoadImageFromThumbnailProvider(itp, ref imgSz, out hbmp);
}
else
{
hbmp = null;
}
return hr;
}
@ -314,9 +351,12 @@ namespace Vanara.PInvoke
{
try
{
var hr = itp.GetThumbnail(imgSz, out hbmp, out _);
HRESULT hr = itp.GetThumbnail(imgSz, out hbmp, out _);
if (hr.Succeeded)
{
imgSz = (uint)GetObject<BITMAP>(hbmp).bmWidth;
}
return hr;
}
finally
@ -336,10 +376,16 @@ namespace Vanara.PInvoke
/// <returns>The returned interface.</returns>
public static object QueryInterface(in object iUnk, in Guid riid)
{
QueryInterface(iUnk, riid, out var ppv).ThrowIfFailed();
QueryInterface(iUnk, riid, out object ppv).ThrowIfFailed();
return ppv;
}
/// <summary>Requests a specified interface from a COM object.</summary>
/// <typeparam name="T">The interface type for which to query and return.</typeparam>
/// <param name="iUnk">The interface to be queried.</param>
/// <returns>The returned interface.</returns>
public static T QueryInterface<T>(in object iUnk) where T : class => (T)QueryInterface(iUnk, typeof(T).GUID);
/// <summary>Requests a specified interface from a COM object.</summary>
/// <param name="iUnk">The interface to be queried.</param>
/// <param name="riid">The interface identifier (IID) of the requested interface.</param>
@ -347,8 +393,8 @@ namespace Vanara.PInvoke
/// <returns>An HRESULT that indicates the success or failure of the call.</returns>
public static HRESULT QueryInterface(in object iUnk, in Guid riid, out object ppv)
{
var tmp = riid;
HRESULT hr = Marshal.QueryInterface(Marshal.GetIUnknownForObject(iUnk), ref tmp, out var ippv);
Guid tmp = riid;
HRESULT hr = Marshal.QueryInterface(Marshal.GetIUnknownForObject(iUnk), ref tmp, out IntPtr ippv);
ppv = hr.Succeeded ? Marshal.GetObjectForIUnknown(ippv) : null;
System.Diagnostics.Debug.WriteLine($"Successful QI:\t{riid}");
return hr;
@ -416,7 +462,7 @@ namespace Vanara.PInvoke
public ManualParentAndItem(IShellItem psi)
{
psf = psi.BindToHandler<IShellFolder>(null, BHID.BHID_SFObject);
SHGetIDListFromObject(psi, out var pItem).ThrowIfFailed();
SHGetIDListFromObject(psi, out PIDL pItem).ThrowIfFailed();
pChild = pItem.LastId;
pItem.Dispose();
}

View File

@ -11,7 +11,7 @@ namespace Vanara.Extensions.Tests
[TestFixture()]
public class InteropExtensionsTests
{
private readonly int i = Marshal.SizeOf(typeof(int));
private readonly int intSz = Marshal.SizeOf(typeof(int));
[Test()]
public void IsBlittableTest()
@ -45,13 +45,13 @@ namespace Vanara.Extensions.Tests
[Test()]
public void MarshalToPtrTest()
{
var h = new SafeHGlobalHandle(Marshal.SizeOf(typeof(RECT)) * 2 + i);
var h = new SafeHGlobalHandle(Marshal.SizeOf(typeof(RECT)) * 2 + intSz);
var rs = new[] { new RECT(), new RECT(10, 11, 12, 13) };
((IntPtr)h).Write(rs, i, h.Size);
Assert.That(Marshal.ReadInt32((IntPtr)h, 4 * i) == 0);
Assert.That(Marshal.ReadInt32((IntPtr)h, 5 * i) == 10);
Assert.That(Marshal.ReadInt32((IntPtr)h, 7 * i) == 12);
var ro = ((IntPtr)h).ToArray<RECT>(2, i);
((IntPtr)h).Write(rs, intSz, h.Size);
Assert.That(Marshal.ReadInt32((IntPtr)h, 4 * intSz) == 0);
Assert.That(Marshal.ReadInt32((IntPtr)h, 5 * intSz) == 10);
Assert.That(Marshal.ReadInt32((IntPtr)h, 7 * intSz) == 12);
var ro = ((IntPtr)h).ToArray<RECT>(2, intSz);
Assert.That(ro.Length == 2);
Assert.That(ro[0].left == 0);
Assert.That(ro[1].right == 12);
@ -64,23 +64,23 @@ namespace Vanara.Extensions.Tests
try
{
var rs = new[] { new RECT(), new RECT(10, 11, 12, 13) };
h = rs.MarshalToPtr(Marshal.AllocHGlobal, out var a, i);
h = rs.MarshalToPtr(Marshal.AllocHGlobal, out var a, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
Assert.That(a, Is.EqualTo(Marshal.SizeOf(typeof(RECT)) * rs.Length + i));
Assert.That(Marshal.ReadInt32(h, 4 * i) == 0);
Assert.That(Marshal.ReadInt32(h, 5 * i) == 10);
Assert.That(Marshal.ReadInt32(h, 7 * i) == 12);
var ro = h.ToArray<RECT>(rs.Length, i);
Assert.That(a, Is.EqualTo(Marshal.SizeOf(typeof(RECT)) * rs.Length + intSz));
Assert.That(Marshal.ReadInt32(h, 4 * intSz) == 0);
Assert.That(Marshal.ReadInt32(h, 5 * intSz) == 10);
Assert.That(Marshal.ReadInt32(h, 7 * intSz) == 12);
var ro = h.ToArray<RECT>(rs.Length, intSz);
Assert.That(ro.Length == 2);
Assert.That(ro[0].left == 0);
Assert.That(ro[1].right == 12);
Marshal.FreeHGlobal(h);
h = new RECT[0].MarshalToPtr(Marshal.AllocHGlobal, out a, i);
h = new RECT[0].MarshalToPtr(Marshal.AllocHGlobal, out a, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
Assert.That(a, Is.EqualTo(i));
Assert.That(a, Is.EqualTo(intSz));
Assert.That(() => new DateTime[1].MarshalToPtr(Marshal.AllocHGlobal, out a, i), Throws.Exception);
Assert.That(() => new DateTime[1].MarshalToPtr(Marshal.AllocHGlobal, out a, intSz), Throws.Exception);
}
finally
{
@ -95,38 +95,38 @@ namespace Vanara.Extensions.Tests
try
{
var rs = new[] { "str1", "str2", "str3" };
h = rs.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out var a, CharSet.Unicode, i);
h = rs.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out var a, CharSet.Unicode, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
var chSz = 2;
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + chSz + i));
var ro = h.ToArray<byte>(a - i, i);
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + chSz + intSz));
var ro = h.ToArray<byte>(a - intSz, intSz);
var chars = System.Text.Encoding.Unicode.GetChars(ro);
Assert.That(chars.Length, Is.EqualTo((a - i) / chSz));
Assert.That(chars.Length, Is.EqualTo((a - intSz) / chSz));
Assert.That(chars[0], Is.EqualTo('s'));
Assert.That(chars[4], Is.EqualTo('\0'));
Assert.That(chars[chars.Length - 2], Is.EqualTo('\0'));
Assert.That(chars[chars.Length - 1], Is.EqualTo('\0'));
Marshal.FreeHGlobal(h);
h = rs.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Ansi, i);
h = rs.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Ansi, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
chSz = 1;
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + chSz + i));
ro = h.ToArray<byte>(a - i, i);
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + chSz + intSz));
ro = h.ToArray<byte>(a - intSz, intSz);
chars = System.Text.Encoding.ASCII.GetChars(ro);
Assert.That(chars.Length, Is.EqualTo((a - i) / chSz));
Assert.That(chars.Length, Is.EqualTo((a - intSz) / chSz));
Assert.That(chars[0], Is.EqualTo('s'));
Assert.That(chars[4], Is.EqualTo('\0'));
Assert.That(chars[chars.Length - 2], Is.EqualTo('\0'));
Assert.That(chars[chars.Length - 1], Is.EqualTo('\0'));
Marshal.FreeHGlobal(h);
h = new string[0].MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Unicode, i);
h = new string[0].MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Unicode, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
Assert.That(a, Is.EqualTo(i + 2));
Assert.That(a, Is.EqualTo(intSz + 2));
Assert.That(() => new[] { "" }.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Unicode, i), Throws.ArgumentException);
Assert.That(() => new string[] { null }.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Unicode, i), Throws.ArgumentException);
Assert.That(() => new[] { "" }.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Unicode, intSz), Throws.ArgumentException);
Assert.That(() => new string[] { null }.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out a, CharSet.Unicode, intSz), Throws.ArgumentException);
}
finally
{
@ -141,29 +141,29 @@ namespace Vanara.Extensions.Tests
try
{
var rs = new[] { "str1", "str2", "str3" };
h = rs.MarshalToPtr(StringListPackMethod.Packed, Marshal.AllocHGlobal, out var a, CharSet.Unicode, i);
h = rs.MarshalToPtr(StringListPackMethod.Packed, Marshal.AllocHGlobal, out var a, CharSet.Unicode, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
var chSz = 2;
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + ((rs.Length + 1) * IntPtr.Size) + i));
var ro = h.ToIEnum<IntPtr>(rs.Length, i).ToArray();
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + ((rs.Length + 1) * IntPtr.Size) + intSz));
var ro = h.ToIEnum<IntPtr>(rs.Length, intSz).ToArray();
Assert.That(ro, Has.None.EqualTo(IntPtr.Zero));
for (var i = 0; i < ro.Length; i++)
Assert.That(StringHelper.GetString(ro[i], CharSet.Unicode), Is.EqualTo(rs[i]));
Marshal.FreeHGlobal(h);
h = rs.MarshalToPtr(StringListPackMethod.Packed, Marshal.AllocHGlobal, out a, CharSet.Ansi, i);
h = rs.MarshalToPtr(StringListPackMethod.Packed, Marshal.AllocHGlobal, out a, CharSet.Ansi, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
chSz = 1;
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + ((rs.Length + 1) * IntPtr.Size) + i));
ro = h.ToIEnum<IntPtr>(rs.Length, i).ToArray();
Assert.That(a, Is.EqualTo(chSz * (rs[0].Length + 1) * rs.Length + ((rs.Length + 1) * IntPtr.Size) + intSz));
ro = h.ToIEnum<IntPtr>(rs.Length, intSz).ToArray();
Assert.That(ro, Has.None.EqualTo(IntPtr.Zero));
for (var i = 0; i < ro.Length; i++)
Assert.That(StringHelper.GetString(ro[i], CharSet.Ansi), Is.EqualTo(rs[i]));
Marshal.FreeHGlobal(h);
h = new string[0].MarshalToPtr(StringListPackMethod.Packed, Marshal.AllocHGlobal, out a, CharSet.Unicode, i);
h = new string[0].MarshalToPtr(StringListPackMethod.Packed, Marshal.AllocHGlobal, out a, CharSet.Unicode, intSz);
Assert.That(h, Is.Not.EqualTo(IntPtr.Zero));
Assert.That(a, Is.EqualTo(i + IntPtr.Size));
Assert.That(a, Is.EqualTo(intSz + IntPtr.Size));
}
finally
{
@ -177,7 +177,7 @@ namespace Vanara.Extensions.Tests
var rect = new RECT(10, 11, 12, 13);
var ptr = rect.MarshalToPtr(Marshal.AllocCoTaskMem, out var a);
Assert.That(ptr != IntPtr.Zero);
Assert.That(Marshal.ReadInt32(ptr, 1 * i) == 11);
Assert.That(Marshal.ReadInt32(ptr, 1 * intSz) == 11);
Marshal.FreeCoTaskMem(ptr);
}
@ -185,28 +185,28 @@ namespace Vanara.Extensions.Tests
public void ToArrayTest()
{
var rs = new[] { 10, 11, 12, 13, 14 };
var h = SafeHGlobalHandle.CreateFromList(rs, rs.Length, i);
var ro = ((IntPtr)h).ToArray<int>(4, i);
var h = SafeHGlobalHandle.CreateFromList(rs, rs.Length, intSz);
var ro = ((IntPtr)h).ToArray<int>(4, intSz);
Assert.That(ro.Length, Is.EqualTo(4));
Assert.That(ro[2], Is.EqualTo(rs[2]));
Assert.That(((IntPtr)h).ToArray<int>(0, i), Is.Empty);
Assert.That(IntPtr.Zero.ToArray<int>(3, i), Is.Null);
Assert.That(((IntPtr)h).ToArray<int>(0, intSz), Is.Empty);
Assert.That(IntPtr.Zero.ToArray<int>(3, intSz), Is.Null);
}
[Test()]
public void ToIEnumTest()
{
var rs = new[] { 10, 11, 12, 13, 14 };
var h = SafeHGlobalHandle.CreateFromList(rs, rs.Length, i);
var ro = ((IntPtr)h).ToIEnum<int>(4, i);
var h = SafeHGlobalHandle.CreateFromList(rs, rs.Length, intSz);
var ro = ((IntPtr)h).ToIEnum<int>(4, intSz);
var v = 10;
foreach (var rv in ro)
Assert.That(rv, Is.EqualTo(v++));
Assert.That(v, Is.EqualTo(14));
Assert.That(((IntPtr)h).ToIEnum<int>(0, i), Is.Empty);
Assert.That(IntPtr.Zero.ToIEnum<int>(0, i), Is.Empty);
Assert.That(((IntPtr)h).ToIEnum<int>(0, intSz), Is.Empty);
Assert.That(IntPtr.Zero.ToIEnum<int>(0, intSz), Is.Empty);
}
[Test()]
@ -265,62 +265,62 @@ namespace Vanara.Extensions.Tests
public void ToStringEnumTest()
{
var rs = new[] { "str1", "str2", "str3" };
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Concatenated, CharSet.Ansi, i))
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Concatenated, CharSet.Ansi, intSz))
{
var ptr = sa.DangerousGetHandle();
Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero));
var se = ptr.ToStringEnum(CharSet.Ansi, i, sa.Size);
var se = ptr.ToStringEnum(CharSet.Ansi, intSz, sa.Size);
Assert.That(se, Is.EquivalentTo(rs));
Assert.That(() => ptr.ToStringEnum(CharSet.Ansi, i, sa.Size - 5).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Ansi, i, sa.Size - 1).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Ansi, intSz, sa.Size - 5).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Ansi, intSz, sa.Size - 1).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
}
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Concatenated, CharSet.Unicode, i))
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Concatenated, CharSet.Unicode, intSz))
{
var ptr = sa.DangerousGetHandle();
Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero));
var se = ptr.ToStringEnum(CharSet.Unicode, i, sa.Size);
var se = ptr.ToStringEnum(CharSet.Unicode, intSz, sa.Size);
Assert.That(se, Is.EquivalentTo(rs));
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, i, sa.Size - 5).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, i, sa.Size - 1).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, intSz, sa.Size - 5).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, intSz, sa.Size - 1).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
}
using (var sa = SafeHGlobalHandle.CreateFromStringList(null, StringListPackMethod.Concatenated, CharSet.Unicode, i))
using (var sa = SafeHGlobalHandle.CreateFromStringList(null, StringListPackMethod.Concatenated, CharSet.Unicode, intSz))
{
var ptr = sa.DangerousGetHandle();
Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero));
var se = ptr.ToStringEnum(CharSet.Unicode, i, sa.Size);
var se = ptr.ToStringEnum(CharSet.Unicode, intSz, sa.Size);
Assert.That(se, Is.Empty);
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, i, i).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, i, sa.Size - 1).ToArray(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, intSz, intSz).Count(), Throws.TypeOf<InsufficientMemoryException>());
Assert.That(() => ptr.ToStringEnum(CharSet.Unicode, intSz, sa.Size - 1).Count(), Throws.TypeOf<InsufficientMemoryException>());
}
Assert.That(IntPtr.Zero.ToStringEnum(CharSet.Unicode, i), Is.Empty);
Assert.That(IntPtr.Zero.ToStringEnum(CharSet.Unicode, intSz), Is.Empty);
}
[Test()]
public void ToStringEnumTest1()
{
var rs = new[] { "str1", "str2", null, "", "str3" };
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Packed, CharSet.Ansi, i))
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Packed, CharSet.Ansi, intSz))
{
var ptr = sa.DangerousGetHandle();
Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero));
var se = ptr.ToStringEnum(rs.Length, CharSet.Ansi, i);
var se = ptr.ToStringEnum(rs.Length, CharSet.Ansi, intSz);
Assert.That(se, Is.EquivalentTo(rs));
}
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Packed, CharSet.Unicode, i))
using (var sa = SafeHGlobalHandle.CreateFromStringList(rs, StringListPackMethod.Packed, CharSet.Unicode, intSz))
{
var ptr = sa.DangerousGetHandle();
Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero));
var se = ptr.ToStringEnum(rs.Length, CharSet.Unicode, i);
var se = ptr.ToStringEnum(rs.Length, CharSet.Unicode, intSz);
Assert.That(se, Is.EquivalentTo(rs));
}
using (var sa = SafeHGlobalHandle.CreateFromStringList(null, StringListPackMethod.Packed, CharSet.Unicode, i))
using (var sa = SafeHGlobalHandle.CreateFromStringList(null, StringListPackMethod.Packed, CharSet.Unicode, intSz))
{
var ptr = sa.DangerousGetHandle();
Assert.That(ptr, Is.Not.EqualTo(IntPtr.Zero));
var se = ptr.ToStringEnum(0, CharSet.Unicode, i);
var se = ptr.ToStringEnum(0, CharSet.Unicode, intSz);
Assert.That(se, Is.Empty);
}
Assert.That(IntPtr.Zero.ToStringEnum(0, CharSet.Unicode, i), Is.Empty);
Assert.That(IntPtr.Zero.ToStringEnum(0, CharSet.Unicode, intSz), Is.Empty);
}
[TestCase("Some string value")]
@ -381,15 +381,15 @@ namespace Vanara.Extensions.Tests
[Test]
public unsafe void AsUnmanagedArrayPointerTest()
{
var h = new SafeHGlobalHandle(Marshal.SizeOf(typeof(RECT)) * 2 + i);
var rs = new[] { new RECT(), new RECT(10, 11, 12, 13) };
((IntPtr)h).Write(rs, i, h.Size);
var h = new SafeHGlobalHandle(Marshal.SizeOf(typeof(RECT)) * 2 + intSz);
var rs = new[] { new RECT(0, 1, 2, 3), new RECT(10, 11, 12, 13) };
((IntPtr)h).Write(rs, intSz, h.Size);
RECT* r = h.DangerousGetHandle().AsUnmanagedArrayPointer<RECT>(Marshal.SizeOf<RECT>(), i);
Assert.That(r->left, Is.EqualTo(10));
Assert.That(r->top, Is.EqualTo(11));
Assert.That(r->right, Is.EqualTo(12));
Assert.That(r->bottom, Is.EqualTo(13));
RECT* r = h.DangerousGetHandle().AsUnmanagedArrayPointer<RECT>(rs.Length, intSz, h.Size);
Assert.That(r[1].left, Is.EqualTo(10));
Assert.That(r[1].top, Is.EqualTo(11));
Assert.That(r[1].right, Is.EqualTo(12));
Assert.That(r[1].bottom, Is.EqualTo(13));
}
[Test]

View File

@ -1,5 +1,6 @@
using NUnit.Framework;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Vanara.PInvoke;
@ -119,7 +120,7 @@ namespace Vanara.InteropServices.Tests
Assert.That(SafeHGlobalHandle.Null.ToEnumerable<RECT>(0), Is.Empty);
Assert.That(SafeHGlobalHandle.Null.ToEnumerable<Exception>(0), Is.Empty.And.EquivalentTo(new Exception[0]));
Assert.That(() => new SafeHGlobalHandle(5).ToEnumerable<Exception>(0), Throws.ArgumentException);
Assert.That(() => new SafeHGlobalHandle(5).ToEnumerable<Exception>(0).ToArray(), Throws.Exception);
Assert.That(SafeHGlobalHandle.Null.ToStructure<RECT>(), Is.EqualTo(default(RECT)));