Skip to content

Share implementation of ComWrappers between CoreCLR and NativeAOT #113907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d673663
Various fixes to get tests passing
jkoritzinsky Feb 18, 2025
9b32654
Remove wrapper ID as a concept entirely
jkoritzinsky Mar 26, 2025
2abcf60
Add #if around built-in COM in the weakref support.
jkoritzinsky Mar 26, 2025
5b08974
Check for aggregation before getting a com weak ref for a ComWrappers…
jkoritzinsky Mar 26, 2025
8feaa8c
Make static now that we don't track an instance id
jkoritzinsky Mar 26, 2025
d045004
Various build fixes
jkoritzinsky Mar 27, 2025
095d924
Encapsulate the Target member
jkoritzinsky Mar 27, 2025
5c93a50
More fixes
jkoritzinsky Mar 27, 2025
045aae9
Add missing cast
jkoritzinsky Mar 27, 2025
dbd8533
TADDR cast for null
jkoritzinsky Mar 28, 2025
64b5e41
Fix defines
jkoritzinsky Mar 28, 2025
fe7d853
QI for identity is done in the managed code now, so we can skip the Q…
jkoritzinsky Mar 28, 2025
6b8913f
Fix ifdefs
jkoritzinsky Mar 28, 2025
4a09807
Fix calling the ComWrappers API when in a ComWrappers scenario.
jkoritzinsky Mar 31, 2025
9a0ee83
Merge branch 'shared-comwrappers' of github.com:jkoritzinsky/runtime …
jkoritzinsky Mar 31, 2025
a6b7513
fix arguments
jkoritzinsky Apr 2, 2025
a4c8b8d
Handle ArgumentException for global marshalling instance.
jkoritzinsky Apr 7, 2025
cee2e71
Fix fallback for RCW case as well
jkoritzinsky Apr 9, 2025
f6439f2
Fix COM Activation
jkoritzinsky Apr 10, 2025
0ec2e6b
Various PR feedback
jkoritzinsky Apr 10, 2025
6da9c57
Update src/coreclr/vm/interoplibinterface_shared.cpp
jkoritzinsky Apr 10, 2025
9dfcdff
Fix lifetime issue with the DAC (and provide a mechanism that we can …
jkoritzinsky Apr 11, 2025
d8605e7
PR feedback
jkoritzinsky Apr 11, 2025
1ae095f
Various PR feedback
jkoritzinsky Apr 11, 2025
ce21cc3
Merge branch 'main' of https://github.com/dotnet/runtime into shared-…
jkoritzinsky Apr 11, 2025
c8d0277
Use the pre-init pattern for all vtables on NativeAOT.
jkoritzinsky Apr 12, 2025
b123e15
PR feedback
jkoritzinsky Apr 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@
<Compile Include="src\System\RuntimeType.GenericCache.cs" />
</ItemGroup>
<ItemGroup Condition="'$(FeatureComWrappers)' == 'true'">
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComWrappers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComWrappers.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\TrackerObjectManager.CoreCLR.cs" />
</ItemGroup>
<ItemGroup Condition="'$(FeatureCominterop)' == 'true'">
<Compile Include="$(CommonPath)System\Runtime\InteropServices\IDispatch.cs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@ namespace System
internal sealed partial class ComAwareWeakReference
{
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWeakRefToObject")]
private static partial void ComWeakRefToObject(IntPtr pComWeakRef, long wrapperId, ObjectHandleOnStack retRcw);
private static partial void ComWeakRefToObject(IntPtr pComWeakRef, ObjectHandleOnStack retRcw);

internal static object? ComWeakRefToObject(IntPtr pComWeakRef, long wrapperId)
internal static object? ComWeakRefToObject(IntPtr pComWeakRef, object? context)
{
object? retRcw = null;
ComWeakRefToObject(pComWeakRef, wrapperId, ObjectHandleOnStack.Create(ref retRcw));
return retRcw;
#if FEATURE_COMINTEROP
if (context is null)
{
// This wrapper was not created by ComWrappers, so we try to rehydrate using built-in COM.
object? retRcw = null;
ComWeakRefToObject(pComWeakRef, ObjectHandleOnStack.Create(ref retRcw));
return retRcw;
}
#endif // FEATURE_COMINTEROP

return ComWeakRefToComWrappersObject(pComWeakRef, context);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -40,16 +48,33 @@ internal static unsafe bool PossiblyComObject(object target)
internal static extern bool HasInteropInfo(object target);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ObjectToComWeakRef")]
private static partial IntPtr ObjectToComWeakRef(ObjectHandleOnStack retRcw, out long wrapperId);
private static partial IntPtr ObjectToComWeakRef(ObjectHandleOnStack retRcw);

internal static nint ObjectToComWeakRef(object target, out long wrapperId)
internal static nint ObjectToComWeakRef(object target, out object? context)
{
if (HasInteropInfo(target))
if (!HasInteropInfo(target))
{
context = null;
return IntPtr.Zero;
}

#if FEATURE_COMINTEROP
if (target is __ComObject)
{
// This object is using built-in COM, so use built-in COM to create the weak reference.
context = null;
return ObjectToComWeakRef(ObjectHandleOnStack.Create(ref target));
}
#endif // FEATURE_COMINTEROP

if (PossiblyComWrappersObject(target))
{
return ObjectToComWeakRef(ObjectHandleOnStack.Create(ref target), out wrapperId);
return ComWrappersObjectToComWeakRef(target, out context);
}

wrapperId = 0;
// This object is not produced using built-in COM or ComWrappers
// or is an aggregated object, so we cannot create a weak reference.
context = null;
return IntPtr.Zero;
}
}
Expand Down
14 changes: 10 additions & 4 deletions src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ internal enum GC_ALLOC_FLAGS
private static partial long GetTotalMemory();

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_Collect")]
private static partial void _Collect(int generation, int mode);
private static partial void _Collect(int generation, int mode, [MarshalAs(UnmanagedType.U1)] bool lowMemoryPressure);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern int GetMaxGeneration();
Expand Down Expand Up @@ -174,7 +174,7 @@ public static void Collect(int generation)
public static void Collect()
{
// -1 says to GC all generations.
_Collect(-1, (int)InternalGCCollectionMode.Blocking);
_Collect(-1, (int)InternalGCCollectionMode.Blocking, lowMemoryPressure: false);
}

public static void Collect(int generation, GCCollectionMode mode)
Expand All @@ -189,6 +189,11 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking)
}

public static void Collect(int generation, GCCollectionMode mode, bool blocking, bool compacting)
{
Collect(generation, mode, blocking, compacting, lowMemoryPressure: false);
}

internal static void Collect(int generation, GCCollectionMode mode, bool blocking, bool compacting, bool lowMemoryPressure)
{
ArgumentOutOfRangeException.ThrowIfNegative(generation);

Expand All @@ -197,7 +202,6 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
}


int iInternalModes = 0;

if (mode == GCCollectionMode.Optimized)
Expand All @@ -222,7 +226,9 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
}

if (compacting)
{
iInternalModes |= (int)InternalGCCollectionMode.Compacting;
}

if (blocking)
{
Expand All @@ -233,7 +239,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking,
iInternalModes |= (int)InternalGCCollectionMode.NonBlocking;
}

_Collect(generation, iInternalModes);
_Collect(generation, (int)iInternalModes, lowMemoryPressure);
}

public static int CollectionCount(int generation)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Collections.Concurrent;

namespace System.Runtime.InteropServices
{
/// <summary>
/// Class for managing wrappers of COM IUnknown types.
/// </summary>
public abstract partial class ComWrappers
{
/// <summary>
/// Get the runtime provided IUnknown implementation.
/// </summary>
/// <param name="fpQueryInterface">Function pointer to QueryInterface.</param>
/// <param name="fpAddRef">Function pointer to AddRef.</param>
/// <param name="fpRelease">Function pointer to Release.</param>
public static unsafe void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease)
=> GetIUnknownImplInternal(out fpQueryInterface, out fpAddRef, out fpRelease);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWrappers_GetIUnknownImpl")]
[SuppressGCTransition]
private static partial void GetIUnknownImplInternal(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease);

internal static IntPtr DefaultIUnknownVftblPtr { get; } = CreateDefaultIUnknownVftbl();
internal static IntPtr TaggedImplVftblPtr { get; } = CreateTaggedImplVftbl();
internal static IntPtr DefaultIReferenceTrackerTargetVftblPtr { get; } = CreateDefaultIReferenceTrackerTargetVftbl();

private static unsafe IntPtr CreateDefaultIUnknownVftbl()
{
IntPtr* vftbl = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ComWrappers), 3 * sizeof(IntPtr));
GetIUnknownImpl(out vftbl[0], out vftbl[1], out vftbl[2]);
return (IntPtr)vftbl;
}

private static unsafe IntPtr CreateTaggedImplVftbl()
{
IntPtr* vftbl = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ComWrappers), 4 * sizeof(IntPtr));
GetIUnknownImpl(out vftbl[0], out vftbl[1], out vftbl[2]);
vftbl[3] = GetTaggedImplCurrentVersion();
return (IntPtr)vftbl;
}

private static readonly ConditionalWeakTable<object, List<ManagedObjectWrapperHolder>> s_allManagedObjectWrapperTable = [];

private static unsafe void RegisterManagedObjectWrapperForDiagnostics(object instance, ManagedObjectWrapperHolder wrapper)
{
// Record the relationship between the managed object and this wrapper.
// This will ensure that we keep all ManagedObjectWrapperHolders alive as long as the managed object is alive,
// even if the ComWrappers instance dies.
// We must do this as the runtime's recording of the relationship between the managed object and the wrapper
// lives as long as the object is alive, and we record the raw pointer to the wrapper.
List<ManagedObjectWrapperHolder> allWrappersForThisInstance = s_allManagedObjectWrapperTable.GetOrCreateValue(instance);
lock (allWrappersForThisInstance)
{
allWrappersForThisInstance.Add(wrapper);
}
RegisterManagedObjectWrapperForDiagnosticsInternal(ObjectHandleOnStack.Create(ref instance), wrapper.Wrapper);
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWrappers_RegisterManagedObjectWrapperForDiagnostics")]
private static unsafe partial void RegisterManagedObjectWrapperForDiagnosticsInternal(ObjectHandleOnStack instance, ManagedObjectWrapper* wrapper);

private static void RegisterNativeObjectWrapperForDiagnostics(NativeObjectWrapper registeredWrapper)
{
object target = registeredWrapper.ProxyHandle.Target!;
RegisterNativeObjectWrapperForDiagnosticsInternal(ObjectHandleOnStack.Create(ref target), ObjectHandleOnStack.Create(ref registeredWrapper));
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWrappers_RegisterNativeObjectWrapperForDiagnostics")]
private static unsafe partial void RegisterNativeObjectWrapperForDiagnosticsInternal(ObjectHandleOnStack target, ObjectHandleOnStack wrapper);

internal static int CallICustomQueryInterface(ManagedObjectWrapperHolder holder, ref Guid iid, out IntPtr ppObject)
{
if (holder.WrappedObject is ICustomQueryInterface customQueryInterface)
{
return (int)customQueryInterface.GetInterface(ref iid, out ppObject);
}

ppObject = IntPtr.Zero;
return -1; // See TryInvokeICustomQueryInterfaceResult
}

internal static IntPtr GetOrCreateComInterfaceForObjectWithGlobalMarshallingInstance(object obj)
{
try
{
return s_globalInstanceForMarshalling is null
? IntPtr.Zero
: s_globalInstanceForMarshalling.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport);
}
catch (ArgumentException)
{
// We've failed to create a COM interface for the object.
// Fallback to built-in COM.
return IntPtr.Zero;
}
}

internal static object? GetOrCreateObjectForComInstanceWithGlobalMarshallingInstance(IntPtr comObject, CreateObjectFlags flags)
{
try
{
return s_globalInstanceForMarshalling?.GetOrCreateObjectForComInstance(comObject, flags);
}
catch (ArgumentNullException)
{
// We've failed to create a managed object for the COM instance.
// Fallback to built-in COM.
return null;
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWrappers_GetIReferenceTrackerTargetVftbl")]
[SuppressGCTransition]
private static partial IntPtr GetDefaultIReferenceTrackerTargetVftbl();

private static IntPtr CreateDefaultIReferenceTrackerTargetVftbl()
=> GetDefaultIReferenceTrackerTargetVftbl();

private static IntPtr GetTaggedImplCurrentVersion()
{
return GetTaggedImpl();
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWrappers_GetTaggedImpl")]
[SuppressGCTransition]
private static partial IntPtr GetTaggedImpl();

internal sealed partial class ManagedObjectWrapperHolder
{
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWrappers_RegisterIsRootedCallback")]
private static partial void RegisterIsRootedCallback();

private static IntPtr AllocateRefCountedHandle(ManagedObjectWrapperHolder holder)
{
return AllocateRefCountedHandle(ObjectHandleOnStack.Create(ref holder));
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ComWrappers_AllocateRefCountedHandle")]
private static partial IntPtr AllocateRefCountedHandle(ObjectHandleOnStack obj);
}
}
}
Loading
Loading