Unmanaged Memory in C# and the Power of Marshal.AllocHGlobal

There are a variety of reasons one might wish to use unmanaged memory in C#, taking advantage of the Marshal class and it’s powerful AllocHGlobal method. The reasons are less important here than the how, at least the how I have learned to do so.

First, you need to know about the word blit and Microsoft’s usage of that word: blittable types. I’ll wait while you catch up.

Next, you need to understand how to construct a complex value type that Microsoft likes to call a “formatted value type.” [sic] To create a formatted value type, you will need to use the StructLayout attribute and the FieldOffset attribute. While not strictly required, these attributes will keep you from wasting precious memory. Here’s an example:

[StructLayout(LayoutKind.Explicit, Size = 8)]
internal struct MyData
{
   [FieldOffset(0)]
   public short Level;

   [FieldOffset(2)]
   public int Weight;

   [FieldOffset(6)]
   public short Indicator;
}

Before you dive into use the AllocHGlobal method to grab some of that raw unmanaged memory that you want, be sure you have first prepared your code to let it go using the FreeHGlobal method. My preference is to do it by implementing IDisposable and a class destructor just in case I forget to dispose of my object that holds my precious unmanaged memory in its hands. Like this:

private bool disposed = false;

~MyPage()
{
   Dispose(false);
}

public void Dispose()
{
   Dispose(true);
   GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
   if (!this.disposed)
   {
      this.disposed = true;
      if (this.myPtr != IntPtr.Zero)
      {
         Marshal.FreeHGlobal(this.myPtr);
         this.myPtr = IntPtr.Zero;
      }
   }
}

Now you are ready to grab some memory and use it unsafely but responsibly with all the benefits that come from doing so. Here’s an example. Of course there are many more things you can do with unmanaged memory, but this will get you started.

public class MyPage : IDisposable
{
   private IntPtr myPtr = IntPtr.Zero;
   
   public MyPage(long capacity)
   {
      long size = Marshal.SizeOf(typeof(MyData));
      long totalBytes = capacity * size;
      this.myPtr = Marshal.AllocHGlobal(new IntPtr(totalBytes));
   }
   
   public MyData this[long index]
   {
      get
      {
         unsafe
         {
            return ((MyData*)this.myPtr.ToPointer())[index];
         }
      }
      set
      {
         unsafe
         {
            ((MyData*)this.myPtr.ToPointer())[index] = value;
         }
      }
   }
   
   
   private bool disposed = false;
   
   ~MyPage()
   {
      Dispose(false);
   }
   
   public void Dispose()
   {
      Dispose(true);
      GC.SuppressFinalize(this);
   }
   
   protected virtual void Dispose(bool disposing)
   {
      if (!this.disposed)
      {
         this.disposed = true;
         if (this.myPtr != IntPtr.Zero)
         {
            Marshal.FreeHGlobal(this.myPtr);
            this.myPtr = IntPtr.Zero;
         }
      }
   }
}

You will note that I have not included bounds checking in the above code, nor have I shown any code for initializing the data. Yes, this is living dangerously, but it is also one of the main reasons for using unsafe access to unmanaged memory—the performance advantage of not checking bounds. So drive safely. And if you are using unmanaged memory from C# for something clever or amazing, please tell us about it in comments.