Click here to Skip to main content
15,890,690 members
Articles / Programming Languages / C#
Article

Pinned Object

Rate me:
Please Sign up or sign in to vote.
4.67/5 (16 votes)
11 Feb 2007CPOL1 min read 64.4K   26   8
A helper class for working with structures that need to be pinned to prevent the GC from moving them.

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:

// (c) 2007 Marc Clifton
using System;
using System.Runtime.InteropServices;

namespace Clifton.Tools.Interop
{
  /// <summary>
  /// A helper class for pinning a managed structure so that it is suitable for
  /// unmanaged calls. A pinned object will not be collected and will not be moved
  /// by the GC until explicitly freed.
  /// </summary>
  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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
GeneralPinning copy of the structure Pin
Lesheniuk2-Mar-07 5:04
Lesheniuk2-Mar-07 5:04 
GeneralRe: Pinning copy of the structure Pin
Marc Clifton2-Mar-07 5:18
mvaMarc Clifton2-Mar-07 5:18 
Questionfixed statement Pin
rvpilot13-Feb-07 10:48
rvpilot13-Feb-07 10:48 
AnswerRe: fixed statement Pin
Marc Clifton13-Feb-07 11:06
mvaMarc Clifton13-Feb-07 11:06 
GeneralRe: fixed statement Pin
rvpilot13-Feb-07 11:14
rvpilot13-Feb-07 11:14 
GeneralTesting Pin
Luc Pattyn12-Feb-07 12:07
sitebuilderLuc Pattyn12-Feb-07 12:07 
GeneralRe: Testing Pin
Marc Clifton12-Feb-07 13:28
mvaMarc Clifton12-Feb-07 13:28 
GeneralMy .NET 1.1 approach, without generics Pin
Luc Pattyn12-Feb-07 11:27
sitebuilderLuc Pattyn12-Feb-07 11:27 
Hi Marc,

your article got me thinking. I had a simple scheme where I pass an object (typically
a byte array) to the constructor of an LP_Pinning object. I did not implement the
equivalent of your ManangedObject property, but then I also never needed it
since I always use the using(...) pattern.

So now I tested my approach for other things, and it seems to accept a struct of
bittable things (say ints); of course it rejects some others when GCHandle.Alloc
complains with "Object contains non-primitive or non-blittable data."

So here is my entire class, please feel free to comment on it:

using System;
using System.Runtime.InteropServices; // GCHandle

namespace LP_Core {
/// <summary>
/// An LP_Pinning instance pins an object for as long as it is alive.
/// </summary>
/// <remarks>
/// This is the preferred use when the pointer value is not needed:
/// using (new LP_Pinning(objectThatNeedsPinning)) {
/// .. use the object here, it is pinned now ...
/// }
/// This is the preferred use when the pointer value is needed:
/// using (LP_Pinning pinning=new LP_Pinning(objectThatNeedsPinning)) {
/// IntPtr ptr=pinning.Ptr;
/// .. use the object here, it is pinned now ...
/// }
/// </remarks>
public class LP_Pinning : IDisposable {
private GCHandle handle;
private bool disposed;
private IntPtr ptr;

/// <summary>
/// Creates an instance op LP_Pinning, and pins the argument.
/// </summary>
/// <param name="obj"></param>
public LP_Pinning(object obj) {
handle=GCHandle.Alloc(obj, GCHandleType.Pinned);
ptr=handle.AddrOfPinnedObject();
}

/// <summary>
/// Undoes the pinning.
/// </summary>
~LP_Pinning() {
Dispose();
}

public void Dispose() {
if (!disposed) {
disposed=true;
handle.Free();
ptr=IntPtr.Zero;
}
}

/// <summary>
/// Returns the pointer to the pinned object.
/// </summary>
public IntPtr Ptr {get {return ptr;}}
}
}

Regards,

Luc Pattyn

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.