static void ValidateExternalWrapperCacheCleanUp() { Console.WriteLine($"Running {nameof(ValidateExternalWrapperCacheCleanUp)}..."); var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Create a wrapper for the object instance. var weakRef1 = CreateAndRegisterWrapper(cw, trackerObjRaw); // Run the GC to have the wrapper marked for collection. ForceGC(); // Create a new wrapper for the same external object. var weakRef2 = CreateAndRegisterWrapper(cw, trackerObjRaw); // We are using a tracking resurrection WeakReference<T> so we should be able // to get back the objects as they are all continually re-registering for Finalization. Assert.True(weakRef1.TryGetTarget(out ITrackerObjectWrapper wrapper1)); Assert.True(weakRef2.TryGetTarget(out ITrackerObjectWrapper wrapper2)); // Check that the two wrappers aren't equal, meaning we created a new wrapper since // the first wrapper was removed from the internal cache. Assert.NotEqual(wrapper1, wrapper2); // Let the wrappers Finalize. wrapper1.ReregisterForFinalize = false; wrapper2.ReregisterForFinalize = false;
private static void ValidateMarshalAPIs(bool validateUseRegistered) { string scenario = validateUseRegistered ? "use registered wrapper" : "fall back to runtime"; Console.WriteLine($"Running {nameof(ValidateMarshalAPIs)}: {scenario}..."); GlobalComWrappers registeredWrapper = GlobalComWrappers.Instance; registeredWrapper.ReturnInvalid = !validateUseRegistered; Console.WriteLine($" -- Validate Marshal.GetIUnknownForObject..."); var testObj = new Test(); IntPtr comWrapper1 = Marshal.GetIUnknownForObject(testObj); Assert.AreNotEqual(IntPtr.Zero, comWrapper1); Assert.AreEqual(testObj, registeredWrapper.LastComputeVtablesObject, "Registered ComWrappers instance should have been called"); IntPtr comWrapper2 = Marshal.GetIUnknownForObject(testObj); Assert.AreEqual(comWrapper1, comWrapper2); Marshal.Release(comWrapper1); Marshal.Release(comWrapper2); Console.WriteLine($" -- Validate Marshal.GetIDispatchForObject..."); Assert.Throws <InvalidCastException>(() => Marshal.GetIDispatchForObject(testObj)); if (validateUseRegistered) { var dispatchObj = new TestEx(IID_IDISPATCH); IntPtr dispatchWrapper = Marshal.GetIDispatchForObject(dispatchObj); Assert.AreNotEqual(IntPtr.Zero, dispatchWrapper); Assert.AreEqual(dispatchObj, registeredWrapper.LastComputeVtablesObject, "Registered ComWrappers instance should have been called"); } Console.WriteLine($" -- Validate Marshal.GetObjectForIUnknown..."); IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); object objWrapper1 = Marshal.GetObjectForIUnknown(trackerObjRaw); Assert.AreEqual(validateUseRegistered, objWrapper1 is FakeWrapper, $"GetObjectForIUnknown should{(validateUseRegistered ? string.Empty : "not")} have returned {nameof(FakeWrapper)} instance"); object objWrapper2 = Marshal.GetObjectForIUnknown(trackerObjRaw); Assert.AreEqual(objWrapper1, objWrapper2); Console.WriteLine($" -- Validate Marshal.GetUniqueObjectForIUnknown..."); object objWrapper3 = Marshal.GetUniqueObjectForIUnknown(trackerObjRaw); Assert.AreEqual(validateUseRegistered, objWrapper3 is FakeWrapper, $"GetObjectForIUnknown should{(validateUseRegistered ? string.Empty : "not")} have returned {nameof(FakeWrapper)} instance"); Assert.AreNotEqual(objWrapper1, objWrapper3); Marshal.Release(trackerObjRaw); }
static void ValidateRuntimeTrackerScenario() { Console.WriteLine($"Running {nameof(ValidateRuntimeTrackerScenario)}..."); var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Create a managed wrapper for the native object. var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); var testWrapperIds = new List <int>(); for (int i = 0; i < 1000; ++i) { // Create a native wrapper for the managed object. IntPtr testWrapper = cw.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.TrackerSupport); // Pass the managed object to the native object. int id = trackerObj.AddObjectRef(testWrapper); // Retain the managed object wrapper ptr. testWrapperIds.Add(id); Marshal.Release(testWrapper); } Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); GC.Collect(); GC.Collect(); GC.Collect(); GC.Collect(); GC.Collect(); Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount); // Remove the managed object ref from the native object. foreach (int id in testWrapperIds) { trackerObj.DropObjectRef(id); } testWrapperIds.Clear(); GC.Collect(); GC.Collect(); GC.Collect(); GC.Collect(); GC.Collect(); }
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 void ValidateWrappersInstanceIsolation() { Console.WriteLine($"Running {nameof(ValidateWrappersInstanceIsolation)}..."); var cw1 = new TestComWrappers(); var cw2 = new TestComWrappers(); var testObj = new Test(); // Allocate a wrapper for the object IntPtr comWrapper1 = cw1.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); IntPtr comWrapper2 = cw2.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); Assert.AreNotEqual(comWrapper1, IntPtr.Zero); Assert.AreNotEqual(comWrapper2, IntPtr.Zero); Assert.AreNotEqual(comWrapper1, comWrapper2); IntPtr comWrapper3 = cw1.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); IntPtr comWrapper4 = cw2.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport); Assert.AreNotEqual(comWrapper3, comWrapper4); Assert.AreEqual(comWrapper1, comWrapper3); Assert.AreEqual(comWrapper2, comWrapper4); Marshal.Release(comWrapper1); Marshal.Release(comWrapper2); Marshal.Release(comWrapper3); Marshal.Release(comWrapper4); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Create objects for the COM instance var trackerObj1 = (ITrackerObjectWrapper)cw1.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); var trackerObj2 = (ITrackerObjectWrapper)cw2.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Assert.AreNotEqual(trackerObj1, trackerObj2); var trackerObj3 = (ITrackerObjectWrapper)cw1.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); var trackerObj4 = (ITrackerObjectWrapper)cw2.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Assert.AreNotEqual(trackerObj3, trackerObj4); Assert.AreEqual(trackerObj1, trackerObj3); Assert.AreEqual(trackerObj2, trackerObj4); Marshal.Release(trackerObjRaw); }
static void ValidateQueryInterfaceAfterManagedObjectCollected() { Console.WriteLine($"Running {nameof(ValidateQueryInterfaceAfterManagedObjectCollected)}..."); var cw = new TestComWrappers(); { // Activate the Reference Tracker system in the .NET runtime by consuming an IReferenceTracker instance. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Marshal.Release(trackerObjRaw); } int refCount; IntPtr refTrackerTarget; { // Create a native wrapper over a managed object. IntPtr testWrapper = CreateWrapper(cw); refTrackerTarget = MockReferenceTrackerRuntime.TrackerTarget_AddRefFromReferenceTrackerAndReturn(testWrapper); // Ownership has been transferred to the IReferenceTrackerTarget instance. // The COM reference count should be 0 and indicates to the GC the managed object // can be collected. refCount = Marshal.Release(testWrapper); Assert.AreEqual(0, refCount); } ForceGC(); // Calling QueryInterface on an IReferenceTrackerTarget instance is permitted when // the wrapper lifetime has been extended. However, the QueryInterface may fail // if the associated managed object was collected. The failure here is an important // part of the contract for a Reference Tracker runtime. var iid = typeof(ITest).GUID; IntPtr iTestComObject; int hr = Marshal.QueryInterface(refTrackerTarget, ref iid, out iTestComObject); const int COR_E_ACCESSING_CCW = unchecked ((int)0x80131544); Assert.AreEqual(COR_E_ACCESSING_CCW, hr); // Release the IReferenceTrackerTarget instance. refCount = MockReferenceTrackerRuntime.TrackerTarget_ReleaseFromReferenceTracker(refTrackerTarget); Assert.AreEqual(0, refCount);
static void ValidateBadComWrapperImpl() { Console.WriteLine($"Running {nameof(ValidateBadComWrapperImpl)}..."); var wrapper = new BadComWrappers(); Assert.Throws <ArgumentException>( () => { wrapper.ComputeVtablesMode = BadComWrappers.FailureMode.ReturnInvalid; wrapper.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.None); }); try { wrapper.ComputeVtablesMode = BadComWrappers.FailureMode.ThrowException; wrapper.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.None); } catch (Exception e) { Assert.AreEqual(BadComWrappers.ExceptionErrorCode, e.HResult); } IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); Assert.Throws <ArgumentNullException>( () => { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ReturnInvalid; wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); }); try { wrapper.CreateObjectMode = BadComWrappers.FailureMode.ThrowException; wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None); } catch (Exception e) { Assert.AreEqual(BadComWrappers.ExceptionErrorCode, e.HResult); } Marshal.Release(trackerObjRaw); }
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(); #if Windows ValidateNotRegisteredForMarshalling(); #endif IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); var trackerObj = GlobalComWrappers.Instance.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Marshal.Release(trackerObjRaw); ValidateNotifyEndOfReferenceTrackingOnThread(); #if Windows // Register a global ComWrappers instance for marshalling. ValidateRegisterForMarshalling(); ValidateMarshalAPIs(validateUseRegistered: true); ValidateMarshalAPIs(validateUseRegistered: false); ValidatePInvokes(validateUseRegistered: true); ValidatePInvokes(validateUseRegistered: false); // RegFree COM is not supported on Windows Nano Server if (!Utilities.IsWindowsNanoServer) { ValidateComActivation(validateUseRegistered: true); ValidateComActivation(validateUseRegistered: false); } #endif ValidateNotifyEndOfReferenceTrackingOnThread(); } catch (Exception e) { Console.WriteLine($"Test Failure: {e}"); return(101); } return(100); }
static void ValidatePrecreatedExternalWrapper() { Console.WriteLine($"Running {nameof(ValidatePrecreatedExternalWrapper)}..."); var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); // Manually create a wrapper var iid = typeof(ITrackerObject).GUID; IntPtr iTestComObject; int hr = Marshal.QueryInterface(trackerObjRaw, ref iid, out iTestComObject); Assert.AreEqual(hr, 0); var nativeWrapper = new ITrackerObjectWrapper(iTestComObject); // Register wrapper, but supply the wrapper. var nativeWrapper2 = (ITrackerObjectWrapper)cw.GetOrRegisterObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, nativeWrapper); Assert.AreEqual(nativeWrapper, nativeWrapper2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); // Validate reuse of a wrapper fails. IntPtr trackerObjRaw2 = MockReferenceTrackerRuntime.CreateTrackerObject(); Assert.Throws <NotSupportedException>( () => { cw.GetOrRegisterObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, nativeWrapper2); }); Marshal.Release(trackerObjRaw2); // Validate passing null wrapper fails. Assert.Throws <ArgumentNullException>( () => { cw.GetOrRegisterObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null); }); }
static void ValidateCreateObjectCachingScenario() { Console.WriteLine($"Running {nameof(ValidateCreateObjectCachingScenario)}..."); var cw = new TestComWrappers(); // Get an object from a tracker runtime. IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject(); var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject); Assert.AreEqual(trackerObj1, trackerObj2); // Ownership has been transferred to the wrapper. Marshal.Release(trackerObjRaw); var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance); Assert.AreNotEqual(trackerObj1, trackerObj3); }