From 44cf9b2d0de10e3783763d0191860384db4311ba Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 30 Apr 2020 12:27:09 -0700 Subject: [PATCH 1/2] Separate registration for global ComWrappers instance for tracker support and marshalling --- .../Runtime/InteropServices/ComWrappers.cs | 100 +++++++++++++++--- src/coreclr/src/vm/ecalllist.h | 2 +- src/coreclr/src/vm/interopconverter.cpp | 4 +- src/coreclr/src/vm/interoplibinterface.cpp | 62 ++++++----- src/coreclr/src/vm/interoplibinterface.h | 11 +- src/coreclr/src/vm/metasig.h | 4 +- src/coreclr/src/vm/mscorlib.h | 5 +- .../GlobalInstance.Marshalling.cs | 56 ++++++++++ .../GlobalInstance.TrackerSupport.cs | 68 ++++++++++++ .../{Program.cs => GlobalInstance.cs} | 62 +++++------ ... => GlobalInstanceMarshallingTests.csproj} | 3 +- .../GlobalInstanceTrackerSupportTests.csproj | 38 +++++++ .../ComWrappers.PlatformNotSupported.cs | 7 +- .../ref/System.Runtime.InteropServices.cs | 3 +- 14 files changed, 333 insertions(+), 92 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 0229ac9c0dbc5..d92604a79705c 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 @@ -56,6 +56,16 @@ public enum CreateObjectFlags UniqueInstance = 2, } + /// + /// 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. /// @@ -107,9 +117,14 @@ private struct ComInterfaceInstance } /// - /// 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 @@ private static bool TryGetOrCreateComInterfaceForObjectInternal(ComWrappers? imp /// 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(ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, ComWrappersScenario scenario, 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 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb /// 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(ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ComWrappersScenario scenario) { - 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,59 @@ private static bool TryGetOrCreateObjectForComInstanceInternal(ComWrappers? impl // 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(); + 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 661cfad2907e4..127ce407ba8c3 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -984,7 +984,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 44470d7d297b4..64312e1438214 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 48bdbe74555f0..f572a0eee0420 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -406,10 +406,20 @@ 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 ComWrappersScenario + { + Instance = 0, + TrackerSupportGlobalInstance = 1, + MarshallingGlobalInstance = 2, + }; + void* CallComputeVTables( _In_ OBJECTREF* implPROTECTED, _In_ OBJECTREF* instancePROTECTED, _In_ INT32 flags, + _In_ ComWrappersScenario scenario, _Out_ DWORD* vtableCount) { CONTRACTL @@ -425,11 +435,12 @@ namespace void* vtables = NULL; PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES); - DECLARE_ARGHOLDER_ARRAY(args, 4); + DECLARE_ARGHOLDER_ARRAY(args, 5); 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); + args[ARGNUM_3] = DWORD_TO_ARGHOLDER(scenario); + args[ARGNUM_4] = PTR_TO_ARGHOLDER(vtableCount); CALL_MANAGED_METHOD(vtables, void*, args); return vtables; @@ -438,7 +449,8 @@ namespace OBJECTREF CallGetObject( _In_ OBJECTREF* implPROTECTED, _In_ IUnknown* externalComObject, - _In_ INT32 flags) + _In_ INT32 flags, + _In_ ComWrappersScenario scenario) { CONTRACTL { @@ -452,10 +464,11 @@ namespace OBJECTREF retObjRef; PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); - DECLARE_ARGHOLDER_ARRAY(args, 3); + DECLARE_ARGHOLDER_ARRAY(args, 4); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); + args[ARGNUM_3] = DWORD_TO_ARGHOLDER(scenario); 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(&gc.implRef, &gc.instRef, flags, scenario, &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 = CallGetObject(&gc.implRef, identity, flags, scenario); } // 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 2985412db4b29..6a5884a66075c 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -39,14 +39,15 @@ class ComWrappersNative 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 6447dc7c7e83b..19eff0782a407 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(ComWrappers_Obj_CreateFlags_Scenario_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) g(COMWRAPPERSSCENARIO) r(i), P(v))) +DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_Scenario_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS) g(COMWRAPPERSSCENARIO), 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 5317a56786381..b91ceab27282f 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_ComWrappers_Obj_CreateFlags_Scenario_RefInt_RetPtrVoid) +DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_CreateFlags_Scenario_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 0000000000000..b2315ff4e6eaa --- /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 0000000000000..4d4f1c6e93474 --- /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 168c1230ffbf3..ef9870b08672d 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 @@ class GlobalComWrappers : ComWrappers 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 @@ protected override void ReleaseObjects(IEnumerable objects) } } - 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 @@ private static void ValidateNotifyEndOfReferenceTrackingOnThread() 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 205811d912c5b..f190505f83cf2 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 0000000000000..4742bf4b547f3 --- /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 05764b08bac53..d78f3d6681fac 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 @@ public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, Create 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 965afd86333ab..d2a0de7091b94 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 @@ public struct ComInterfaceDispatch 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)] From 1215c7ad87cc6c08c7e7996ee047ba47292414a0 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 30 Apr 2020 18:17:01 -0700 Subject: [PATCH 2/2] PR feedback --- .../Runtime/InteropServices/ComWrappers.cs | 7 +++-- src/coreclr/src/vm/interoplibinterface.cpp | 30 +++++++++---------- src/coreclr/src/vm/metasig.h | 4 +-- src/coreclr/src/vm/mscorlib.h | 4 +-- 4 files changed, 24 insertions(+), 21 deletions(-) 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 d92604a79705c..323e60a564801 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 @@ -180,7 +180,7 @@ private static bool TryGetOrCreateComInterfaceForObjectInternal(ComWrappers? imp protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count); // Called by the runtime to execute the abstract instance function - internal static unsafe void* CallComputeVtables(ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, ComWrappersScenario scenario, out int count) + internal static unsafe void* CallComputeVtables(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count) { ComWrappers? impl = null; switch (scenario) @@ -232,7 +232,7 @@ public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateOb protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags); // Called by the runtime to execute the abstract instance function. - internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ComWrappersScenario scenario) + internal static object? CallCreateObject(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags) { ComWrappers? impl = null; switch (scenario) @@ -356,6 +356,9 @@ public static void RegisterForMarshalling(ComWrappers instance) throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance); } + // 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(); } diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index f572a0eee0420..934e0bbf6fadc 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -408,7 +408,7 @@ namespace // Scenarios for ComWrappers usage. // These values should match the managed definition in ComWrappers. - enum ComWrappersScenario + enum class ComWrappersScenario { Instance = 0, TrackerSupportGlobalInstance = 1, @@ -416,10 +416,10 @@ namespace }; void* CallComputeVTables( + _In_ ComWrappersScenario scenario, _In_ OBJECTREF* implPROTECTED, _In_ OBJECTREF* instancePROTECTED, _In_ INT32 flags, - _In_ ComWrappersScenario scenario, _Out_ DWORD* vtableCount) { CONTRACTL @@ -436,21 +436,21 @@ namespace PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES); DECLARE_ARGHOLDER_ARRAY(args, 5); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); - args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*instancePROTECTED); - args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); - args[ARGNUM_3] = DWORD_TO_ARGHOLDER(scenario); + 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, - _In_ ComWrappersScenario scenario) + _In_ INT32 flags) { CONTRACTL { @@ -465,10 +465,10 @@ namespace PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT); DECLARE_ARGHOLDER_ARRAY(args, 4); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*implPROTECTED); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(externalComObject); - args[ARGNUM_2] = DWORD_TO_ARGHOLDER(flags); - args[ARGNUM_3] = DWORD_TO_ARGHOLDER(scenario); + 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; @@ -567,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, scenario, &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) @@ -730,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, scenario); + gc.objRefMaybe = CallCreateObject(scenario, &gc.implRef, identity, flags); } // The object may be null if the specified ComWrapper implementation returns null diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index 19eff0782a407..30ca8fe868d8a 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_Scenario_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) g(COMWRAPPERSSCENARIO) r(i), P(v))) -DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_Scenario_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS) g(COMWRAPPERSSCENARIO), 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 b91ceab27282f..7c0258449f0a5 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -463,8 +463,8 @@ DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers) DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags) DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags) DEFINE_CLASS(COMWRAPPERSSCENARIO, Interop, ComWrappersScenario) -DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables, SM_ComWrappers_Obj_CreateFlags_Scenario_RefInt_RetPtrVoid) -DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_ComWrappers_IntPtr_CreateFlags_Scenario_RetObj) +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