| File: Windows\Win32\System\Com\Lifetime.cs | Web Access |
| Project: src\winforms\src\System.Private.Windows.Core\src\System.Private.Windows.Core.csproj (System.Private.Windows.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace Windows.Win32.System.Com; /// <summary> /// Struct that handles managed object COM projection lifetime management. /// </summary> /// <typeparam name="TVTable"> /// Struct that repesents the VTable for a COM pointer. /// </typeparam> /// <typeparam name="TObject"> /// The type of object being projected. /// </typeparam> internal unsafe struct Lifetime<TVTable, TObject> where TVTable : unmanaged { private TVTable* _vtable; private void* _handle; private uint _refCount; public static unsafe uint AddRef(void* @this) => Interlocked.Increment(ref ((Lifetime<TVTable, TObject>*)@this)->_refCount); public static unsafe uint Release(void* @this) { var lifetime = (Lifetime<TVTable, TObject>*)@this; Debug.Assert(lifetime->_refCount > 0); uint count = Interlocked.Decrement(ref lifetime->_refCount); if (count == 0) { GCHandle.FromIntPtr((nint)lifetime->_handle).Free(); Marshal.FreeCoTaskMem((nint)lifetime); } return count; } /// <summary> /// Allocate a lifetime wrapper for the given <paramref name="object"/> with the given /// <paramref name="vtable"/>. /// </summary> /// <remarks> /// <para> /// This creates a <see cref="GCHandle"/> to root the <paramref name="object"/> until ref /// counting has gone to zero. /// </para> /// <para> /// The <paramref name="vtable"/> should be fixed, typically as a static. Com calls always /// include the "this" pointer as the first argument. /// </para> /// </remarks> public static unsafe Lifetime<TVTable, TObject>* Allocate(TObject @object, TVTable* vtable) { var wrapper = (Lifetime<TVTable, TObject>*)Marshal.AllocCoTaskMem(sizeof(Lifetime<TVTable, TObject>)); // Create the wrapper instance. wrapper->_vtable = vtable; wrapper->_handle = (void*)GCHandle.ToIntPtr(GCHandle.Alloc(@object)); wrapper->_refCount = 1; return wrapper; } /// <summary> /// Get the object associated with this lifetime. /// </summary> /// <param name="this"> /// The passed back "this" pointer that originally came from <see cref="Allocate(TObject, TVTable*)"/>. /// </param> /// <returns>The object associated with this lifetime, if any.</returns> /// <exception cref="InvalidOperationException">The handle was freed.</exception> public static TObject? GetObject(void* @this) { var lifetime = (Lifetime<TVTable, TObject>*)@this; return (TObject?)GCHandle.FromIntPtr((IntPtr)lifetime->_handle).Target; } }