From e3c744461d05b891d3ed3b4bcb4821663f20aa90 Mon Sep 17 00:00:00 2001
From: Elinor Fung <47805090+elinor-fung@users.noreply.github.com>
Date: Mon, 4 May 2020 18:57:40 -0700
Subject: [PATCH] Separate registration for global ComWrappers instance for
tracker support and marshalling (#35681)
---
.../System/Runtime/InteropServices/ComWrappers.cs | 103 +++++++++++++++++----
src/coreclr/src/vm/ecalllist.h | 2 +-
src/coreclr/src/vm/interopconverter.cpp | 4 +-
src/coreclr/src/vm/interoplibinterface.cpp | 74 ++++++++-------
src/coreclr/src/vm/interoplibinterface.h | 11 ++-
src/coreclr/src/vm/metasig.h | 4 +-
src/coreclr/src/vm/mscorlib.h | 5 +-
.../GlobalInstance/GlobalInstance.Marshalling.cs | 56 +++++++++++
.../GlobalInstance.TrackerSupport.cs | 68 ++++++++++++++
.../{Program.cs => GlobalInstance.cs} | 62 ++++++-------
...sproj => GlobalInstanceMarshallingTests.csproj} | 3 +-
.../GlobalInstanceTrackerSupportTests.csproj | 38 ++++++++
.../ComWrappers.PlatformNotSupported.cs | 7 +-
.../ref/System.Runtime.InteropServices.cs | 3 +-
14 files changed, 342 insertions(+), 98 deletions(-)
create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.Marshalling.cs
create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.TrackerSupport.cs
rename src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/{Program.cs => GlobalInstance.cs} (93%)
rename src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/{GlobalInstanceTests.csproj => GlobalInstanceMarshallingTests.csproj} (95%)
create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTrackerSupportTests.csproj
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs
index 0229ac9..323e60a 100644
--- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs
@@ -57,6 +57,16 @@ namespace System.Runtime.InteropServices
}
///
+ /// Internal enumeration used by the runtime to indicate the scenario for which ComWrappers is being used.
+ ///
+ internal enum ComWrappersScenario
+ {
+ Instance = 0,
+ TrackerSupportGlobalInstance = 1,
+ MarshallingGlobalInstance = 2,
+ }
+
+ ///
/// Class for managing wrappers of COM IUnknown types.
///
[CLSCompliant(false)]
@@ -107,9 +117,14 @@ namespace System.Runtime.InteropServices
}
///
- /// Globally registered instance of the ComWrappers class.
+ /// Globally registered instance of the ComWrappers class for reference tracker support.
///
- private static ComWrappers? s_globalInstance;
+ private static ComWrappers? s_globalInstanceForTrackerSupport;
+
+ ///
+ /// Globally registered instance of the ComWrappers class for marshalling.
+ ///
+ private static ComWrappers? s_globalInstanceForMarshalling;
///
/// Create a COM representation of the supplied object that can be passed to a non-managed environment.
@@ -164,10 +179,23 @@ namespace System.Runtime.InteropServices
///
protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count);
- // Call to execute the abstract instance function
- internal static unsafe void* CallComputeVtables(ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count)
+ // Called by the runtime to execute the abstract instance function
+ internal static unsafe void* CallComputeVtables(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count)
{
- ComWrappers? impl = comWrappersImpl ?? s_globalInstance;
+ ComWrappers? impl = null;
+ switch (scenario)
+ {
+ case ComWrappersScenario.Instance:
+ impl = comWrappersImpl;
+ break;
+ case ComWrappersScenario.TrackerSupportGlobalInstance:
+ impl = s_globalInstanceForTrackerSupport;
+ break;
+ case ComWrappersScenario.MarshallingGlobalInstance:
+ impl = s_globalInstanceForMarshalling;
+ break;
+ }
+
if (impl is null)
{
count = -1;
@@ -203,10 +231,23 @@ namespace System.Runtime.InteropServices
///
protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags);
- // Call to execute the abstract instance function
- internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags)
+ // Called by the runtime to execute the abstract instance function.
+ internal static object? CallCreateObject(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags)
{
- ComWrappers? impl = comWrappersImpl ?? s_globalInstance;
+ ComWrappers? impl = null;
+ switch (scenario)
+ {
+ case ComWrappersScenario.Instance:
+ impl = comWrappersImpl;
+ break;
+ case ComWrappersScenario.TrackerSupportGlobalInstance:
+ impl = s_globalInstanceForTrackerSupport;
+ break;
+ case ComWrappersScenario.MarshallingGlobalInstance:
+ impl = s_globalInstanceForMarshalling;
+ break;
+ }
+
if (impl == null)
return null;
@@ -268,32 +309,62 @@ namespace System.Runtime.InteropServices
// Call to execute the virtual instance function
internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerable objects)
- => (comWrappersImpl ?? s_globalInstance!).ReleaseObjects(objects);
+ => (comWrappersImpl ?? s_globalInstanceForTrackerSupport!).ReleaseObjects(objects);
///
- /// Register this class's implementation to be used as the single global instance.
+ /// Register a instance to be used as the global instance for reference tracker support.
///
+ /// Instance to register
///
/// This function can only be called a single time. Subsequent calls to this function will result
/// in a being thrown.
///
- /// Scenarios where the global instance may be used are:
+ /// Scenarios where this global instance may be used are:
/// * Object tracking via the and flags.
- /// * Usage of COM related Marshal APIs.
///
- public void RegisterAsGlobalInstance()
+ public static void RegisterForTrackerSupport(ComWrappers instance)
{
- if (null != Interlocked.CompareExchange(ref s_globalInstance, this, null))
+ if (instance == null)
+ throw new ArgumentNullException(nameof(instance));
+
+ if (null != Interlocked.CompareExchange(ref s_globalInstanceForTrackerSupport, instance, null))
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance);
+ }
+ }
+
+ ///
+ /// Register a instance to be used as the global instance for marshalling in the runtime.
+ ///
+ /// Instance to register
+ ///
+ /// This function can only be called a single time. Subsequent calls to this function will result
+ /// in a being thrown.
+ ///
+ /// Scenarios where this global instance may be used are:
+ /// * Usage of COM-related Marshal APIs
+ /// * P/Invokes with COM-related types
+ /// * COM activation
+ ///
+ public static void RegisterForMarshalling(ComWrappers instance)
+ {
+ if (instance == null)
+ throw new ArgumentNullException(nameof(instance));
+
+ if (null != Interlocked.CompareExchange(ref s_globalInstanceForMarshalling, instance, null))
{
throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance);
}
- SetGlobalInstanceRegistered();
+ // Indicate to the runtime that a global instance has been registered for marshalling.
+ // This allows the native runtime know to call into the managed ComWrappers only if a
+ // global instance is registered for marshalling.
+ SetGlobalInstanceRegisteredForMarshalling();
}
[DllImport(RuntimeHelpers.QCall)]
[SuppressGCTransition]
- private static extern void SetGlobalInstanceRegistered();
+ private static extern void SetGlobalInstanceRegisteredForMarshalling();
///
/// Get the runtime provided IUnknown implementation.
diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h
index 89d6bd5..87aa7b6 100644
--- a/src/coreclr/src/vm/ecalllist.h
+++ b/src/coreclr/src/vm/ecalllist.h
@@ -985,7 +985,7 @@ FCFuncStart(gComWrappersFuncs)
QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl)
QCFuncElement("TryGetOrCreateComInterfaceForObjectInternal", ComWrappersNative::TryGetOrCreateComInterfaceForObject)
QCFuncElement("TryGetOrCreateObjectForComInstanceInternal", ComWrappersNative::TryGetOrCreateObjectForComInstance)
- QCFuncElement("SetGlobalInstanceRegistered", GlobalComWrappers::SetGlobalInstanceRegistered)
+ QCFuncElement("SetGlobalInstanceRegisteredForMarshalling", GlobalComWrappersForMarshalling::SetGlobalInstanceRegisteredForMarshalling)
FCFuncEnd()
#endif // FEATURE_COMWRAPPERS
diff --git a/src/coreclr/src/vm/interopconverter.cpp b/src/coreclr/src/vm/interopconverter.cpp
index 44470d7..64312e1 100644
--- a/src/coreclr/src/vm/interopconverter.cpp
+++ b/src/coreclr/src/vm/interopconverter.cpp
@@ -28,7 +28,7 @@ namespace
_Outptr_ IUnknown** wrapperRaw)
{
#ifdef FEATURE_COMWRAPPERS
- return GlobalComWrappers::TryGetOrCreateComInterfaceForObject(instance, (void**)wrapperRaw);
+ return GlobalComWrappersForMarshalling::TryGetOrCreateComInterfaceForObject(instance, (void**)wrapperRaw);
#else
return false;
#endif // FEATURE_COMWRAPPERS
@@ -40,7 +40,7 @@ namespace
_Out_ OBJECTREF *pObjOut)
{
#ifdef FEATURE_COMWRAPPERS
- return GlobalComWrappers::TryGetOrCreateObjectForComInstance(pUnknown, dwFlags, pObjOut);
+ return GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(pUnknown, dwFlags, pObjOut);
#else
return false;
#endif // FEATURE_COMWRAPPERS
diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp
index 48bdbe7..934e0bb 100644
--- a/src/coreclr/src/vm/interoplibinterface.cpp
+++ b/src/coreclr/src/vm/interoplibinterface.cpp
@@ -406,7 +406,17 @@ namespace
// Defined handle types for the specific object uses.
const HandleType InstanceHandleType{ HNDTYPE_STRONG };
+ // Scenarios for ComWrappers usage.
+ // These values should match the managed definition in ComWrappers.
+ enum class ComWrappersScenario
+ {
+ Instance = 0,
+ TrackerSupportGlobalInstance = 1,
+ MarshallingGlobalInstance = 2,
+ };
+
void* CallComputeVTables(
+ _In_ ComWrappersScenario scenario,
_In_ OBJECTREF* implPROTECTED,
_In_ OBJECTREF* instancePROTECTED,
_In_ INT32 flags,
@@ -425,17 +435,19 @@ namespace
void* vtables = NULL;
PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES);
- DECLARE_ARGHOLDER_ARRAY(args, 4);
- args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
- args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*instancePROTECTED);
- args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags);
- args[ARGNUM_3] = PTR_TO_ARGHOLDER(vtableCount);
+ DECLARE_ARGHOLDER_ARRAY(args, 5);
+ args[ARGNUM_0] = DWORD_TO_ARGHOLDER(scenario);
+ args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
+ args[ARGNUM_2] = OBJECTREF_TO_ARGHOLDER(*instancePROTECTED);
+ args[ARGNUM_3] = DWORD_TO_ARGHOLDER(flags);
+ args[ARGNUM_4] = PTR_TO_ARGHOLDER(vtableCount);
CALL_MANAGED_METHOD(vtables, void*, args);
return vtables;
}
- OBJECTREF CallGetObject(
+ OBJECTREF CallCreateObject(
+ _In_ ComWrappersScenario scenario,
_In_ OBJECTREF* implPROTECTED,
_In_ IUnknown* externalComObject,
_In_ INT32 flags)
@@ -452,10 +464,11 @@ namespace
OBJECTREF retObjRef;
PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT);
- DECLARE_ARGHOLDER_ARRAY(args, 3);
- args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
- args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject);
- args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags);
+ DECLARE_ARGHOLDER_ARRAY(args, 4);
+ args[ARGNUM_0] = DWORD_TO_ARGHOLDER(scenario);
+ args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
+ args[ARGNUM_2] = PTR_TO_ARGHOLDER(externalComObject);
+ args[ARGNUM_3] = DWORD_TO_ARGHOLDER(flags);
CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args);
return retObjRef;
@@ -511,6 +524,7 @@ namespace
_In_opt_ OBJECTREF impl,
_In_ OBJECTREF instance,
_In_ CreateComInterfaceFlags flags,
+ _In_ ComWrappersScenario scenario,
_Outptr_ void** wrapperRaw)
{
CONTRACT(bool)
@@ -519,6 +533,7 @@ namespace
MODE_COOPERATIVE;
PRECONDITION(instance != NULL);
PRECONDITION(wrapperRaw != NULL);
+ PRECONDITION((impl != NULL && scenario == ComWrappersScenario::Instance)|| (impl == NULL && scenario != ComWrappersScenario::Instance));
}
CONTRACT_END;
@@ -552,7 +567,7 @@ namespace
// is taken. However, a key assumption here is that the returned memory will be
// idempotent for the same object.
DWORD vtableCount;
- void* vtables = CallComputeVTables(&gc.implRef, &gc.instRef, flags, &vtableCount);
+ void* vtables = CallComputeVTables(scenario, &gc.implRef, &gc.instRef, flags, &vtableCount);
// Re-query the associated InteropSyncBlockInfo for an existing managed object wrapper.
if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRawMaybe)
@@ -621,16 +636,11 @@ namespace
RETURN (wrapperRawMaybe != NULL);
}
- // The unwrap parameter indicates whether or not COM instances that are actually CCWs should
- // be unwrapped to the original managed object.
- // For implicit usage of ComWrappers (i.e. automatically called by the runtime when there is a global instance),
- // CCWs should be unwrapped to allow for round-tripping object -> COM instance -> object.
- // For explicit usage of ComWrappers (i.e. directly called via a ComWrappers APIs), CCWs should not be unwrapped.
bool TryGetOrCreateObjectForComInstanceInternal(
_In_opt_ OBJECTREF impl,
_In_ IUnknown* identity,
_In_ CreateObjectFlags flags,
- _In_ bool unwrap,
+ _In_ ComWrappersScenario scenario,
_In_opt_ OBJECTREF wrapperMaybe,
_Out_ OBJECTREF* objRef)
{
@@ -640,6 +650,7 @@ namespace
MODE_COOPERATIVE;
PRECONDITION(identity != NULL);
PRECONDITION(objRef != NULL);
+ PRECONDITION((impl != NULL && scenario == ComWrappersScenario::Instance) || (impl == NULL && scenario != ComWrappersScenario::Instance));
}
CONTRACT_END;
@@ -670,8 +681,10 @@ namespace
extObjCxt = cache->Find(identity);
// If is no object found in the cache, check if the object COM instance is actually the CCW
- // representing a managed object.
- if (extObjCxt == NULL && unwrap)
+ // representing a managed object. For the scenario of marshalling through a global instance,
+ // COM instances that are actually CCWs should be unwrapped to the original managed object
+ // to allow for round-tripping object -> COM instance -> object.
+ if (extObjCxt == NULL && scenario == ComWrappersScenario::MarshallingGlobalInstance)
{
// If the COM instance is a CCW that is not COM-activated, use the object of that wrapper object.
InteropLib::OBJECTHANDLE handleLocal;
@@ -717,7 +730,7 @@ namespace
// If the wrapper hasn't been set yet, call the implementation to create one.
if (gc.objRefMaybe == NULL)
{
- gc.objRefMaybe = CallGetObject(&gc.implRef, identity, flags);
+ gc.objRefMaybe = CallCreateObject(scenario, &gc.implRef, identity, flags);
}
// The object may be null if the specified ComWrapper implementation returns null
@@ -1017,14 +1030,13 @@ namespace InteropLibImports
gc.implRef = NULL; // Use the globally registered implementation.
gc.wrapperMaybeRef = NULL; // No supplied wrapper here.
- bool unwrapIfManagedObjectWrapper = false; // Don't unwrap CCWs
// Get wrapper for external object
bool success = TryGetOrCreateObjectForComInstanceInternal(
gc.implRef,
externalComObject,
externalObjectFlags,
- unwrapIfManagedObjectWrapper,
+ ComWrappersScenario::TrackerSupportGlobalInstance,
gc.wrapperMaybeRef,
&gc.objRef);
@@ -1036,6 +1048,7 @@ namespace InteropLibImports
gc.implRef,
gc.objRef,
trackerTargetFlags,
+ ComWrappersScenario::TrackerSupportGlobalInstance,
trackerTarget);
if (!success)
@@ -1220,6 +1233,7 @@ BOOL QCALLTYPE ComWrappersNative::TryGetOrCreateComInterfaceForObject(
ObjectToOBJECTREF(*comWrappersImpl.m_ppObject),
ObjectToOBJECTREF(*instance.m_ppObject),
(CreateComInterfaceFlags)flags,
+ ComWrappersScenario::Instance,
wrapper);
}
@@ -1256,13 +1270,12 @@ BOOL QCALLTYPE ComWrappersNative::TryGetOrCreateObjectForComInstance(
{
GCX_COOP();
- bool unwrapIfManagedObjectWrapper = false; // Don't unwrap CCWs
OBJECTREF newObj;
success = TryGetOrCreateObjectForComInstanceInternal(
ObjectToOBJECTREF(*comWrappersImpl.m_ppObject),
identity,
(CreateObjectFlags)flags,
- unwrapIfManagedObjectWrapper,
+ ComWrappersScenario::Instance,
ObjectToOBJECTREF(*wrapperMaybe.m_ppObject),
&newObj);
@@ -1369,7 +1382,7 @@ void ComWrappersNative::MarkWrapperAsComActivated(_In_ IUnknown* wrapperMaybe)
_ASSERTE(SUCCEEDED(hr) || hr == E_INVALIDARG);
}
-void QCALLTYPE GlobalComWrappers::SetGlobalInstanceRegistered()
+void QCALLTYPE GlobalComWrappersForMarshalling::SetGlobalInstanceRegisteredForMarshalling()
{
// QCALL contracts are not used here because the managed declaration
// uses the SuppressGCTransition attribute
@@ -1378,7 +1391,7 @@ void QCALLTYPE GlobalComWrappers::SetGlobalInstanceRegistered()
g_IsGlobalComWrappersRegistered = true;
}
-bool GlobalComWrappers::TryGetOrCreateComInterfaceForObject(
+bool GlobalComWrappersForMarshalling::TryGetOrCreateComInterfaceForObject(
_In_ OBJECTREF instance,
_Outptr_ void** wrapperRaw)
{
@@ -1397,11 +1410,12 @@ bool GlobalComWrappers::TryGetOrCreateComInterfaceForObject(
NULL,
instance,
flags,
+ ComWrappersScenario::MarshallingGlobalInstance,
wrapperRaw);
}
}
-bool GlobalComWrappers::TryGetOrCreateObjectForComInstance(
+bool GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(
_In_ IUnknown* externalComObject,
_In_ INT32 objFromComIPFlags,
_Out_ OBJECTREF* objRef)
@@ -1427,16 +1441,12 @@ bool GlobalComWrappers::TryGetOrCreateObjectForComInstance(
if ((objFromComIPFlags & ObjFromComIP::UNIQUE_OBJECT) != 0)
flags |= CreateObjectFlags::CreateObjectFlags_UniqueInstance;
- // For implicit usage of ComWrappers (i.e. automatically called by the runtime when there is a global instance),
- // unwrap CCWs to allow for round-tripping object -> COM instance -> object.
- bool unwrapIfManagedObjectWrapper = true;
-
// Passing NULL as the ComWrappers implementation indicates using the globally registered instance
return TryGetOrCreateObjectForComInstanceInternal(
NULL /*comWrappersImpl*/,
identity,
(CreateObjectFlags)flags,
- unwrapIfManagedObjectWrapper,
+ ComWrappersScenario::MarshallingGlobalInstance,
NULL /*wrapperMaybe*/,
objRef);
}
diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h
index 2985412..6a5884a 100644
--- a/src/coreclr/src/vm/interoplibinterface.h
+++ b/src/coreclr/src/vm/interoplibinterface.h
@@ -39,14 +39,15 @@ public: // COM activation
static void MarkWrapperAsComActivated(_In_ IUnknown* wrapperMaybe);
};
-class GlobalComWrappers
+class GlobalComWrappersForMarshalling
{
public:
- // Native QCall for the ComWrappers managed type to indicate a global instance is registered
- // This should be set if the private static member representing the global instance on ComWrappers is non-null.
- static void QCALLTYPE SetGlobalInstanceRegistered();
+ // Native QCall for the ComWrappers managed type to indicate a global instance
+ // is registered for marshalling. This should be set if the private static member
+ // representing the global instance for marshalling on ComWrappers is non-null.
+ static void QCALLTYPE SetGlobalInstanceRegisteredForMarshalling();
-public: // Functions operating on a registered global instance
+public: // Functions operating on a registered global instance for marshalling
static bool TryGetOrCreateComInterfaceForObject(
_In_ OBJECTREF instance,
_Outptr_ void** wrapperRaw);
diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h
index 6447dc7..30ca8fe 100644
--- a/src/coreclr/src/vm/metasig.h
+++ b/src/coreclr/src/vm/metasig.h
@@ -196,8 +196,8 @@ DEFINE_METASIG_T(SM(PtrTypeName_ArrType_RetVoid, P(g(TYPENAMENATIVE)) a(C(TYPE))
DEFINE_METASIG_T(SM(PtrTypeName_RetVoid, P(g(TYPENAMENATIVE)), v))
DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v))
DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION)))
-DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v)))
-DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j))
+DEFINE_METASIG_T(SM(Scenario_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, g(COMWRAPPERSSCENARIO) C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v)))
+DEFINE_METASIG_T(SM(Scenario_ComWrappers_IntPtr_CreateFlags_RetObj, g(COMWRAPPERSSCENARIO) C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j))
DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v))
DEFINE_METASIG_T(SM(Obj_RefGuid_RefIntPtr_RetInt, j r(g(GUID)) r(I), i))
#endif // FEATURE_COMINTEROP
diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h
index 5317a56..7c02584 100644
--- a/src/coreclr/src/vm/mscorlib.h
+++ b/src/coreclr/src/vm/mscorlib.h
@@ -462,8 +462,9 @@ DEFINE_CLASS(CUSTOMQUERYINTERFACERESULT, Interop, CustomQueryInterface
DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers)
DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags)
DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags)
-DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid)
-DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_CreateFlags_RetObj)
+DEFINE_CLASS(COMWRAPPERSSCENARIO, Interop, ComWrappersScenario)
+DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_Scenario_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid)
+DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_Scenario_ComWrappers_IntPtr_CreateFlags_RetObj)
DEFINE_METHOD(COMWRAPPERS, RELEASE_OBJECTS, CallReleaseObjects, SM_ComWrappers_IEnumerable_RetVoid)
DEFINE_METHOD(COMWRAPPERS, CALL_ICUSTOMQUERYINTERFACE, CallICustomQueryInterface, SM_Obj_RefGuid_RefIntPtr_RetInt)
#endif //FEATURE_COMINTEROP
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.Marshalling.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.Marshalling.cs
new file mode 100644
index 0000000..b2315ff
--- /dev/null
+++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.Marshalling.cs
@@ -0,0 +1,56 @@
+// 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.
+
+namespace ComWrappersTests.GlobalInstance
+{
+ using System;
+
+ using ComWrappersTests.Common;
+ using TestLibrary;
+
+ partial class Program
+ {
+ private static void ValidateNotRegisteredForTrackerSupport()
+ {
+ Console.WriteLine($"Running {nameof(ValidateNotRegisteredForTrackerSupport)}...");
+
+ int hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread();
+ Assert.AreNotEqual(GlobalComWrappers.ReleaseObjectsCallAck, hr);
+ }
+
+ static int Main(string[] doNotUse)
+ {
+ try
+ {
+ // The first test registers a global ComWrappers instance for marshalling
+ // Subsequents tests assume the global instance has already been registered.
+ ValidateRegisterForMarshalling();
+
+ ValidateMarshalAPIs(validateUseRegistered: true);
+ ValidateMarshalAPIs(validateUseRegistered: false);
+
+ ValidatePInvokes(validateUseRegistered: true);
+ ValidatePInvokes(validateUseRegistered: false);
+
+ ValidateComActivation(validateUseRegistered: true);
+ ValidateComActivation(validateUseRegistered: false);
+
+ ValidateNotRegisteredForTrackerSupport();
+
+ // Register a global ComWrappers instance for tracker support
+ ValidateRegisterForTrackerSupport();
+
+ ValidateNotifyEndOfReferenceTrackingOnThread();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+
+ return 100;
+ }
+ }
+}
+
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.TrackerSupport.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.TrackerSupport.cs
new file mode 100644
index 0000000..4d4f1c6
--- /dev/null
+++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.TrackerSupport.cs
@@ -0,0 +1,68 @@
+// 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.
+
+namespace ComWrappersTests.GlobalInstance
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+ using ComWrappersTests.Common;
+ using TestLibrary;
+
+ partial class Program
+ {
+ private static void ValidateNotRegisteredForMarshalling()
+ {
+ Console.WriteLine($"Running {nameof(ValidateNotRegisteredForMarshalling)}...");
+
+ var testObj = new Test();
+ IntPtr comWrapper1 = Marshal.GetIUnknownForObject(testObj);
+ Assert.IsNull(GlobalComWrappers.Instance.LastComputeVtablesObject, "ComWrappers instance should not have been called");
+
+ IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+ object objWrapper = Marshal.GetObjectForIUnknown(trackerObjRaw);
+ Assert.IsFalse(objWrapper is FakeWrapper, $"ComWrappers instance should not have been called");
+ }
+
+ static int Main(string[] doNotUse)
+ {
+ try
+ {
+ // The first test registers a global ComWrappers instance for tracker support.
+ // Subsequents tests assume the global instance has already been registered.
+ ValidateRegisterForTrackerSupport();
+
+ ValidateNotRegisteredForMarshalling();
+
+ IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+ var trackerObj = GlobalComWrappers.Instance.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject);
+ Marshal.Release(trackerObjRaw);
+
+ ValidateNotifyEndOfReferenceTrackingOnThread();
+
+ // Register a global ComWrappers instance for marshalling.
+ ValidateRegisterForMarshalling();
+
+ ValidateMarshalAPIs(validateUseRegistered: true);
+ ValidateMarshalAPIs(validateUseRegistered: false);
+
+ ValidatePInvokes(validateUseRegistered: true);
+ ValidatePInvokes(validateUseRegistered: false);
+
+ ValidateComActivation(validateUseRegistered: true);
+ ValidateComActivation(validateUseRegistered: false);
+
+ ValidateNotifyEndOfReferenceTrackingOnThread();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+
+ return 100;
+ }
+ }
+}
+
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs
similarity index 93%
rename from src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/Program.cs
rename to src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs
index 168c123..ef9870b0 100644
--- a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/Program.cs
+++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs
@@ -12,7 +12,7 @@ namespace ComWrappersTests.GlobalInstance
using ComWrappersTests.Common;
using TestLibrary;
- class Program
+ partial class Program
{
struct MarshalInterface
{
@@ -105,7 +105,7 @@ namespace ComWrappersTests.GlobalInstance
public bool ReturnInvalid { get; set; }
- public object LastComputeVtablesObject { get; private set; }
+ public object LastComputeVtablesObject { get; private set; } = null;
protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
{
@@ -227,23 +227,43 @@ namespace ComWrappersTests.GlobalInstance
}
}
- private static void ValidateRegisterAsGlobalInstance()
+ private static void ValidateRegisterForMarshalling()
{
- Console.WriteLine($"Running {nameof(ValidateRegisterAsGlobalInstance)}...");
+ Console.WriteLine($"Running {nameof(ValidateRegisterForMarshalling)}...");
var wrappers1 = GlobalComWrappers.Instance;
- wrappers1.RegisterAsGlobalInstance();
+ ComWrappers.RegisterForMarshalling(wrappers1);
Assert.Throws(
() =>
{
- wrappers1.RegisterAsGlobalInstance();
+ ComWrappers.RegisterForMarshalling(wrappers1);
}, "Should not be able to re-register for global ComWrappers");
var wrappers2 = new GlobalComWrappers();
Assert.Throws(
() =>
{
- wrappers2.RegisterAsGlobalInstance();
+ ComWrappers.RegisterForMarshalling(wrappers2);
+ }, "Should not be able to reset for global ComWrappers");
+ }
+
+ private static void ValidateRegisterForTrackerSupport()
+ {
+ Console.WriteLine($"Running {nameof(ValidateRegisterForTrackerSupport)}...");
+
+ var wrappers1 = GlobalComWrappers.Instance;
+ ComWrappers.RegisterForTrackerSupport(wrappers1);
+ Assert.Throws(
+ () =>
+ {
+ ComWrappers.RegisterForTrackerSupport(wrappers1);
+ }, "Should not be able to re-register for global ComWrappers");
+
+ var wrappers2 = new GlobalComWrappers();
+ Assert.Throws(
+ () =>
+ {
+ ComWrappers.RegisterForTrackerSupport(wrappers2);
}, "Should not be able to reset for global ComWrappers");
}
@@ -418,34 +438,6 @@ namespace ComWrappersTests.GlobalInstance
int hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread();
Assert.AreEqual(GlobalComWrappers.ReleaseObjectsCallAck, hr);
}
-
- static int Main(string[] doNotUse)
- {
- try
- {
- // The first test registereds a global ComWrappers instance
- // Subsequents tests assume the global instance has already been registered.
- ValidateRegisterAsGlobalInstance();
-
- ValidateMarshalAPIs(validateUseRegistered: true);
- ValidateMarshalAPIs(validateUseRegistered: false);
-
- ValidatePInvokes(validateUseRegistered: true);
- ValidatePInvokes(validateUseRegistered: false);
-
- ValidateComActivation(validateUseRegistered: true);
- ValidateComActivation(validateUseRegistered: false);
-
- ValidateNotifyEndOfReferenceTrackingOnThread();
- }
- catch (Exception e)
- {
- Console.WriteLine($"Test Failure: {e}");
- return 101;
- }
-
- return 100;
- }
}
}
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTests.csproj b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceMarshallingTests.csproj
similarity index 95%
rename from src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTests.csproj
rename to src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceMarshallingTests.csproj
index 205811d..f190505 100644
--- a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTests.csproj
+++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceMarshallingTests.csproj
@@ -15,7 +15,8 @@
-
+
+
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTrackerSupportTests.csproj b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTrackerSupportTests.csproj
new file mode 100644
index 0000000..4742bf4
--- /dev/null
+++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTrackerSupportTests.csproj
@@ -0,0 +1,38 @@
+
+
+ Exe
+ App.manifest
+ true
+ true
+
+ BuildOnly
+
+ true
+ true
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ Content
+ Always
+
+
+
+
+ PreserveNewest
+
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs
index 05764b0..d78f3d6 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs
@@ -62,7 +62,12 @@ namespace System.Runtime.InteropServices
protected abstract void ReleaseObjects(IEnumerable objects);
- public void RegisterAsGlobalInstance()
+ public static void RegisterForTrackerSupport(ComWrappers instance)
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ public static void RegisterForMarshalling(ComWrappers instance)
{
throw new PlatformNotSupportedException();
}
diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
index 965afd8..d2a0de7 100644
--- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
+++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
@@ -1006,7 +1006,8 @@ namespace System.Runtime.InteropServices
protected abstract object CreateObject(System.IntPtr externalComObject, CreateObjectFlags flags);
public object GetOrRegisterObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object wrapper) { throw null; }
protected abstract void ReleaseObjects(System.Collections.IEnumerable objects);
- public void RegisterAsGlobalInstance() { }
+ public static void RegisterForTrackerSupport(ComWrappers instance) { }
+ public static void RegisterForMarshalling(ComWrappers instance) { }
protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Method)]
--
2.7.4