}
/// <summary>
+ /// Internal enumeration used by the runtime to indicate the scenario for which ComWrappers is being used.
+ /// </summary>
+ internal enum ComWrappersScenario
+ {
+ Instance = 0,
+ TrackerSupportGlobalInstance = 1,
+ MarshallingGlobalInstance = 2,
+ }
+
+ /// <summary>
/// Class for managing wrappers of COM IUnknown types.
/// </summary>
[CLSCompliant(false)]
}
/// <summary>
- /// Globally registered instance of the ComWrappers class.
+ /// Globally registered instance of the ComWrappers class for reference tracker support.
/// </summary>
- private static ComWrappers? s_globalInstance;
+ private static ComWrappers? s_globalInstanceForTrackerSupport;
+
+ /// <summary>
+ /// Globally registered instance of the ComWrappers class for marshalling.
+ /// </summary>
+ private static ComWrappers? s_globalInstanceForMarshalling;
/// <summary>
/// Create a COM representation of the supplied object that can be passed to a non-managed environment.
/// </remarks>
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;
/// </remarks>
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;
// 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);
/// <summary>
- /// Register this class's implementation to be used as the single global instance.
+ /// Register a <see cref="ComWrappers" /> instance to be used as the global instance for reference tracker support.
/// </summary>
+ /// <param name="instance">Instance to register</param>
/// <remarks>
/// This function can only be called a single time. Subsequent calls to this function will result
/// in a <see cref="System.InvalidOperationException"/> being thrown.
///
- /// Scenarios where the global instance may be used are:
+ /// Scenarios where this global instance may be used are:
/// * Object tracking via the <see cref="CreateComInterfaceFlags.TrackerSupport" /> and <see cref="CreateObjectFlags.TrackerObject" /> flags.
- /// * Usage of COM related Marshal APIs.
/// </remarks>
- 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);
+ }
+ }
+
+ /// <summary>
+ /// Register a <see cref="ComWrappers" /> instance to be used as the global instance for marshalling in the runtime.
+ /// </summary>
+ /// <param name="instance">Instance to register</param>
+ /// <remarks>
+ /// This function can only be called a single time. Subsequent calls to this function will result
+ /// in a <see cref="System.InvalidOperationException"/> 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
+ /// </remarks>
+ 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();
/// <summary>
/// Get the runtime provided IUnknown implementation.
QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl)
QCFuncElement("TryGetOrCreateComInterfaceForObjectInternal", ComWrappersNative::TryGetOrCreateComInterfaceForObject)
QCFuncElement("TryGetOrCreateObjectForComInstanceInternal", ComWrappersNative::TryGetOrCreateObjectForComInstance)
- QCFuncElement("SetGlobalInstanceRegistered", GlobalComWrappers::SetGlobalInstanceRegistered)
+ QCFuncElement("SetGlobalInstanceRegisteredForMarshalling", GlobalComWrappersForMarshalling::SetGlobalInstanceRegisteredForMarshalling)
FCFuncEnd()
#endif // FEATURE_COMWRAPPERS
_Outptr_ IUnknown** wrapperRaw)
{
#ifdef FEATURE_COMWRAPPERS
- return GlobalComWrappers::TryGetOrCreateComInterfaceForObject(instance, (void**)wrapperRaw);
+ return GlobalComWrappersForMarshalling::TryGetOrCreateComInterfaceForObject(instance, (void**)wrapperRaw);
#else
return false;
#endif // FEATURE_COMWRAPPERS
_Out_ OBJECTREF *pObjOut)
{
#ifdef FEATURE_COMWRAPPERS
- return GlobalComWrappers::TryGetOrCreateObjectForComInstance(pUnknown, dwFlags, pObjOut);
+ return GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(pUnknown, dwFlags, pObjOut);
#else
return false;
#endif // FEATURE_COMWRAPPERS
// 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,
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)
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;
_In_opt_ OBJECTREF impl,
_In_ OBJECTREF instance,
_In_ CreateComInterfaceFlags flags,
+ _In_ ComWrappersScenario scenario,
_Outptr_ void** wrapperRaw)
{
CONTRACT(bool)
MODE_COOPERATIVE;
PRECONDITION(instance != NULL);
PRECONDITION(wrapperRaw != NULL);
+ PRECONDITION((impl != NULL && scenario == ComWrappersScenario::Instance)|| (impl == NULL && scenario != ComWrappersScenario::Instance));
}
CONTRACT_END;
// 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)
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)
{
MODE_COOPERATIVE;
PRECONDITION(identity != NULL);
PRECONDITION(objRef != NULL);
+ PRECONDITION((impl != NULL && scenario == ComWrappersScenario::Instance) || (impl == NULL && scenario != ComWrappersScenario::Instance));
}
CONTRACT_END;
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;
// 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
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);
gc.implRef,
gc.objRef,
trackerTargetFlags,
+ ComWrappersScenario::TrackerSupportGlobalInstance,
trackerTarget);
if (!success)
ObjectToOBJECTREF(*comWrappersImpl.m_ppObject),
ObjectToOBJECTREF(*instance.m_ppObject),
(CreateComInterfaceFlags)flags,
+ ComWrappersScenario::Instance,
wrapper);
}
{
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);
_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
g_IsGlobalComWrappersRegistered = true;
}
-bool GlobalComWrappers::TryGetOrCreateComInterfaceForObject(
+bool GlobalComWrappersForMarshalling::TryGetOrCreateComInterfaceForObject(
_In_ OBJECTREF instance,
_Outptr_ void** wrapperRaw)
{
NULL,
instance,
flags,
+ ComWrappersScenario::MarshallingGlobalInstance,
wrapperRaw);
}
}
-bool GlobalComWrappers::TryGetOrCreateObjectForComInstance(
+bool GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(
_In_ IUnknown* externalComObject,
_In_ INT32 objFromComIPFlags,
_Out_ OBJECTREF* objRef)
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);
}
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);
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
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
--- /dev/null
+// 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;
+ }
+ }
+}
+
--- /dev/null
+// 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;
+ }
+ }
+}
+
using ComWrappersTests.Common;
using TestLibrary;
- class Program
+ partial class Program
{
struct MarshalInterface
{
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)
{
}
}
- 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<InvalidOperationException>(
() =>
{
- wrappers1.RegisterAsGlobalInstance();
+ ComWrappers.RegisterForMarshalling(wrappers1);
}, "Should not be able to re-register for global ComWrappers");
var wrappers2 = new GlobalComWrappers();
Assert.Throws<InvalidOperationException>(
() =>
{
- 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<InvalidOperationException>(
+ () =>
+ {
+ ComWrappers.RegisterForTrackerSupport(wrappers1);
+ }, "Should not be able to re-register for global ComWrappers");
+
+ var wrappers2 = new GlobalComWrappers();
+ Assert.Throws<InvalidOperationException>(
+ () =>
+ {
+ ComWrappers.RegisterForTrackerSupport(wrappers2);
}, "Should not be able to reset for global ComWrappers");
}
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;
- }
}
}
</PropertyGroup>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Interop.settings.targets))" />
<ItemGroup>
- <Compile Include="Program.cs" />
+ <Compile Include="GlobalInstance.cs" />
+ <Compile Include="GlobalInstance.Marshalling.cs" />
<Compile Include="../Common.cs" />
<Compile Include="../../ServerContracts/Server.Contracts.cs" />
<Compile Include="../../ServerContracts/ServerGuids.cs" />
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <ApplicationManifest>App.manifest</ApplicationManifest>
+ <CLRTestScriptLocalCoreShim>true</CLRTestScriptLocalCoreShim>
+ <RequiresMockHostPolicy>true</RequiresMockHostPolicy>
+ <!-- Blocked on ILAsm supporting embedding resources. See https://github.com/dotnet/coreclr/issues/20819 -->
+ <IlrtTestKind>BuildOnly</IlrtTestKind>
+ <!-- Test unsupported outside of windows -->
+ <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+ <DisableProjectBuild Condition="'$(TargetsWindows)' != 'true'">true</DisableProjectBuild>
+ <!-- This test would require the runincontext.exe to include App.manifest describing the COM interfaces -->
+ <UnloadabilityIncompatible>true</UnloadabilityIncompatible>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <Import Project="$([MSBuild]::GetPathOfFileAbove(Interop.settings.targets))" />
+ <ItemGroup>
+ <Compile Include="GlobalInstance.cs" />
+ <Compile Include="GlobalInstance.TrackerSupport.cs" />
+ <Compile Include="../Common.cs" />
+ <Compile Include="../../ServerContracts/Server.Contracts.cs" />
+ <Compile Include="../../ServerContracts/ServerGuids.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="../MockReferenceTrackerRuntime/CMakeLists.txt" />
+ <ProjectReference Include="../../NativeServer/CMakeLists.txt" />
+ <ProjectReference Include="../../NETServer/NETServer.csproj">
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ <OutputItemType>Content</OutputItemType>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="CoreShim.X.manifest">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+</Project>
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();
}
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)]