1 out of 2 rated this helpful - Rate this topic

GCHandle Structure

Provides a way to access a managed object from unmanaged memory.

Namespace:  System.Runtime.InteropServices
Assembly:  mscorlib (in mscorlib.dll)
[ComVisibleAttribute(true)]
public struct GCHandle

The GCHandle type exposes the following members.

  Name Description
Public property Supported by the XNA Framework IsAllocated Gets a value indicating whether the handle is allocated.
Public property Supported by the XNA Framework Target Gets or sets the object this handle represents.
Top
  Name Description
Public method Supported by the XNA Framework AddrOfPinnedObject Retrieves the address of an object in a Pinned handle.
Public method Static member Supported by the XNA Framework Alloc(Object) Allocates a Normal handle for the specified object.
Public method Static member Supported by the XNA Framework Alloc(Object, GCHandleType) Allocates a handle of the specified type for the specified object.
Public method Supported by the XNA Framework Equals Determines whether the specified GCHandle object is equal to the current GCHandle object. (Overrides ValueType.Equals(Object).)

In XNA Framework, this member is overridden by Equals(Object).
Protected method Supported by the XNA Framework Finalize Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. (Inherited from Object.)
Public method Supported by the XNA Framework Free Releases a GCHandle.
Public method Static member FromIntPtr Returns a new GCHandle object created from a handle to a managed object.
Public method Supported by the XNA Framework GetHashCode Returns an identifier for the current GCHandle object. (Overrides ValueType.GetHashCode().)

In XNA Framework, this member is overridden by GetHashCode().
Public method Supported by the XNA Framework GetType Gets the Type of the current instance. (Inherited from Object.)
Protected method Supported by the XNA Framework MemberwiseClone Creates a shallow copy of the current Object. (Inherited from Object.)
Public method Static member ToIntPtr Returns the internal integer representation of a GCHandle object.
Public method Supported by the XNA Framework ToString Returns the fully qualified type name of this instance. (Inherited from ValueType.)

In XNA Framework 3.0, this member is inherited from Object.ToString().
Top
  Name Description
Public operator Static member Equality Returns a value indicating whether two GCHandle objects are equal.
Public operator Static member Supported by the XNA Framework Explicit(GCHandle to IntPtr) A GCHandle is stored using an internal integer representation.
Public operator Static member Supported by the XNA Framework Explicit(IntPtr to GCHandle) A GCHandle is stored using an internal integer representation.
Public operator Static member Inequality Returns a value indicating whether two GCHandle objects are not equal.
Top

The GCHandle structure is used with the GCHandleType enumeration to create a handle corresponding to any managed object. This handle can be one of four types: Weak, WeakTrackResurrection, Normal, or Pinned. When the handle has been allocated, you can use it to prevent the managed object from being collected by the garbage collector when an unmanaged client holds the only reference. Without such a handle, the object can be collected by the garbage collector before completing its work on behalf of the unmanaged client.

You can also use GCHandle to create a pinned object that returns a memory address to prevent the garbage collector from moving the object in memory.

When the handle goes out of scope you must explicitly release it by calling the Free method; otherwise, memory leaks may occur. When you free a pinned handle, the associated object will be unpinned and will become eligible for garbage collection, if there are no other references to it.

The following example shows an App class that creates a handle to a managed object using the GCHandle.Alloc method, which prevents the managed object from being collected. A call to the EnumWindows method passes a delegate and a managed object (both declared as managed types, but not shown), and casts the handle to an IntPtr. The unmanaged function passes the type back to the caller as a parameter of the callback function.


using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security.Permissions;

public delegate bool CallBack(int handle, IntPtr param);

public class LibWrap
{
	// passing managed object as LPARAM
	// BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);

	[DllImport("user32.dll")]
	public static extern bool EnumWindows(CallBack cb, IntPtr param);
}

public class App
{
	public static void Main()
	{
		Run();
	}

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
	public static void Run()
        {
		TextWriter tw = System.Console.Out;
		GCHandle gch = GCHandle.Alloc(tw);

		CallBack cewp = new CallBack(CaptureEnumWindowsProc);

		// platform invoke will prevent delegate to be garbage collected
		// before call ends

		LibWrap.EnumWindows(cewp, GCHandle.ToIntPtr(gch));
		gch.Free();
        }

	private static bool CaptureEnumWindowsProc(int handle, IntPtr param)
	{
		GCHandle gch = GCHandle.FromIntPtr(param);
		TextWriter tw = (TextWriter)gch.Target;
		tw.WriteLine(handle);
		return true;
	}
}


.NET Framework

Supported in: 4, 3.5, 3.0, 2.0, 1.1, 1.0

.NET Framework Client Profile

Supported in: 4, 3.5 SP1

Windows 7, Windows Vista SP1 or later, Windows XP SP3, Windows XP SP2 x64 Edition, Windows Server 2008 (Server Core not supported), Windows Server 2008 R2 (Server Core supported with SP1 or later), Windows Server 2003 SP2

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
RE: Location of call to GCHandle.Free() in example
It's the latter. I was also extremely confused by this example. There is quite a bit of implied knowledge required. A few well-placed code comments or link references reminding the reader of certain CLR behaviors would help immensely. I'm still learning, but here are my observations:
  • EnumWindows() is a synchronous call. Therefore CaptureEnumWindowsProc()is fully executed before EnumWindows() returns. This is why GCHandle.Free() is called after EnumWindows() and not within the callback. Even if it were asynchronous, you couldn't free within the callback anyway (see bullet #3 below).
  • Garbage collection does not necessarily wait for a reference-type localvariable to fall out of scope before reclaiming it. This is obviously the whole purpose of GCHandle (to protect variables from being prematurely freed). However, this aspect of garbage collection is never stated explicitly. Coming from the C++ world, my assumption was that a local variable would count as "referenced" (and therefore exempt from garbage collection) until it fell out of scope.
  • The example doesn't explain why the GCHandle variable itself is not susceptible to garbage collection. The answer is that it is a value-type struct which is allocated on the stack. Garbage collection only affects heap-allocated reference-type variables. This may be obvious to some, but the poor choice of naming for GCHandle.Alloc() makes it easily missed by newcomers.
WARNING F# USERS!
Creating a GCHandle in F# makes it immutable, and calling Free will free the handle for the GC, but it will not reflect back on the GCHandle itself. Bind GCHandles as mutable to ensure that calling Free updates the handle as well.
Location of call to GCHandle.Free() in example
In this example it seems that a normal GCHandle is being used to prevent the TextWriter instance from being garbage collected after the local variable 'tw' containing the only reference to it goes of scope and before the callback method 'CaptureEnumWindowsProc' which uses it executes - assuming callback executes asynchronously with respect to 'LibWrap.EnumWindows' function call.  In which case shouldn't GCHandle.Free() be invoked in the callback and not in the Run() method ? Or does this have something do with the JIT compiler flagging 'tw' as inactive once the call('frame') for unmanaged 'LibWrap.EnumWindows' function has been setup - assuming callback executes synchronously with respect to 'LibWrap.EnumWindows' function call?