From a06ae14956613b15c5fbaf81976f790806b1f946 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Oct 2023 16:36:29 -0700 Subject: [PATCH 1/8] When we don't have a managed value in a ClearNative call, the managed poniter to the managed field with a null pointer instead of skipping the ClearNative call. --- src/coreclr/vm/ilmarshalers.h | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/coreclr/vm/ilmarshalers.h b/src/coreclr/vm/ilmarshalers.h index f3c9f31628f156..47fbfcb134c76e 100644 --- a/src/coreclr/vm/ilmarshalers.h +++ b/src/coreclr/vm/ilmarshalers.h @@ -3138,39 +3138,33 @@ class ILMngdMarshaler : public ILMarshaler void EmitClearNative(ILCodeStream* pslILEmit) override { WRAPPER_NO_CONTRACT; - ILCodeLabel* pNoManagedValueLabel = nullptr; if (IsFieldMarshal(m_dwMarshalFlags)) { - pNoManagedValueLabel = pslILEmit->NewCodeLabel(); + ILCodeLabel* pHasManagedValueLabel = pHasManagedValueLabel = pslILEmit->NewCodeLabel(); pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); - pslILEmit->EmitBRFALSE(pNoManagedValueLabel); + pslILEmit->EmitBRTRUE(pHasManagedValueLabel); + pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); + EmitStoreManagedHomeAddr(pslILEmit); + pslILEmit->EmitLabel(pHasManagedValueLabel); } EmitCallMngdMarshalerMethod(pslILEmit, GetClearNativeMethod()); - - if (IsFieldMarshal(m_dwMarshalFlags)) - { - pslILEmit->EmitLabel(pNoManagedValueLabel); - } } void EmitClearNativeContents(ILCodeStream* pslILEmit) override { WRAPPER_NO_CONTRACT; - ILCodeLabel* pNoManagedValueLabel = nullptr; if (IsFieldMarshal(m_dwMarshalFlags)) { - pNoManagedValueLabel = pslILEmit->NewCodeLabel(); + ILCodeLabel* pHasManagedValueLabel = pHasManagedValueLabel = pslILEmit->NewCodeLabel(); pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); - pslILEmit->EmitBRFALSE(pNoManagedValueLabel); + pslILEmit->EmitBRTRUE(pHasManagedValueLabel); + pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); + EmitStoreManagedHomeAddr(pslILEmit); + pslILEmit->EmitLabel(pHasManagedValueLabel); } EmitCallMngdMarshalerMethod(pslILEmit, GetClearNativeContentsMethod()); - - if (IsFieldMarshal(m_dwMarshalFlags)) - { - pslILEmit->EmitLabel(pNoManagedValueLabel); - } } bool NeedsClearCLR() override From 42b89944248616eab8ffa18e860a5dd29e8b3b8e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Oct 2023 16:59:47 -0700 Subject: [PATCH 2/8] Fix duplicate assignment --- src/coreclr/vm/ilmarshalers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/ilmarshalers.h b/src/coreclr/vm/ilmarshalers.h index 47fbfcb134c76e..7cbbef24073e56 100644 --- a/src/coreclr/vm/ilmarshalers.h +++ b/src/coreclr/vm/ilmarshalers.h @@ -3140,7 +3140,7 @@ class ILMngdMarshaler : public ILMarshaler WRAPPER_NO_CONTRACT; if (IsFieldMarshal(m_dwMarshalFlags)) { - ILCodeLabel* pHasManagedValueLabel = pHasManagedValueLabel = pslILEmit->NewCodeLabel(); + ILCodeLabel* pHasManagedValueLabel = pslILEmit->NewCodeLabel(); pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); pslILEmit->EmitBRTRUE(pHasManagedValueLabel); pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); @@ -3156,7 +3156,7 @@ class ILMngdMarshaler : public ILMarshaler WRAPPER_NO_CONTRACT; if (IsFieldMarshal(m_dwMarshalFlags)) { - ILCodeLabel* pHasManagedValueLabel = pHasManagedValueLabel = pslILEmit->NewCodeLabel(); + ILCodeLabel* pHasManagedValueLabel = pslILEmit->NewCodeLabel(); pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); pslILEmit->EmitBRTRUE(pHasManagedValueLabel); pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); From 2baa6da0e4a909776e13bb84e4cddcb8df7d8894 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Oct 2023 12:26:42 -0700 Subject: [PATCH 3/8] Add test using a combination of ComWrappers and built-in COM to validate lifetimes. We can't use ICustomMarshaler here as that isn't supported in field scenarios. --- .../StructArray/CMakeLists.txt | 9 ++ .../StructArray/MarshalStructArrayNative.cpp | 13 +++ .../StructArray/MarshalStructArrayTest.cs | 105 ++++++++++++++++++ .../StructArray/MarshalStructArrayTest.csproj | 13 +++ src/tests/Interop/CMakeLists.txt | 1 + 5 files changed, 141 insertions(+) create mode 100644 src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt create mode 100644 src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp create mode 100644 src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs create mode 100644 src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt b/src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt new file mode 100644 index 00000000000000..80e9592fe8edb3 --- /dev/null +++ b/src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt @@ -0,0 +1,9 @@ +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") +set(SOURCES MarshalStructArrayNative.cpp) + +# add the executable +add_library (MarshalStructArrayNative SHARED ${SOURCES}) +target_link_libraries(MarshalStructArrayNative PRIVATE ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS MarshalStructArrayNative DESTINATION bin) diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp new file mode 100644 index 00000000000000..6b7fedf147c4f0 --- /dev/null +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#include +#include +#include +#include + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ForwardToCallback(void* value, void(STDMETHODCALLTYPE* callback)(void*)) +{ + callback(value); +} \ No newline at end of file diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs new file mode 100644 index 00000000000000..597f095d3cf0cb --- /dev/null +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using TestLibrary; +using Xunit; + +public static unsafe class MarshalStructArrayTest +{ + [SkipOnMono("Mono doesn't support built-in COM interop, which this test requires")] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltInComEnabled))] + public static void ArrayElementsInStructFreed() + { + var (underlyingWeakRef, wrapperWeakRef) = CallNative(); + + // Try to GC 10 times until the built-in RCW is collected. + for (int i = 0; i < 10 && wrapperWeakRef.IsAlive; i++) + { + GC.Collect(); + } + + Assert.False(wrapperWeakRef.IsAlive); + + // Run two more GCs, one to collect the ComWrappers MOW and one to collect the underlying weak reference. + GC.Collect(); + GC.Collect(); + Assert.False(underlyingWeakRef.IsAlive); + + [MethodImpl(MethodImplOptions.NoInlining)] + static (WeakReference underlyingObject, WeakReference builtinWrapperRef) CallNative() + { + var (weakRef, obj) = CreateComObject(); + StructWithObjectArrayField[] arr = [ new() { objs = [obj] } ]; + MarshalStructArrayNative.ForwardToCallback(arr, &Callback); + return (weakRef, new WeakReference(obj)); + } + + [UnmanagedCallersOnly] + static void Callback(void* arg) + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static (WeakReference weakRef, object builtinWrapper) CreateComObject() + { + object underlyingObject = new(); + SimpleComWrappers wrappers = new(); + nint unk = wrappers.GetOrCreateComInterfaceForObject(underlyingObject, CreateComInterfaceFlags.None); + object builtinWrapper = Marshal.GetUniqueObjectForIUnknown(unk); + Marshal.Release(unk); + return (new WeakReference(underlyingObject), builtinWrapper); + } + } +} + +public struct StructWithObjectArrayField +{ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public object[] objs; +} + +public static unsafe class MarshalStructArrayNative +{ + [DllImport(nameof(MarshalStructArrayNative))] + public static extern void ForwardToCallback(StructWithObjectArrayField[] arr, delegate* unmanaged cb); +} + +public sealed unsafe class SimpleComWrappers : ComWrappers +{ + private static readonly ComInterfaceEntry* s_fakeVtable = CreateVtableEntry(); + + // Create a vtable for a fake interface just so we have at least one to provide to ComWrappers. + private static ComInterfaceEntry* CreateVtableEntry() + { + ComInterfaceEntry* entry = (ComInterfaceEntry*)NativeMemory.AllocZeroed((nuint)sizeof(ComInterfaceEntry)); + entry->IID = Guid.NewGuid(); + nint* vtable = (nint*)NativeMemory.Alloc((nuint)(sizeof(void*) * 4)); + GetIUnknownImpl(out vtable[0], out vtable[1], out vtable[2]); + vtable[3] = (nint)(delegate* unmanaged)&NativeMethodImpl; + entry->Vtable = (nint)vtable; + return entry; + } + + protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + count = 1; + return s_fakeVtable; + } + + protected override object? CreateObject(nint externalComObject, CreateObjectFlags flags) + { + throw new NotImplementedException(); + } + + protected override void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + + [UnmanagedCallersOnly] + static int NativeMethodImpl(nint thisPtr) + { + return 0; + } +} \ No newline at end of file diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj new file mode 100644 index 00000000000000..faf335c0aacddb --- /dev/null +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + + + + + + + diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index 21e83b1f9f370f..bdd5d894517087 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -38,6 +38,7 @@ add_subdirectory(UnmanagedCallersOnly) add_subdirectory(PrimitiveMarshalling/Bool) add_subdirectory(PrimitiveMarshalling/UIntPtr) add_subdirectory(ArrayMarshalling/BoolArray) +add_subdirectory(ArrayMarshalling/StructArray) add_subdirectory(PrimitiveMarshalling/EnumMarshalling) add_subdirectory(FuncPtrAsDelegateParam) add_subdirectory(SimpleStruct) From c34d03b88dae2ca5fa386f6b78d93033e0675fb9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Oct 2023 11:11:35 -0700 Subject: [PATCH 4/8] We already normalize null refs early, so we don't need to normalize here. --- src/coreclr/vm/ilmarshalers.h | 20 ------------------- .../StructArray/MarshalStructArrayTest.cs | 1 + 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/src/coreclr/vm/ilmarshalers.h b/src/coreclr/vm/ilmarshalers.h index 7cbbef24073e56..61ff10ac2b2b86 100644 --- a/src/coreclr/vm/ilmarshalers.h +++ b/src/coreclr/vm/ilmarshalers.h @@ -3138,32 +3138,12 @@ class ILMngdMarshaler : public ILMarshaler void EmitClearNative(ILCodeStream* pslILEmit) override { WRAPPER_NO_CONTRACT; - if (IsFieldMarshal(m_dwMarshalFlags)) - { - ILCodeLabel* pHasManagedValueLabel = pslILEmit->NewCodeLabel(); - pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); - pslILEmit->EmitBRTRUE(pHasManagedValueLabel); - pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); - EmitStoreManagedHomeAddr(pslILEmit); - pslILEmit->EmitLabel(pHasManagedValueLabel); - } - EmitCallMngdMarshalerMethod(pslILEmit, GetClearNativeMethod()); } void EmitClearNativeContents(ILCodeStream* pslILEmit) override { WRAPPER_NO_CONTRACT; - if (IsFieldMarshal(m_dwMarshalFlags)) - { - ILCodeLabel* pHasManagedValueLabel = pslILEmit->NewCodeLabel(); - pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); - pslILEmit->EmitBRTRUE(pHasManagedValueLabel); - pslILEmit->EmitLDARG(StructMarshalStubs::MANAGED_STRUCT_ARGIDX); - EmitStoreManagedHomeAddr(pslILEmit); - pslILEmit->EmitLabel(pHasManagedValueLabel); - } - EmitCallMngdMarshalerMethod(pslILEmit, GetClearNativeContentsMethod()); } diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs index 597f095d3cf0cb..699241e746db11 100644 --- a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs @@ -38,6 +38,7 @@ public static void ArrayElementsInStructFreed() [UnmanagedCallersOnly] static void Callback(void* arg) { + Assert.True(arg != null); } [MethodImpl(MethodImplOptions.NoInlining)] From 6ff0065f7bb324fab2623cd47984e91cafa8fe01 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Oct 2023 11:14:57 -0700 Subject: [PATCH 5/8] Simplify test --- .../StructArray/MarshalStructArrayTest.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs index 699241e746db11..ecfc57ea93d920 100644 --- a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs @@ -68,24 +68,10 @@ public static unsafe class MarshalStructArrayNative public sealed unsafe class SimpleComWrappers : ComWrappers { - private static readonly ComInterfaceEntry* s_fakeVtable = CreateVtableEntry(); - - // Create a vtable for a fake interface just so we have at least one to provide to ComWrappers. - private static ComInterfaceEntry* CreateVtableEntry() - { - ComInterfaceEntry* entry = (ComInterfaceEntry*)NativeMemory.AllocZeroed((nuint)sizeof(ComInterfaceEntry)); - entry->IID = Guid.NewGuid(); - nint* vtable = (nint*)NativeMemory.Alloc((nuint)(sizeof(void*) * 4)); - GetIUnknownImpl(out vtable[0], out vtable[1], out vtable[2]); - vtable[3] = (nint)(delegate* unmanaged)&NativeMethodImpl; - entry->Vtable = (nint)vtable; - return entry; - } - protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) { - count = 1; - return s_fakeVtable; + count = 0; + return null; } protected override object? CreateObject(nint externalComObject, CreateObjectFlags flags) From 1a902f24fbbf5245995d4496d6cbf9a49da7977b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Oct 2023 11:19:06 -0700 Subject: [PATCH 6/8] Update src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs Co-authored-by: Aaron Robinson --- .../ArrayMarshalling/StructArray/MarshalStructArrayTest.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs index ecfc57ea93d920..215e07f013d95f 100644 --- a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs @@ -83,10 +83,4 @@ protected override void ReleaseObjects(IEnumerable objects) { throw new NotImplementedException(); } - - [UnmanagedCallersOnly] - static int NativeMethodImpl(nint thisPtr) - { - return 0; - } } \ No newline at end of file From a5ea5f6fa36be2df47328b9b35dbc0a4e2e66256 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Oct 2023 12:51:58 -0700 Subject: [PATCH 7/8] Rewrite test to be more reliable and add lots of comments about what we are testing and how we are testing it as it is very much not straightfoward. --- .../StructArray/MarshalStructArrayTest.cs | 92 +++++++++++-------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs index 215e07f013d95f..b940da304f4d88 100644 --- a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs @@ -1,3 +1,5 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections; using System.Runtime.CompilerServices; @@ -7,50 +9,39 @@ public static unsafe class MarshalStructArrayTest { + [SkipOnMono("Mono doesn't support built-in COM interop, which this test requires")] [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBuiltInComEnabled))] public static void ArrayElementsInStructFreed() { - var (underlyingWeakRef, wrapperWeakRef) = CallNative(); - - // Try to GC 10 times until the built-in RCW is collected. - for (int i = 0; i < 10 && wrapperWeakRef.IsAlive; i++) - { - GC.Collect(); - } - - Assert.False(wrapperWeakRef.IsAlive); + // Validate that we free the native resources of fields that use "managed" marshallers in CoreCLR's IL Stub + // marshalling system when they are fields of structs and we don't have the managed value avaliable at the time we are + // releasing the resources. - // Run two more GCs, one to collect the ComWrappers MOW and one to collect the underlying weak reference. - GC.Collect(); - GC.Collect(); - Assert.False(underlyingWeakRef.IsAlive); + // To test this, we are using a structure with a "ByValArray" field that contains a runtime-implemented RCW. + // The marshaller for the ByValArray unmanaged type uses the marshaller kind under test. + // Using an RCW as the element type allows us to hook the release mechanism to validate that the value has been freed. + // Since the runtime will unwrap an RCW when passing it down to native code (instead of re-wrapping in a CCW), this will + // allow us to directly test with the custom release mechanism we have implemented without needing to do GC.Collect calls. + object underlyingObject = new(); + SimpleComWrappers wrappers = new(); + nint unk = wrappers.GetOrCreateComInterfaceForObject(underlyingObject, CreateComInterfaceFlags.CallerDefinedIUnknown); + object builtinWrapper = Marshal.GetUniqueObjectForIUnknown(unk); + Marshal.Release(unk); + + StructWithObjectArrayField str = new() { objs = [builtinWrapper] }; - [MethodImpl(MethodImplOptions.NoInlining)] - static (WeakReference underlyingObject, WeakReference builtinWrapperRef) CallNative() - { - var (weakRef, obj) = CreateComObject(); - StructWithObjectArrayField[] arr = [ new() { objs = [obj] } ]; - MarshalStructArrayNative.ForwardToCallback(arr, &Callback); - return (weakRef, new WeakReference(obj)); - } + IntPtr ptr = (IntPtr)NativeMemory.Alloc((nuint)Marshal.SizeOf()); + Marshal.StructureToPtr(str, ptr, false); + SimpleComWrappers.ReleaseCalled = false; + Marshal.DestroyStructure(ptr); - [UnmanagedCallersOnly] - static void Callback(void* arg) - { - Assert.True(arg != null); - } + Assert.True(SimpleComWrappers.ReleaseCalled); + NativeMemory.Free((void*)ptr); - [MethodImpl(MethodImplOptions.NoInlining)] - static (WeakReference weakRef, object builtinWrapper) CreateComObject() - { - object underlyingObject = new(); - SimpleComWrappers wrappers = new(); - nint unk = wrappers.GetOrCreateComInterfaceForObject(underlyingObject, CreateComInterfaceFlags.None); - object builtinWrapper = Marshal.GetUniqueObjectForIUnknown(unk); - Marshal.Release(unk); - return (new WeakReference(underlyingObject), builtinWrapper); - } + // Make sure that the runtime-implemented RCW isn't collected (and Release called) + // when we are trying to test that DestroyStructure calls release. + GC.KeepAlive(builtinWrapper); } } @@ -68,10 +59,24 @@ public static unsafe class MarshalStructArrayNative public sealed unsafe class SimpleComWrappers : ComWrappers { + private static readonly ComInterfaceEntry* customIUnknown = CreateCustomIUnknownInterfaceEntry(); + + private static ComInterfaceEntry* CreateCustomIUnknownInterfaceEntry() + { + ComInterfaceEntry* entry = (ComInterfaceEntry*)NativeMemory.AllocZeroed((nuint)sizeof(ComInterfaceEntry)); + entry->IID = Guid.Parse("00000000-0000-0000-C000-000000000046"); + nint* vtable = (nint*)NativeMemory.Alloc((nuint)(sizeof(void*) * 3)); + GetIUnknownImpl(out vtable[0], out vtable[1], out _); + vtable[2] = (nint)(delegate* unmanaged)&Release; + entry->Vtable = (nint)vtable; + return entry; + } + protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) { - count = 0; - return null; + Assert.True(flags.HasFlag(CreateComInterfaceFlags.CallerDefinedIUnknown)); + count = 1; + return customIUnknown; } protected override object? CreateObject(nint externalComObject, CreateObjectFlags flags) @@ -83,4 +88,15 @@ protected override void ReleaseObjects(IEnumerable objects) { throw new NotImplementedException(); } + + [ThreadStatic] + public static bool ReleaseCalled = false; + + [UnmanagedCallersOnly] + private static uint Release(nint thisPtr) + { + ReleaseCalled = true; + GetIUnknownImpl(out _, out _, out var release); + return ((delegate* unmanaged)release)(thisPtr); + } } \ No newline at end of file From 6dc66b1a0ad35723f495481389bd57c05c6c9505 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Oct 2023 13:08:18 -0700 Subject: [PATCH 8/8] Remove now-dead code --- .../ArrayMarshalling/StructArray/CMakeLists.txt | 9 --------- .../StructArray/MarshalStructArrayNative.cpp | 13 ------------- .../StructArray/MarshalStructArrayTest.cs | 6 ------ .../StructArray/MarshalStructArrayTest.csproj | 1 - src/tests/Interop/CMakeLists.txt | 1 - 5 files changed, 30 deletions(-) delete mode 100644 src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt delete mode 100644 src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt b/src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt deleted file mode 100644 index 80e9592fe8edb3..00000000000000 --- a/src/tests/Interop/ArrayMarshalling/StructArray/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") -set(SOURCES MarshalStructArrayNative.cpp) - -# add the executable -add_library (MarshalStructArrayNative SHARED ${SOURCES}) -target_link_libraries(MarshalStructArrayNative PRIVATE ${LINK_LIBRARIES_ADDITIONAL}) - -# add the install targets -install (TARGETS MarshalStructArrayNative DESTINATION bin) diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp deleted file mode 100644 index 6b7fedf147c4f0..00000000000000 --- a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayNative.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -#include -#include -#include -#include - -extern "C" DLL_EXPORT void STDMETHODCALLTYPE ForwardToCallback(void* value, void(STDMETHODCALLTYPE* callback)(void*)) -{ - callback(value); -} \ No newline at end of file diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs index b940da304f4d88..421af3979bbace 100644 --- a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.cs @@ -51,12 +51,6 @@ public struct StructWithObjectArrayField public object[] objs; } -public static unsafe class MarshalStructArrayNative -{ - [DllImport(nameof(MarshalStructArrayNative))] - public static extern void ForwardToCallback(StructWithObjectArrayField[] arr, delegate* unmanaged cb); -} - public sealed unsafe class SimpleComWrappers : ComWrappers { private static readonly ComInterfaceEntry* customIUnknown = CreateCustomIUnknownInterfaceEntry(); diff --git a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj index faf335c0aacddb..a309408302d26d 100644 --- a/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj +++ b/src/tests/Interop/ArrayMarshalling/StructArray/MarshalStructArrayTest.csproj @@ -8,6 +8,5 @@ - diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index bdd5d894517087..21e83b1f9f370f 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -38,7 +38,6 @@ add_subdirectory(UnmanagedCallersOnly) add_subdirectory(PrimitiveMarshalling/Bool) add_subdirectory(PrimitiveMarshalling/UIntPtr) add_subdirectory(ArrayMarshalling/BoolArray) -add_subdirectory(ArrayMarshalling/StructArray) add_subdirectory(PrimitiveMarshalling/EnumMarshalling) add_subdirectory(FuncPtrAsDelegateParam) add_subdirectory(SimpleStruct)