|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// Copied from dotnet/sdk to provide COM pointer lifetime management
// using the CsWin32 struct-based COM pattern.
using System;
using System.Runtime.CompilerServices;
namespace Windows.Win32.System.Com;
/// <summary>
/// Lifetime management struct for a native COM pointer. Meant to be utilized in a <see langword="using"/> statement
/// to ensure <see cref="IUnknown.Release()"/> is called when going out of scope with the using.
/// </summary>
/// <typeparam name="T">
/// This should be one of the struct COM definitions as generated by CsWin32,
/// or a manually defined COM struct implementing <see cref="IComIID"/>.
/// </typeparam>
internal readonly unsafe ref struct ComScope<T> where T : unmanaged, IComIID
{
// Keeping internal as nint allows us to use Unsafe methods to get
// significantly better generated code.
private readonly nint _value;
/// <summary>
/// Gets the pointer to the COM interface.
/// </summary>
public T* Pointer => (T*)_value;
/// <summary>
/// Initializes a new instance of the <see cref="ComScope{T}"/> struct.
/// </summary>
public ComScope(T* value) => _value = (nint)value;
/// <summary>
/// Initializes a new instance of the <see cref="ComScope{T}"/> struct with a void pointer.
/// </summary>
public ComScope(void* value) => _value = (nint)value;
public static implicit operator T*(in ComScope<T> scope) => (T*)scope._value;
public static implicit operator void*(in ComScope<T> scope) => (void*)scope._value;
public static implicit operator nint(in ComScope<T> scope) => scope._value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator T**(in ComScope<T> scope) =>
(T**)Unsafe.AsPointer(ref Unsafe.AsRef(in scope._value));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator void**(in ComScope<T> scope) =>
(void**)Unsafe.AsPointer(ref Unsafe.AsRef(in scope._value));
/// <summary>
/// <see langword="true"/> if the pointer is null.
/// </summary>
public bool IsNull => _value == 0;
/// <inheritdoc cref="IDisposable.Dispose"/>
public void Dispose()
{
IUnknown* unknown = (IUnknown*)_value;
// Really want this to be null after disposal to avoid double releases, but we also want
// to maintain the readonly state of the struct to allow passing as `in` without creating implicit
// copies (which would break the T** and void** operators). Because <see cref="ComScope{T}"/>
// is a <see langword="ref struct"/>, it cannot be captured or copied to the heap, so all disposals
// observe the original storage.
*(void**)this = null;
if (unknown is not null)
{
unknown->Release();
}
}
}
|