1 Native memory helper classes
David Hall edited this page 2022-08-08 16:03:24 -06:00

Including the namespace Vanara.InteropServices provides access to memory helper classes and generic classes. These classes wrap the capabilities exposed by the manual memory extensions. Here is how to use those classes:

Basics and base classes

IMemoryMethods - Interface to capture different memory allocation schemes

As there are many ways to allocate memory under the Windows SDK, IMemoryMethods provides a means of capturing those schemes for reuse in generic classes. Below is a list of schemes already in the project:

Class NuGet Package
Vanara.InteropServices.CoTaskMemoryMethods Vanara.Core
Vanara.InteropServices.HGlobalMemoryMethods Vanara.Core
Vanara.InteropServices.LocalMemoryMethods Vanara.PInvoke.Kernel32
Vanara.PInvoke.Kernel32.HeapMemoryMethods Vanara.PInvoke.Kernel32
Vanara.PInvoke.Kernel32.MoveableHGlobalMemoryMethods Vanara.PInvoke.Kernel32
Vanara.PInvoke.Crypt32.CryptMemMethods Vanara.PInvoke.Cryptography

If you need to write your own, deriving from Vanara.InteropServices.MemoryMethodsBase may help you with some of the intuited methods.

Memory base classes

When you need native memory and want it to auto-deallocate when it is cleaned up with the garbage collector, there are two main classes: SafeCoTaskMemHandle and SafeHGlobalHandle. In Win8 and later, these are interchangeable. Links to all their base classes are below and I'll cover their use in the next section.

Class Description/Use NuGet Package
Vanara.InteropServices.SafeAllocatedMemoryHandleBase Abstract base class for all SafeHandle derivatives that encapsulate handling unmanaged memory. This class assumes read-only memory. Vanara.Core
Vanara.InteropServices.SafeAllocatedMemoryHandle Abstract base class for all SafeHandle derivatives that encapsulate handling unmanaged memory. Vanara.Core
Vanara.InteropServices.SafeMemoryHandle<TMem> Abstract base class for all SafeAllocatedMemoryHandle derivatives that apply a specific memory handling routine set. Vanara.Core
Vanara.InteropServices.SafeMemoryHandleExt<TMem> Abstract base class for all SafeAllocatedMemoryHandle derivatives that apply an extended memory handling routine set for structures and sequences. Vanara.Core
Vanara.InteropServices.SafeCoTaskMemHandle A SafeHandle for memory allocated via COM. Vanara.Core
Vanara.InteropServices.SafeHGlobalHandle A SafeHandle for memory allocated via LocalAlloc. Vanara.Core

Using a safe native memory handle

Allocate raw memory and then work with it.

// Allocate 1024 bytes of memory and use the "using" statement to force disposal at the end of scope
// All memory is zeroed by the helper class
using SafeCoTaskMemHandle mem = new(1024);
// Notice that "mem" implicitly converts to IntPtr and "mem.Size" will implicitly convert to int, uint, long and ulong.
ReadFile(hFile, mem, mem.Size, out var read, default);
// Extract an array of points
POINT[] pts = mem.ToArray<POINT>(10);
// Extract a structure offset from the base
RECT rect = mem.ToStructure<RECT>(80);
// Pull an offset zero-terminated string
string str = mem.ToString(-1, 96);

Convert an array of bytes to native memory to act on.

byte[] bytes = new byte[1024];
// Change byte array to contain the contents you desire
using SafeCoTaskMemHandle mem = new(bytes);
// Tweak a few more bytes directly in memory by writing an offset structure
mem.Write(new RECT(100,100,200,200), false, 256);
// Write out the memory
WriteFile(hFile, mem, mem.Size, out var written, default);

Using SafeCoTaskMemStruct to interact with structures in native memory

While you can use any SafeAllocatedMemoryHandle derivative to work with structures, SafeCoTaskMemStruct<TStruct> simplifies it with a couple helper methods.

// This autosizes to the size of the structure
using SafeCoTaskMemStruct<RECT> pRect = new();           // Init to 0,0,0,0
pRect.Write(4U, pRect.FieldOffset("top"));                                      // Write to 4th offset = 0,4,0,0
ref RECT rr = ref pRect.AsRef();                         // Get a ref to the memory
rr.bottom = 8;                                           // Write last value = 0,4,0,8
RECT r = pRect.Value;                                    // Copies the memory to a struct
Console.Write($"{r.left},{r.top},{r.right},{r.bottom}"); // outputs 0,4,0,8

using SafeCoTaskMemStruct<RECT> pRect = new(r);           // Init to 0,4,0,8

Using SafeLPSTR, SafeLPTSTR and SafeLPWSTR to interact with strings in native memory

Three distinct classes are used to interact with strings in memory that align to the Windows definitions. You can also use any SafeAllocatedMemoryHandle derivative, but then you'll have to explicitly define the character set. All are derived from SafeMemString<TMem>, which is a SafeMemoryHandle<TMem>, and supports most of the methods and properties of both System.String and System.Text.StringBuilder.

// Create using a string
using SafeLPTSTR text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " +
   "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation " +
   "ullamco laboris nisi ut aliquip ex ea commodo consequat.";
// Append, replace, remove and read/write indexed characters
text.Append(" Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.");
text.Replace(" ad ", "a");
text.Remove(0, 50);
text[12] = 'X';
// Use equality and comparison operators
if (text == "Unknown string value")
   return false;
// Express implicitly as an IntPtr or string
WriteFile(hFile, text, text.Length, out _, default);
Console.WriteLine(text);