Introduction
This article describes a very short helper class to work with unmanaged memory that must be pinned to prevent the garbage collector from moving it. This is a particular requirement when working with the Win32 API's asynchronous I/O methods, which I found myself doing when implementing a USB device interface.
There is no download; simply copy the code from this article--please do not remove the copyright.
The Code
There really isn't much to this code:
- It uses generics to internally cast the unmanaged memory to the desired managed type.
- The class maintains an instance of the managed structure.
- The constructor pins this structure and initializes a pointer suitable for working with Win32 API methods.
- The ManagedObject property is used to return a managed object that references the unmanaged memory and to copy the managed object to the unmanaged memory. One caveat is that the managed object must be a structure suitable for use with the Marshal class.
- The Pointer property is used to return the address of the unmanaged memory.
- The destructor disposes of the unmanaged memory.
- The class implements IDisposable, so that you can use this class in a "using" block as well.
Here's the code:
using System;
using System.Runtime.InteropServices;
namespace Clifton.Tools.Interop
{
public class PinnedObject<T> : IDisposable where T : struct
{
protected T managedObject;
protected GCHandle handle;
protected IntPtr ptr;
protected bool disposed;
public T ManangedObject
{
get
{
return (T)handle.Target;
}
set
{
Marshal.StructureToPtr(value, ptr, false);
}
}
public IntPtr Pointer
{
get { return ptr; }
}
public PinnedObject()
{
handle = GCHandle.Alloc(managedObject, GCHandleType.Pinned);
ptr = handle.AddrOfPinnedObject();
}
~PinnedObject()
{
Dispose();
}
public void Dispose()
{
if (!disposed)
{
handle.Free();
ptr = IntPtr.Zero;
disposed = true;
}
}
}
}
Example
Using Unsafe Pointers In C#
This example illustrates manipulating a PinnedObject
using C++ style pointer syntax within an unsafe code block. The code illustrates:
- Assigning a new structure to the pinned object (testing the ManagedObject setter)
- Getting the pointer and manipulating the structure via the pointer (testing the Pointer getter)
- Getting the structure (testing the ManagedObject getter)
using System;
using Clifton.Tools.Interop;
namespace pintest
{
public struct TestStruct
{
public int a;
}
public static class Test
{
public static void Main()
{
PinnedObject<TestStruct> pin = new PinnedObject<TestStruct>();
TestStruct ts = new TestStruct();
ts.a = 1;
pin.ManangedObject = ts;
unsafe
{
TestStruct* p = (TestStruct*)pin.Pointer;
++p->a;
}
Console.WriteLine(pin.ManangedObject.a);
}
}
}
Conclusion
A simple class but hopefully you will find it useful for the strong type management and functionality that it encapsulates.