3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 namespace System.Runtime.InteropServices
9 using System.Security.Permissions;
10 using System.Runtime.CompilerServices;
11 using System.Threading;
12 using System.Runtime.Versioning;
13 using System.Diagnostics.Contracts;
15 // These are the types of handles used by the EE.
16 // IMPORTANT: These must match the definitions in ObjectHandle.h in the EE.
17 // IMPORTANT: If new values are added to the enum the GCHandle::MaxHandleType
18 // constant must be updated.
20 [System.Runtime.InteropServices.ComVisible(true)]
21 public enum GCHandleType
24 WeakTrackResurrection = 1,
29 // This class allows you to create an opaque, GC handle to any
30 // COM+ object. A GC handle is used when an object reference must be
31 // reachable from unmanaged memory. There are 3 kinds of roots:
32 // Normal - keeps the object from being collected.
33 // Weak - allows object to be collected and handle contents will be zeroed.
34 // Weak references are zeroed before the finalizer runs, so if the
35 // object is resurrected in the finalizer the weak reference is
37 // WeakTrackResurrection - Same as weak, but stays until after object is
39 // Pinned - same as normal, but allows the address of the actual object
43 [StructLayout(LayoutKind.Sequential)]
44 [System.Runtime.InteropServices.ComVisible(true)]
45 public struct GCHandle
47 // IMPORTANT: This must be kept in [....] with the GCHandleType enum.
48 private const GCHandleType MaxHandleType = GCHandleType.Pinned;
51 [System.Security.SecuritySafeCritical] // auto-generated
54 s_probeIsActive = Mda.IsInvalidGCHandleCookieProbeEnabled();
56 s_cookieTable = new GCHandleCookieTable();
60 // Allocate a handle storing the object and the type.
61 [System.Security.SecurityCritical] // auto-generated
62 internal GCHandle(Object value, GCHandleType type)
64 // Make sure the type parameter is within the valid range for the enum.
65 if ((uint)type > (uint)MaxHandleType)
66 throw new ArgumentOutOfRangeException("type", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
67 Contract.EndContractBlock();
69 m_handle = InternalAlloc(value, type);
71 // Record if the handle is pinned.
72 if (type == GCHandleType.Pinned)
76 // Used in the conversion functions below.
77 [System.Security.SecurityCritical] // auto-generated
78 internal GCHandle(IntPtr handle)
80 InternalCheckDomain(handle);
84 // Creates a new GC handle for an object.
86 // value - The object that the GC handle is created for.
87 // type - The type of GC handle to create.
89 // returns a new GC handle that protects the object.
90 [System.Security.SecurityCritical] // auto-generated_required
91 public static GCHandle Alloc(Object value)
93 return new GCHandle(value, GCHandleType.Normal);
96 [System.Security.SecurityCritical] // auto-generated_required
97 public static GCHandle Alloc(Object value, GCHandleType type)
99 return new GCHandle(value, type);
103 // Frees a GC handle.
104 [System.Security.SecurityCritical] // auto-generated_required
107 // Copy the handle instance member to a local variable. This is required to prevent
108 // race conditions releasing the handle.
109 IntPtr handle = m_handle;
111 // Free the handle if it hasn't already been freed.
112 if (handle != IntPtr.Zero && Interlocked.CompareExchange(ref m_handle, IntPtr.Zero, handle) == handle)
115 // If this handle was passed out to unmanaged code, we need to remove it
116 // from the cookie table.
117 // NOTE: the entry in the cookie table must be released before the
118 // internal handle is freed to prevent a race with reusing GC handles.
120 s_cookieTable.RemoveHandleIfPresent(handle);
124 InternalFree((IntPtr)(((int)handle) & ~1));
126 InternalFree((IntPtr)(((long)handle) & ~1L));
131 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
135 // Target property - allows getting / updating of the handle's referent.
138 [System.Security.SecurityCritical] // auto-generated_required
141 // Check if the handle was never initialized or was freed.
142 if (m_handle == IntPtr.Zero)
143 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
145 return InternalGet(GetHandleValue());
148 [System.Security.SecurityCritical] // auto-generated_required
151 // Check if the handle was never initialized or was freed.
152 if (m_handle == IntPtr.Zero)
153 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
155 InternalSet(GetHandleValue(), value, IsPinned());
159 // Retrieve the address of an object in a Pinned handle. This throws
160 // an exception if the handle is any type other than Pinned.
161 [System.Security.SecurityCritical] // auto-generated_required
162 public IntPtr AddrOfPinnedObject()
164 // Check if the handle was not a pinned handle.
167 // Check if the handle was never initialized for was freed.
168 if (m_handle == IntPtr.Zero)
169 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
171 // You can only get the address of pinned handles.
172 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotPinned"));
176 return InternalAddrOfPinnedObject(GetHandleValue());
179 // Determine whether this handle has been allocated or not.
180 public bool IsAllocated
184 return m_handle != IntPtr.Zero;
188 // Used to create a GCHandle from an int. This is intended to
189 // be used with the reverse conversion.
190 [System.Security.SecurityCritical] // auto-generated_required
191 public static explicit operator GCHandle(IntPtr value)
193 return FromIntPtr(value);
196 [System.Security.SecurityCritical] // auto-generated_required
197 public static GCHandle FromIntPtr(IntPtr value)
199 if (value == IntPtr.Zero)
200 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
201 Contract.EndContractBlock();
203 IntPtr handle = value;
208 // Make sure this cookie matches up with a GCHandle we've passed out a cookie for.
209 handle = s_cookieTable.GetHandle(value);
210 if (IntPtr.Zero == handle)
212 // Fire an MDA if we were unable to retrieve the GCHandle.
213 Mda.FireInvalidGCHandleCookieProbe(value);
214 return new GCHandle(IntPtr.Zero);
219 return new GCHandle(handle);
222 // Used to get the internal integer representation of the handle out.
223 public static explicit operator IntPtr(GCHandle value)
225 return ToIntPtr(value);
228 public static IntPtr ToIntPtr(GCHandle value)
233 // Remember that we passed this GCHandle out by storing the cookie we returned so we
234 // can later validate.
235 return s_cookieTable.FindOrAddHandle(value.m_handle);
238 return value.m_handle;
241 public override int GetHashCode()
243 return m_handle.GetHashCode();
246 public override bool Equals(Object o)
250 // Check that o is a GCHandle first
251 if(o == null || !(o is GCHandle))
256 return m_handle == hnd.m_handle;
259 public static bool operator ==(GCHandle a, GCHandle b)
261 return a.m_handle == b.m_handle;
264 public static bool operator !=(GCHandle a, GCHandle b)
266 return a.m_handle != b.m_handle;
269 internal IntPtr GetHandleValue()
272 return new IntPtr(((int)m_handle) & ~1);
274 return new IntPtr(((long)m_handle) & ~1L);
278 internal bool IsPinned()
281 return (((int)m_handle) & 1) != 0;
283 return (((long)m_handle) & 1) != 0;
287 internal void SetIsPinned()
290 m_handle = new IntPtr(((int)m_handle) | 1);
292 m_handle = new IntPtr(((long)m_handle) | 1L);
296 // Internal native calls that this implementation uses.
297 [System.Security.SecurityCritical] // auto-generated
298 [MethodImplAttribute(MethodImplOptions.InternalCall)]
299 [ResourceExposure(ResourceScope.None)]
300 internal static extern IntPtr InternalAlloc(Object value, GCHandleType type);
301 [System.Security.SecurityCritical] // auto-generated
302 [ResourceExposure(ResourceScope.None)]
303 [MethodImplAttribute(MethodImplOptions.InternalCall)]
304 internal static extern void InternalFree(IntPtr handle);
305 [System.Security.SecurityCritical] // auto-generated
306 [ResourceExposure(ResourceScope.None)]
307 [MethodImplAttribute(MethodImplOptions.InternalCall)]
308 internal static extern Object InternalGet(IntPtr handle);
309 [System.Security.SecurityCritical] // auto-generated
310 [ResourceExposure(ResourceScope.None)]
311 [MethodImplAttribute(MethodImplOptions.InternalCall)]
312 internal static extern void InternalSet(IntPtr handle, Object value, bool isPinned);
313 [System.Security.SecurityCritical] // auto-generated
314 [ResourceExposure(ResourceScope.None)]
315 [MethodImplAttribute(MethodImplOptions.InternalCall)]
316 internal static extern Object InternalCompareExchange(IntPtr handle, Object value, Object oldValue, bool isPinned);
317 [System.Security.SecurityCritical] // auto-generated
318 [ResourceExposure(ResourceScope.None)]
319 [MethodImplAttribute(MethodImplOptions.InternalCall)]
320 internal static extern IntPtr InternalAddrOfPinnedObject(IntPtr handle);
321 [System.Security.SecurityCritical] // auto-generated
322 [ResourceExposure(ResourceScope.None)]
323 [MethodImplAttribute(MethodImplOptions.InternalCall)]
324 internal static extern void InternalCheckDomain(IntPtr handle);
325 [System.Security.SecurityCritical] // auto-generated
326 [ResourceExposure(ResourceScope.None)]
327 [MethodImplAttribute(MethodImplOptions.InternalCall)]
328 internal static extern GCHandleType InternalGetHandleType(IntPtr handle);
330 // The actual integer handle value that the EE uses internally.
331 private IntPtr m_handle;
334 // The GCHandle cookie table.
335 static private volatile GCHandleCookieTable s_cookieTable = null;
336 static private volatile bool s_probeIsActive = false;