-
Notifications
You must be signed in to change notification settings - Fork 222
Manual native memory extensions
Including the namespace Vanara.Extensions provides access to many extensions to existing .NET classes and structures, including extensions for IntPtr. Here are some of the things you can do:
The library can extract structures or classes that have an implied or expressed layout. This includes classes marked with the VanaraMarshalerAttribute.
// Extract a RECT (This is a copy and no longer points to the native memory)
IntPtr ptr = /* a pointer to native memory */;
CharSet charSet = CharSet.Unicode; /* Type of characters in the strings */
int offset = 0; /* number of bytes to offset the beginning of the enumeration */
int memSize = /* number of bytes allocated to ptr */;
RECT r = ptr.ToStructure<RECT>(memSize, offset);
// Get a reference to value types (This reference still points to the native memory)
ref RECT rr = ref ptr.AsRef<RECT>(offset, memSize);This copies the contents of native memory to a managed array.
IntPtr ptr = /* a pointer to native memory */;
int elemCount = /* number of array elements */;
int offset = 0; /* number of bytes to offset the beginning of the enumeration */
int memSize = /* number of bytes allocated to ptr */;
ptr.ToArray<RECT>(elemCount, offset, memSize);You may also just need to enumerate over the memory. You can do this in two ways, with a direct iterator or with a Span. The only difference is that ToIEnum can also handle custom structure marshaling using IVanaraMarshaler.
// Use the iterator
IntPtr ptr = /* a pointer to native memory */;
int elemCount = /* number of array elements */;
int offset = 0; /* number of bytes to offset the beginning of the enumeration */
int memSize = /* number of bytes allocated to ptr */;
foreach (var rect in ptr.ToIEnum<RECT>(elemCount, offset, memSize))
{
// iterate while ptr is still pointing to valid memory
}
// Use Span
foreach (var rect in ptr.AsSpan<RECT>(elemCount, offset, memSize))
{
// iterate while ptr is still pointing to valid memory
}String arrays, natively, take two forms: 1) An array of pointers to string, and 2) An array of strings where each string is terminated with null and the array is terminated with an additional null.
// Array of string pointers
IntPtr ptr = /* a pointer to native memory */;
int elemCount = /* number of array elements */;
CharSet charSet = CharSet.Unicode; /* Type of characters in the strings */
int offset = 0; /* number of bytes to offset the beginning of the enumeration */
int memSize = /* number of bytes allocated to ptr */;
foreach (var rect in ptr.ToStringEnum(elemCount, charSet, offset, memSize))
{
// iterate while ptr is still pointing to valid memory
}
// Array of zero terminated strings
foreach (var rect in ptr.ToStringEnum(charSet, offset, memSize))
{
// iterate while ptr is still pointing to valid memory
}To start, it is important to know that there are multiple allocation schemes in the Windows SDK. Two are supported in .NET: LocalAlloc (using Marshal.AllocHGlobal) and CoTaskMemAlloc (using Marshal.AllocCoTaskMem). Under the hood, since Win8, they both allocate memory from the process heap. Then you have all the heap functions from Kernel32 - HeapCreate/HeapAlloc and others. These are more complex and support locking. On top of these, you have the C/C++ functions too. To accommodate all these, in all the Vanara extensions, you must specify an allocator when creating native memory.
RECT r = new RECT(5, 5, 20, 20);
IntPtr ptr = r.MarshalToPtr(Marshal.AllocCoTaskMem, out int memSize, 0 /*prefixBytes*/, null /*lock method*/, null /*unlock method*/);
// use the ptr
Marshal.FreeCoTaskMem(ptr);RECT[] r = new[] { new RECT(5, 5, 20, 20), new RECT(1, 1, 5, 5), new RECT(50, 50, 200, 200) };
IntPtr ptr = r.MarshalToPtr(Marshal.AllocHGlobal, out int memSize, 0 /*prefixBytes*/,
null /*lock method*/, null /*unlock method*/);
// use the ptr
Marshal.FreeHGlobal(ptr);string[] s = new[] { "ABCD", "EFGH", "IJKL" };
// **Note** the need to supply the packing method and character set
IntPtr ptr = s.MarshalToPtr(StringListPackMethod.Concatenated, Marshal.AllocHGlobal, out int memSize,
CharSet.Unicode, 0 /*prefixBytes*/, null /*lock method*/, null /*unlock method*/);
// use the ptr
Marshal.FreeHGlobal(ptr);Matching all the allocating methods are write-only methods.
const int memSize = 1024;
IntPtr ptr = Marshal.AllocCoTaskMem(memSize);
RECT r = new RECT(5, 5, 20, 20);
ptr.Write(r, 0 /*offset*/, memSize);
RECT[] ra = new[] { new RECT(5, 5, 20, 20), new RECT(1, 1, 5, 5), new RECT(50, 50, 200, 200) };
ptr.Write(ra, 0 /*offset*/, memSize);
string[] s = new[] { "ABCD", "EFGH", "IJKL" };
// **Note** the need to supply the packing method and character set
ptr.Write(StringListPackMethod.Concatenated, CharSet.Ansi, 0 /*offset*/, memSize);