File: Interop\ComAggregate.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_pxr0p0dn_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Diagnostics;
using System.Runtime.InteropServices;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
 
internal static class ComAggregate
{
    /// <summary>
    /// This method creates a native COM object that aggregates the passed in managed object.
    /// The reason we need to do this is to enable legacy managed code that expects managed casts
    /// expressions to perform a QI on the COM object wrapped by an RCW. These clients are relying
    /// on the fact that COM type equality is based on GUID, whereas type equality is identity in 
    /// the managed world.
    /// Example: IMethodXML is defined many times throughout VS and used by many managed clients
    ///          dealing with CodeFunction objects. If the CodeFunction objects they deal with are
    ///          direct references to managed objects, then casts operations are managed casts
    ///          (as opposed to QI calls), and they fail, since the managed type for IMethodXML
    ///          have different identity (since they are defined in different assemblies). The QI
    ///          works, since under the hood, the casts operations are converted to QI with 
    ///          a GUID which is shared between all these types.
    ///          The solution to this is to return to these managed clients a native object,
    ///          which wraps the managed implementation of these interface using aggregation.
    ///          This means the interfaces will be obtained through QI, while the implementation
    ///          will be forwarded to the managed implementation.
    /// </summary>
    internal static object CreateAggregatedObject(object managedObject)
        => WrapperPolicy.CreateAggregatedObject(managedObject);
 
    /// <summary>
    /// Return the RCW for the native IComWrapperFixed instance aggregating "managedObject"
    /// if there is one. Return "null" if "managedObject" is not aggregated.
    /// </summary>
    internal static IComWrapperFixed? TryGetWrapper(object managedObject)
        => WrapperPolicy.TryGetWrapper(managedObject);
 
    internal static T GetManagedObject<T>(object value) where T : class
    {
        Contract.ThrowIfNull(value, "value");
 
        if (value is IComWrapperFixed wrapper)
        {
            return GetManagedObject<T>(wrapper);
        }
 
        Debug.Assert(value is T, "Why are you casting an object to an reference type it doesn't support?");
        return (T)value;
    }
 
    internal static T GetManagedObject<T>(IComWrapperFixed comWrapper) where T : class
    {
        Contract.ThrowIfNull(comWrapper, "comWrapper");
 
        var handle = GCHandle.FromIntPtr(comWrapper.GCHandlePtr);
        var target = handle.Target;
 
        Contract.ThrowIfNull(target, "target");
        Debug.Assert(target is T, "Why are you casting an object to an reference type it doesn't support?");
        return (T)target;
    }
 
    internal static T? TryGetManagedObject<T>(object? value) where T : class
    {
        if (value is IComWrapperFixed wrapper)
        {
            return TryGetManagedObject<T>(wrapper);
        }
 
        return value as T;
    }
 
    internal static T? TryGetManagedObject<T>(IComWrapperFixed comWrapper) where T : class
    {
        if (comWrapper == null)
        {
            return null;
        }
 
        var handle = GCHandle.FromIntPtr(comWrapper.GCHandlePtr);
        return handle.Target as T;
    }
}