diff --git a/docs/design/libraries/ComInterfaceGenerator/VTableStubs.md b/docs/design/libraries/ComInterfaceGenerator/VTableStubs.md index 21a10864759c8d..3f5514d60694d7 100644 --- a/docs/design/libraries/ComInterfaceGenerator/VTableStubs.md +++ b/docs/design/libraries/ComInterfaceGenerator/VTableStubs.md @@ -80,26 +80,29 @@ public readonly ref struct VirtualMethodTableInfo } } -public interface IUnmanagedVirtualMethodTableProvider where T : IEquatable +public interface IUnmanagedVirtualMethodTableProvider { - protected VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(T typeKey); + protected VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type); public sealed VirtualMethodTableInfo GetVirtualMethodTableInfoForKey() - where TUnmanagedInterfaceType : IUnmanagedInterfaceType + where TUnmanagedInterfaceType : IUnmanagedInterfaceType { - return GetVirtualMethodTableInfoForKey(TUnmanagedInterfaceType.TypeKey); + // Dispatch from a non-virtual generic to a virtual non-generic with System.Type + // to avoid generic virtual method dispatch, which is very slow. + return GetVirtualMethodTableInfoForKey(typeof(TUnmanagedInterfaceType)); } } -public interface IUnmanagedInterfaceType where T : IEquatable +public interface IUnmanagedInterfaceType where TUnmanagedInterfaceType : IUnmanagedInterfaceType { - public abstract static T TypeKey { get; } } ``` ## Required API Shapes -The user will be required to implement `IUnmanagedVirtualMethodTableProvider` on the type that provides the method tables, and `IUnmanagedInterfaceType` on the type that defines the unmanaged interface. The `T` types must match between the two interfaces. This mechanism is designed to enable each native API platform to provide their own casting key, for example `IID`s in COM, without interfering with each other or requiring using reflection-based types like `System.Type`. +The user will be required to implement `IUnmanagedVirtualMethodTableProvider` on the type that provides the method tables, and `IUnmanagedInterfaceType` on the type that defines the unmanaged interface. The `TUnmanagedInterfaceType` follows the same design principles as the generic math designs as somewhat of a "self" type to enable us to use the derived interface type in any additional APIs we add to support unmanaged-to-managed stubs. + +Previously, each of these interface types were also generic on another type `T`. The `T` types were required to match between the two interfaces. This mechanism was designed to enable each native API platform to provide their own casting key, for example `IID`s in COM, without interfering with each other or requiring using reflection-based types like `System.Type`. However, practical implementation showed that providing just a "type key" was not enough information to cover any non-trivial scenarios (like COM) efficiently without effectively forcing a two-level lookup model or hard-coding type support in the `IUnmanagedVirtualMethodTableProvider` implementation. Additionally, we determined that using reflection to get to attributes is considered "okay" and using generic attributes would enable APIs that build on this model like COM to effectively retrieve information from the `System.Type` instance without causing additional problems. ## Example Usage @@ -160,11 +163,8 @@ using System.Runtime.InteropServices; [assembly:DisableRuntimeMarshalling] // Define the interface of the native API -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType { - // There is no concept of casting for this API, but providing a type key is still required by the generator. - // Use an empty readonly record struct to provide a type that implements IEquatable but contains no data. - static NoCasting IUnmanagedInterfaceType.TypeKey => default; [VirtualMethodIndex(0, ImplicitThisParameter = false, Direction = CustomTypeMarshallerDirection.In)] int GetVersion(); @@ -176,11 +176,8 @@ partial interface INativeAPI : IUnmanagedInterfaceType int Multiply(int x, int y); } -// Define the key for native "casting" support for our scenario -readonly record struct NoCasting {} - // Define our runtime wrapper type for the native interface. -unsafe class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native +unsafe class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native { private CNativeAPI* _nativeAPI; @@ -192,7 +189,7 @@ unsafe class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativ } } - VirtualMethodTableInfo IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableInfoForKey(NoCasting _) + VirtualMethodTableInfo IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableInfoForKey(Type _) { return new(IntPtr.Zero, MemoryMarshal.Cast(new ReadOnlySpan(_nativeAPI, 1))); } @@ -229,7 +226,7 @@ partial interface INativeAPI { int INativeAPI.GetVersion() { - var (_, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); + var (_, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); int retVal; retVal = ((delegate* unmanaged)vtable[0])(); return retVal; @@ -242,7 +239,7 @@ partial interface INativeAPI { int INativeAPI.Add(int x, int y) { - var (_, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); + var (_, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); int retVal; retVal = ((delegate* unmanaged)vtable[1])(x, y); return retVal; @@ -255,7 +252,7 @@ partial interface INativeAPI { int INativeAPI.Multiply(int x, int y) { - var (_, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); + var (_, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); int retVal; retVal = ((delegate* unmanaged)vtable[2])(x, y); return retVal; @@ -266,7 +263,7 @@ partial interface INativeAPI // LibraryImport-generated code omitted for brevity ``` -As this generator is primarily designed to provide building blocks for future work, it has a larger requirement on user-written code. In particular, this generator does not provide any support for authoring a runtime wrapper object that stores the native pointers for the underlying object or the virtual method table. However, this lack of support also provides significant flexibility for developers. The only requirement for the runtime wrapper object type is that it implements `IUnmanagedVirtualMethodTableProvider` with a `T` matching the `TypeKey` type of the native interface. +As this generator is primarily designed to provide building blocks for future work, it has a larger requirement on user-written code. In particular, this generator does not provide any support for authoring a runtime wrapper object that stores the native pointers for the underlying object or the virtual method table. However, this lack of support also provides significant flexibility for developers. The only requirement for the runtime wrapper object type is that it implements `IUnmanagedVirtualMethodTableProvider`. The emitted interface implementation can be used in two ways: @@ -290,10 +287,8 @@ struct IUnknown using System; using System.Runtime.InteropServices; -interface IUnknown: IUnmanagedInterfaceType +interface IUnknown: IUnmanagedInterfaceType { - static Guid IUnmanagedTypeInterfaceType.TypeKey => Guid.Parse("00000000-0000-0000-C000-000000000046"); - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvStdcall), typeof(CallConvMemberFunction) })] [VirtualMethodIndex(0)] int QueryInterface(in Guid riid, out IntPtr ppvObject); @@ -307,7 +302,7 @@ interface IUnknown: IUnmanagedInterfaceType uint Release(); } -class BaseIUnknownComObject : IUnmanagedVirtualMethodTableProvider, IDynamicInterfaceCastable +class BaseIUnknownComObject : IUnmanagedVirtualMethodTableProvider, IDynamicInterfaceCastable { private IntPtr _unknownPtr; @@ -316,9 +311,9 @@ class BaseIUnknownComObject : IUnmanagedVirtualMethodTableProvider, IDynam _unknownPtr = unknown; } - unsafe VirtualMethodTableInfo IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableInfoForKey(Guid iid) + unsafe VirtualMethodTableInfo IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableInfoForKey(Type type) { - if (iid == IUnknown.TypeKey) + if (type == typeof(IUnknown)) { return new VirtualMethodTableInfo(_unknownPtr, new ReadOnlySpan(**(IntPtr***)_unknownPtr), 3); } @@ -358,7 +353,7 @@ partial interface IUnknown { int IUnknown.QueryInterface(in Guid riid, out IntPtr ppvObject) { - var (thisPtr, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); + var (thisPtr, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); int retVal; fixed (Guid* riid__gen_native = &riid) fixed (IntPtr* ppvObject__gen_native = &ppvObject) @@ -375,7 +370,7 @@ partial interface IUnknown { uint IUnknown.AddRef() { - var (thisPtr, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); + var (thisPtr, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); uint retVal; retVal = ((delegate* unmanaged[Stdcall, MemberFunction])vtable[1])(thisPtr); return retVal; @@ -388,7 +383,7 @@ partial interface IUnknown { uint IUnknown.Release() { - var (thisPtr, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); + var (thisPtr, vtable) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(); uint retVal; retVal = ((delegate* unmanaged[Stdcall, MemberFunction])vtable[2])(thisPtr); return retVal; diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs index cc7b913562c7ae..1b9c1b43a7fe1e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs @@ -97,11 +97,11 @@ public ManagedToNativeVTableMethodGenerator( /// /// The generated code assumes it will be in an unsafe context. /// - public BlockSyntax GenerateStubBody(int index, ImmutableArray callConv, TypeSyntax containingTypeName, ManagedTypeInfo typeKeyType) + public BlockSyntax GenerateStubBody(int index, ImmutableArray callConv, TypeSyntax containingTypeName) { var setupStatements = new List { - // var (, ) = ((IUnmanagedVirtualMethodTableProvider<>)this).GetVirtualMethodTableInfoForKey<>(); + // var (, ) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey<>(); ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, @@ -119,11 +119,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray info.MarshallingAttributeInfo is NativeThisInfo(ManagedTypeInfo typeKeyType) ? new Marshaller(typeKeyType) : _inner.Create(info, context); + => info.MarshallingAttributeInfo is NativeThisInfo ? new Marshaller() : _inner.Create(info, context); private sealed class Marshaller : IMarshallingGenerator { - private readonly ManagedTypeInfo _typeKeyType; - - public Marshaller(ManagedTypeInfo typeKeyType) => _typeKeyType = typeKeyType; - public ManagedTypeInfo AsNativeType(TypePositionInfo info) => new PointerTypeInfo("void*", "void*", false); public IEnumerable Generate(TypePositionInfo info, StubCodeContext context) { @@ -44,10 +43,7 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont IdentifierName(managedIdentifier), InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - GenericName(Identifier(TypeNames.IUnmanagedVirtualMethodTableProvider), - TypeArgumentList( - SingletonSeparatedList( - _typeKeyType.Syntax))), + ParseTypeName(TypeNames.IUnmanagedVirtualMethodTableProvider), GenericName(Identifier("GetObjectForUnmanagedWrapper"), TypeArgumentList( SingletonSeparatedList( diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs index 669db87f806ed5..d8fd57dabf8807 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs @@ -30,7 +30,6 @@ internal sealed record IncrementalStubGenerationContext( MarshallingInfo ExceptionMarshallingInfo, MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> ManagedToUnmanagedGeneratorFactory, MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> UnmanagedToManagedGeneratorFactory, - ManagedTypeInfo TypeKeyType, ManagedTypeInfo TypeKeyOwner, SequenceEqualImmutableArray Diagnostics); @@ -348,20 +347,15 @@ private static IncrementalStubGenerationContext CalculateStubInformation(MethodD ImmutableArray callConv = GenerateCallConvSyntaxFromAttributes(suppressGCTransitionAttribute, unmanagedCallConvAttribute); - var typeKeyOwner = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol.ContainingType); - ManagedTypeInfo typeKeyType = SpecialTypeInfo.Byte; + var interfaceType = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol.ContainingType); - INamedTypeSymbol? iUnmanagedInterfaceTypeInstantiation = symbol.ContainingType.AllInterfaces.FirstOrDefault(iface => SymbolEqualityComparer.Default.Equals(iface.OriginalDefinition, iUnmanagedInterfaceTypeType)); - if (iUnmanagedInterfaceTypeInstantiation is null) + INamedTypeSymbol expectedUnmanagedInterfaceType = iUnmanagedInterfaceTypeType.Construct(symbol.ContainingType); + + bool implementsIUnmanagedInterfaceOfSelf = symbol.ContainingType.AllInterfaces.Any(iface => SymbolEqualityComparer.Default.Equals(iface, expectedUnmanagedInterfaceType)); + if (!implementsIUnmanagedInterfaceOfSelf) { // TODO: Report invalid configuration } - else - { - // The type key is the second generic type parameter, so we need to get the info for the - // second argument. - typeKeyType = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(iUnmanagedInterfaceTypeInstantiation.TypeArguments[1]); - } MarshallingInfo exceptionMarshallingInfo = CreateExceptionMarshallingInfo(virtualMethodIndexAttr, symbol, environment.Compilation, generatorDiagnostics, virtualMethodIndexData); @@ -375,8 +369,7 @@ private static IncrementalStubGenerationContext CalculateStubInformation(MethodD exceptionMarshallingInfo, ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.ManagedToUnmanaged), ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.UnmanagedToManaged), - typeKeyType, - typeKeyOwner, + interfaceType, new SequenceEqualImmutableArray(generatorDiagnostics.Diagnostics.ToImmutableArray())); } @@ -442,8 +435,7 @@ private static (MemberDeclarationSyntax, ImmutableArray) GenerateMan BlockSyntax code = stubGenerator.GenerateStubBody( methodStub.VtableIndexData.Index, methodStub.CallingConvention.Array, - methodStub.TypeKeyOwner.Syntax, - methodStub.TypeKeyType); + methodStub.TypeKeyOwner.Syntax); return ( methodStub.ContainingSyntaxContext.AddContainingSyntax( @@ -518,7 +510,7 @@ private static ImmutableArray AddImplicitElementInfos(Incremen var elements = ImmutableArray.CreateBuilder(originalElements.Length + 2); - elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, new NativeThisInfo(methodStub.TypeKeyType)) + elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, NativeThisInfo.Instance) { InstanceIdentifier = ThisParameterIdentifier, NativeIndex = 0, diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs index b7284126f5baa0..4aaf4fd45d1b89 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs @@ -38,7 +38,7 @@ public static class TypeNames public const string IUnmanagedVirtualMethodTableProvider = "System.Runtime.InteropServices.IUnmanagedVirtualMethodTableProvider"; - public const string IUnmanagedInterfaceType_Metadata = "System.Runtime.InteropServices.IUnmanagedInterfaceType`2"; + public const string IUnmanagedInterfaceType_Metadata = "System.Runtime.InteropServices.IUnmanagedInterfaceType`1"; public const string System_Span_Metadata = "System.Span`1"; public const string System_Span = "System.Span"; diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedVirtualMethodTableProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedVirtualMethodTableProvider.cs index b0c3a1f51075a1..99e4309726ca9d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedVirtualMethodTableProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedVirtualMethodTableProvider.cs @@ -48,17 +48,17 @@ public void Deconstruct(out IntPtr thisPointer, out ReadOnlySpan virtual } /// - /// This interface allows an object to provide information about a virtual method table for a managed interface that implements to enable invoking methods in the virtual method table. + /// This interface allows an object to provide information about a virtual method table for a managed interface that implements to enable invoking methods in the virtual method table. /// /// The type to use to represent the the identity of the unmanaged type. - public unsafe interface IUnmanagedVirtualMethodTableProvider where T : IEquatable + public unsafe interface IUnmanagedVirtualMethodTableProvider { /// - /// Get the information about the virtual method table for a given unmanaged interface type represented by . + /// Get the information about the virtual method table for a given unmanaged interface type represented by . /// - /// The object that represents the identity of the unmanaged interface. + /// The managed type for the unmanaged interface. /// The virtual method table information for the unmanaged interface. - protected VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(T typeKey); + protected VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type); /// /// Get the information about the virtual method table for the given unmanaged interface type. @@ -66,9 +66,9 @@ public unsafe interface IUnmanagedVirtualMethodTableProvider where T : IEquat /// The managed interface type that represents the unmanaged interface. /// The virtual method table information for the unmanaged interface. public sealed VirtualMethodTableInfo GetVirtualMethodTableInfoForKey() - where TUnmanagedInterfaceType : IUnmanagedInterfaceType + where TUnmanagedInterfaceType : IUnmanagedInterfaceType { - return GetVirtualMethodTableInfoForKey(TUnmanagedInterfaceType.TypeKey); + return GetVirtualMethodTableInfoForKey(typeof(TUnmanagedInterfaceType)); } /// @@ -77,7 +77,7 @@ public sealed VirtualMethodTableInfo GetVirtualMethodTableInfoForKeyThe managed interface type that represents the unmanaged interface. /// The length of the virtual method table for the unmanaged interface. public static int GetVirtualMethodTableLength() - where TUnmanagedInterfaceType : IUnmanagedInterfaceType + where TUnmanagedInterfaceType : IUnmanagedInterfaceType { return TUnmanagedInterfaceType.VirtualMethodTableLength; } @@ -88,7 +88,7 @@ public static int GetVirtualMethodTableLength() /// The managed interface type that represents the unmanaged interface. /// A pointer to the virtual method table of managed implementations of the unmanaged interface type public static void* GetVirtualMethodTableManagedImplementation() - where TUnmanagedInterfaceType : IUnmanagedInterfaceType + where TUnmanagedInterfaceType : IUnmanagedInterfaceType { return TUnmanagedInterfaceType.VirtualMethodTableManagedImplementation; } @@ -100,7 +100,7 @@ public static int GetVirtualMethodTableLength() /// The managed object that implements the unmanaged interface. /// A pointer-sized value that can be passed to unmanaged code that represents public static void* GetUnmanagedWrapperForObject(TUnmanagedInterfaceType obj) - where TUnmanagedInterfaceType : IUnmanagedInterfaceType + where TUnmanagedInterfaceType : IUnmanagedInterfaceType { return TUnmanagedInterfaceType.GetUnmanagedWrapperForObject(obj); } @@ -112,7 +112,7 @@ public static int GetVirtualMethodTableLength() /// A pointer-sized value returned by or . /// The object wrapped by . public static TUnmanagedInterfaceType GetObjectForUnmanagedWrapper(void* ptr) - where TUnmanagedInterfaceType : IUnmanagedInterfaceType + where TUnmanagedInterfaceType : IUnmanagedInterfaceType { return TUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(ptr); } @@ -123,9 +123,8 @@ public static TUnmanagedInterfaceType GetObjectForUnmanagedWrapper /// The managed interface. /// The type of a value that can represent types from the corresponding unmanaged type system. - public unsafe interface IUnmanagedInterfaceType - where TInterface : IUnmanagedInterfaceType - where TKey : IEquatable + public unsafe interface IUnmanagedInterfaceType + where TInterface : IUnmanagedInterfaceType { /// /// Get the length of the virtual method table for the given unmanaged interface type. @@ -152,10 +151,5 @@ public unsafe interface IUnmanagedInterfaceType /// A pointer-sized value returned by or . /// The object wrapped by . public static abstract TInterface GetObjectForUnmanagedWrapper(void* ptr); - - /// - /// The value that represents the unmanaged type's identity in the corresponding unmanaged type system. - /// - public static abstract TKey TypeKey { get; } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ImplicitThisTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ImplicitThisTests.cs index a325169297f087..55174ae53b3170 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ImplicitThisTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ImplicitThisTests.cs @@ -13,35 +13,32 @@ internal unsafe partial class NativeExportsNE { internal partial class ImplicitThis { - public readonly record struct NoCasting; - - internal partial interface INativeObject : IUnmanagedInterfaceType + internal partial interface INativeObject : IUnmanagedInterfaceType { - static int IUnmanagedInterfaceType.VirtualMethodTableLength => 2; + static int IUnmanagedInterfaceType.VirtualMethodTableLength => 2; - static NoCasting IUnmanagedInterfaceType.TypeKey => default; - private static void** s_vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(INativeObject), sizeof(void*) * IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength()); - static void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation + private static void** s_vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(INativeObject), sizeof(void*) * IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength()); + static void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation { get { if (s_vtable[0] == null) { - Native.PopulateUnmanagedVirtualMethodTable(new Span(s_vtable, IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength())); + Native.PopulateUnmanagedVirtualMethodTable(new Span(s_vtable, IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength())); } return s_vtable; } } - static void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(INativeObject obj) + static void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(INativeObject obj) { - return VTableGCHandlePair.Allocate(obj); + return VTableGCHandlePair.Allocate(obj); } - static INativeObject IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) + static INativeObject IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) { - return VTableGCHandlePair.GetObject(ptr); + return VTableGCHandlePair.GetObject(ptr); } [VirtualMethodIndex(0, ImplicitThisParameter = true)] @@ -51,7 +48,7 @@ static INativeObject IUnmanagedInterfaceType.GetObject } [NativeMarshalling(typeof(NativeObjectMarshaller))] - public class NativeObject : INativeObject.Native, IUnmanagedVirtualMethodTableProvider, IDisposable + public class NativeObject : INativeObject.Native, IUnmanagedVirtualMethodTableProvider, IDisposable { private readonly void* _pointer; @@ -60,7 +57,11 @@ public NativeObject(void* pointer) _pointer = pointer; } - public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(NoCasting typeKey) => new VirtualMethodTableInfo((IntPtr)_pointer, new ReadOnlySpan(*(void**)_pointer, 2)); + public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type) + { + Assert.Equal(typeof(INativeObject), type); + return new VirtualMethodTableInfo((IntPtr)_pointer, new ReadOnlySpan(*(void**)_pointer, 2)); + } public void Dispose() { @@ -112,7 +113,7 @@ public unsafe void ValidateImplicitThisUnmanagedToManagedFunctionCallsSucceed() ManagedObjectImplementation impl = new ManagedObjectImplementation(startingValue); - void* wrapper = IUnmanagedVirtualMethodTableProvider.GetUnmanagedWrapperForObject(impl); + void* wrapper = IUnmanagedVirtualMethodTableProvider.GetUnmanagedWrapperForObject(impl); Assert.Equal(startingValue, NativeExportsNE.ImplicitThis.GetNativeObjectData(wrapper)); NativeExportsNE.ImplicitThis.SetNativeObjectData(wrapper, newValue); @@ -120,7 +121,7 @@ public unsafe void ValidateImplicitThisUnmanagedToManagedFunctionCallsSucceed() // Verify that we actually updated the managed instance. Assert.Equal(newValue, impl.GetData()); - VTableGCHandlePair.Free(wrapper); + VTableGCHandlePair.Free(wrapper); } class ManagedObjectImplementation : NativeExportsNE.ImplicitThis.INativeObject diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/NoImplicitThisTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/NoImplicitThisTests.cs index 5d382cb2186e80..fd0d0a4498ed92 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/NoImplicitThisTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/NoImplicitThisTests.cs @@ -12,18 +12,14 @@ internal unsafe partial class NativeExportsNE { internal partial class NoImplicitThis { - public readonly record struct NoCasting; - - internal partial interface IStaticMethodTable : IUnmanagedInterfaceType + internal partial interface IStaticMethodTable : IUnmanagedInterfaceType { - static int IUnmanagedInterfaceType.VirtualMethodTableLength => 2; - static void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; - - static void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(IStaticMethodTable obj) => null; + static int IUnmanagedInterfaceType.VirtualMethodTableLength => 2; + static void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; - static IStaticMethodTable IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) => null; + static void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(IStaticMethodTable obj) => null; - static NoCasting IUnmanagedInterfaceType.TypeKey => default; + static IStaticMethodTable IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) => null; [VirtualMethodIndex(0, Direction = MarshalDirection.ManagedToUnmanaged, ImplicitThisParameter = false)] int Add(int x, int y); @@ -32,7 +28,7 @@ internal partial interface IStaticMethodTable : IUnmanagedInterfaceType + public class StaticMethodTable : IStaticMethodTable.Native, IUnmanagedVirtualMethodTableProvider { private readonly void* _vtableStart; @@ -41,8 +37,11 @@ public StaticMethodTable(void* vtableStart) _vtableStart = vtableStart; } - public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(NoCasting typeKey) => - new VirtualMethodTableInfo(IntPtr.Zero, new ReadOnlySpan(_vtableStart, IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength())); + public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type) + { + Assert.Equal(typeof(IStaticMethodTable), type); + return new VirtualMethodTableInfo(IntPtr.Zero, new ReadOnlySpan(_vtableStart, IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength())); + } } [CustomMarshaller(typeof(StaticMethodTable), MarshalMode.ManagedToUnmanagedOut, typeof(StaticMethodTableMarshaller))] diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/VTableGCHandlePair.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/VTableGCHandlePair.cs index a5653fb6188418..c6ee2cd505d0c7 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/VTableGCHandlePair.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/VTableGCHandlePair.cs @@ -1,22 +1,16 @@ // 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.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace ComInterfaceGenerator.Tests { - public static unsafe class VTableGCHandlePair - where TUnmanagedInterface : IUnmanagedInterfaceType - where TKey : IEquatable + public static unsafe class VTableGCHandlePair + where TUnmanagedInterface : IUnmanagedInterfaceType { public static void* Allocate(TUnmanagedInterface obj) { - void** unmanaged = (void**)NativeMemory.Alloc((nuint)sizeof(void*) * (nuint)IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength()); + void** unmanaged = (void**)NativeMemory.Alloc((nuint)sizeof(void*) * (nuint)IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength()); unmanaged[0] = TUnmanagedInterface.VirtualMethodTableManagedImplementation; unmanaged[1] = (void*)GCHandle.ToIntPtr(GCHandle.Alloc(obj)); return unmanaged; diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs index 53571572109196..d8033b561084ee 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs @@ -23,10 +23,9 @@ public async Task NoSpecifiedCallConvForwardsDefault() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] void Method(); } @@ -50,10 +49,9 @@ public async Task SuppressGCTransitionAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [SuppressGCTransitionAttribute] [VirtualMethodIndex(0)] void Method(); @@ -78,10 +76,9 @@ public async Task EmptyUnmanagedCallConvAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [UnmanagedCallConv] [VirtualMethodIndex(0)] void Method(); @@ -106,10 +103,9 @@ public async Task SimpleUnmanagedCallConvAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] [VirtualMethodIndex(0)] void Method(); @@ -134,10 +130,9 @@ public async Task ComplexUnmanagedCallConvAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl), typeof(CallConvMemberFunction) })] [VirtualMethodIndex(0)] void Method(); @@ -168,10 +163,9 @@ public async Task ComplexUnmanagedCallConvAttributeWithSuppressGCTransitionForwa using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [SuppressGCTransition] [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl), typeof(CallConvMemberFunction) })] [VirtualMethodIndex(0)] diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs index da20a1a478a7f9..ef02fb31818439 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs @@ -10,26 +10,25 @@ internal partial class CodeSnippets { public static readonly string DisableRuntimeMarshalling = "[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]"; public static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;"; - public const string INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl = $$""" + public const string INativeAPI_IUnmanagedInterfaceTypeImpl = $$""" partial interface INativeAPI { - {{INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} } """; - public const string INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl = """ - static int IUnmanagedInterfaceType.VirtualMethodTableLength => 1; - static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; - static unsafe void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(INativeAPI obj) => null; - static unsafe INativeAPI IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) => null; - static NoCasting IUnmanagedInterfaceType.TypeKey => default; + public const string INativeAPI_IUnmanagedInterfaceTypeMethodImpl = """ + static int IUnmanagedInterfaceType.VirtualMethodTableLength => 1; + static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; + static unsafe void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(INativeAPI obj) => null; + static unsafe INativeAPI IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) => null; """; public static string NativeInterfaceUsage() => @" // Try using the generated native interface -sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native +sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native { - public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(NoCasting typeKey) => throw null; + public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(System.Type type) => throw null; } "; @@ -37,31 +36,28 @@ sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -readonly record struct NoCasting {} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType { [VirtualMethodIndex(0)] void Method(); -}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static readonly string SpecifiedMethodIndexNoExplicitParametersNoImplicitThis = @" using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -readonly record struct NoCasting {} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType { [VirtualMethodIndex(0, ImplicitThisParameter = false)] void Method(); -}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static readonly string SpecifiedMethodIndexNoExplicitParametersCallConvWithCallingConventions = @" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -readonly record struct NoCasting {} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType { [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] @@ -84,7 +80,7 @@ partial interface INativeAPI : IUnmanagedInterfaceType [SuppressGCTransition] [VirtualMethodIndex(4)] void Method4(); -}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static string BasicParametersAndModifiers(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -93,12 +89,11 @@ public static string BasicParametersAndModifiers(string typeName, string preDecl [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0)] {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static string BasicParametersAndModifiersManagedToUnmanaged(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -107,12 +102,11 @@ public static string BasicParametersAndModifiersManagedToUnmanaged(string typeNa [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, Direction = MarshalDirection.ManagedToUnmanaged)] {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static string BasicParametersAndModifiers() => BasicParametersAndModifiers(typeof(T).FullName!); public static string BasicParametersAndModifiersNoRef(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -122,23 +116,21 @@ public static string BasicParametersAndModifiersNoRef(string typeName, string pr [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0)] {typeName} Method({typeName} value, in {typeName} inValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static string BasicParametersAndModifiersNoImplicitThis(string typeName) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = false)] {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static string BasicParametersAndModifiersNoImplicitThis() => BasicParametersAndModifiersNoImplicitThis(typeof(T).FullName!); public static string MarshalUsingCollectionCountInfoParametersAndModifiers() => MarshalUsingCollectionCountInfoParametersAndModifiers(typeof(T).ToString()); @@ -148,8 +140,7 @@ public static string MarshalUsingCollectionCountInfoParametersAndModifiers(strin using System.Runtime.InteropServices.Marshalling; [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0)] [return:MarshalUsing(ConstantElementCount=10)] @@ -160,7 +151,7 @@ partial interface INativeAPI : IUnmanagedInterfaceType [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef, [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut, out int pOutSize); -}}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static string BasicReturnTypeComExceptionHandling(string typeName, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -168,12 +159,11 @@ public static string BasicReturnTypeComExceptionHandling(string typeName, string using System.Runtime.InteropServices.Marshalling; {preDeclaration} -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ExceptionMarshalling = ExceptionMarshalling.Com)] {typeName} Method(); -}}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public static string BasicReturnTypeCustomExceptionHandling(string typeName, string customExceptionType, string preDeclaration = "") => $@" using System.Runtime.CompilerServices; @@ -181,12 +171,11 @@ public static string BasicReturnTypeCustomExceptionHandling(string typeName, str using System.Runtime.InteropServices.Marshalling; {preDeclaration} -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, CustomExceptionMarshallingType = typeof({customExceptionType}))] {typeName} Method(); -}}" + NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; public class ManagedToUnmanaged : IVirtualMethodIndexSignatureProvider { @@ -211,7 +200,7 @@ public class UnmanagedToManaged : IVirtualMethodIndexSignatureProvider true; // Unmanaged-to-managed-only stubs don't provide implementations for the interface, so we don't want to try to use the generated nested interface - // since it won't have managed implementations for the meth + // since it won't have managed implementations for the methods public static string NativeInterfaceUsage() => string.Empty; } public class Bidirectional : IVirtualMethodIndexSignatureProvider diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs index 544e87cf272d03..8ad0d298d7a925 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs @@ -19,19 +19,18 @@ internal interface IVirtualMethodIndexSignatureProvider : ICustomMars public static readonly string DisableRuntimeMarshalling = "[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]"; public static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;"; - public const string INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl = $$""" + public const string INativeAPI_IUnmanagedInterfaceTypeImpl = $$""" partial interface INativeAPI { - {{INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} } """; - public const string INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl = """ - static int IUnmanagedInterfaceType.VirtualMethodTableLength => 1; - static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; - static unsafe void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(INativeAPI obj) => null; - static unsafe INativeAPI IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) => null; - static NoCasting IUnmanagedInterfaceType.TypeKey => default; + public const string INativeAPI_IUnmanagedInterfaceTypeMethodImpl = """ + static int IUnmanagedInterfaceType.VirtualMethodTableLength => 1; + static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; + static unsafe void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(INativeAPI obj) => null; + static unsafe INativeAPI IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) => null; """; public static abstract string NativeInterfaceUsage(); @@ -43,12 +42,11 @@ static string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifier [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifiersNoRef(string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -57,12 +55,11 @@ static string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifier [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] {typeName} Method({typeName} value, in {typeName} inValue, out {typeName} outValue); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.BasicParameterByValue(string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; @@ -70,12 +67,11 @@ static string ICustomMarshallingSignatureTestProvider.BasicParameterByValue(stri using System.Runtime.InteropServices.Marshalling; {preDeclaration} -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] void Method({typeName} value); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.BasicParameterWithByRefModifier(string modifier, string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; @@ -85,32 +81,29 @@ static string ICustomMarshallingSignatureTestProvider.BasicParameterWithByRefMod [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] void Method({modifier} {typeName} value); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.BasicReturnType(string typeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; {preDeclaration} -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] {typeName} Method(); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.MarshalUsingParametersAndModifiers(string typeName, string marshallerTypeName, string preDeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; {preDeclaration} -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return: MarshalUsing(typeof({marshallerTypeName}))] @@ -119,15 +112,14 @@ partial interface INativeAPI : IUnmanagedInterfaceType [MarshalUsing(typeof({marshallerTypeName}))] in {typeName} pIn, [MarshalUsing(typeof({marshallerTypeName}))] ref {typeName} pRef, [MarshalUsing(typeof({marshallerTypeName}))] out {typeName} pOut); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionCountInfoParametersAndModifiers(string collectionType) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(ConstantElementCount=10)] @@ -138,7 +130,7 @@ partial interface INativeAPI : IUnmanagedInterfaceType [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef, [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut, out int pOutSize); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionParametersAndModifiers(string collectionType, string marshallerType) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -146,8 +138,7 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionPara [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(typeof({marshallerType}), ConstantElementCount=10)] @@ -159,7 +150,7 @@ partial interface INativeAPI : IUnmanagedInterfaceType [MarshalUsing(typeof({marshallerType}), CountElementName = ""pOutSize"")] out {collectionType} pOut, out int pOutSize ); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionReturnValueLength(string collectionType, string marshallerType) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -167,14 +158,13 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionRetu [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] int Method( [MarshalUsing(typeof({marshallerType}), CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out {collectionType} pOut ); -}}" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +}}" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionOutConstantLength(string collectionType, string predeclaration) => $@" using System.Runtime.CompilerServices; @@ -184,15 +174,14 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionOutC [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] int Method( [MarshalUsing(ConstantElementCount = 10)] out {collectionType} pOut ); }} -" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionReturnConstantLength(string collectionType, string predeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -201,14 +190,13 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionRetu [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(ConstantElementCount = 10)] {collectionType} Method(); }} -" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; static string ICustomMarshallingSignatureTestProvider.CustomElementMarshalling(string collectionType, string elementMarshaller, string predeclaration) => $@" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -217,8 +205,7 @@ static string ICustomMarshallingSignatureTestProvider.CustomElementMarshalling(s [assembly:DisableRuntimeMarshalling] -readonly record struct NoCasting {{}} -partial interface INativeAPI : IUnmanagedInterfaceType +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(ConstantElementCount=10)] @@ -232,6 +219,6 @@ TestCollection Method( out int pOutSize ); }} -" + TProvider.NativeInterfaceUsage() + INativeAPI_NoCasting_IUnmanagedInterfaceTypeImpl; +" + TProvider.NativeInterfaceUsage() + INativeAPI_IUnmanagedInterfaceTypeImpl; } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs index 548eb708ea03df..6fa6406e0f4b94 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs @@ -23,10 +23,9 @@ public async Task NativeInterfaceNestedInUserInterface() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] void Method(); } @@ -50,10 +49,9 @@ public async Task NativeInterfaceInheritsFromUserInterface() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] void Method(); } @@ -77,10 +75,9 @@ public async Task NativeInterfaceHasDynamicInterfaceCastableImplementationAttrib using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] void Method(); } @@ -109,10 +106,9 @@ public async Task NativeInterfaceHasStaticMethodWithSameNameWithExpectedPrefixWi using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - readonly record struct NoCasting {} - partial interface INativeAPI : IUnmanagedInterfaceType + partial interface INativeAPI : IUnmanagedInterfaceType { - {{CodeSnippets.INativeAPI_NoCasting_IUnmanagedInterfaceTypeMethodImpl}} + {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] void Method(); }