diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs index 9d5a973f99f92..a2c9391b97a0e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs @@ -266,7 +266,8 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.ManagedToUnmanaged), ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.UnmanagedToManaged), typeKeyOwner, - new SequenceEqualImmutableArray(generatorDiagnostics.Diagnostics.ToImmutableArray())); + new SequenceEqualImmutableArray(generatorDiagnostics.Diagnostics.ToImmutableArray()), + ParseTypeName(TypeNames.ComWrappersUnwrapper)); } private static Diagnostic? GetDiagnosticIfInvalidTypeForGeneration(InterfaceDeclarationSyntax syntax, INamedTypeSymbol type) diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs index 0ba0e37d578c9..c8f55e8ae8cef 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs @@ -45,6 +45,16 @@ public class Ids isEnabledByDefault: true, description: GetResourceString(nameof(SR.InvalidAttributedMethodDescription))); + public static readonly DiagnosticDescriptor InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttribute = + new DiagnosticDescriptor( + Ids.InvalidLibraryImportAttributeUsage, + GetResourceString(nameof(SR.InvalidVirtualMethodIndexAttributeUsage)), + GetResourceString(nameof(SR.InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttributeMessage)), + Category, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: GetResourceString(nameof(SR.InvalidAttributedMethodDescription))); + public static readonly DiagnosticDescriptor InvalidStringMarshallingConfiguration = new DiagnosticDescriptor( Ids.InvalidLibraryImportAttributeUsage, diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs index 0614a8d25cf11..e437df7b315f7 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs @@ -18,5 +18,6 @@ internal sealed record IncrementalMethodStubGenerationContext( MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> ManagedToUnmanagedGeneratorFactory, MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> UnmanagedToManagedGeneratorFactory, ManagedTypeInfo TypeKeyOwner, - SequenceEqualImmutableArray Diagnostics); + SequenceEqualImmutableArray Diagnostics, + TypeSyntax UnwrapperSyntax); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/InlinedTypes.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/InlinedTypes.cs new file mode 100644 index 0000000000000..cb55e36b32cb9 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/InlinedTypes.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Microsoft.Interop +{ + internal static class InlinedTypes + { + /// + /// Returns the ClassDeclarationSyntax for: + /// + /// public sealed unsafe class ComWrappersUnwrapper : IUnmanagedObjectUnwrapper + /// { + /// public static object GetObjectForUnmanagedWrapper(void* ptr) + /// { + /// return ComWrappers.ComInterfaceDispatch.GetInstance((ComWrappers.ComInterfaceDispatch*)ptr); + /// } + /// } + /// + /// + public static ClassDeclarationSyntax ComWrappersUnwrapper { get; } = GetComWrappersUnwrapper(); + + public static ClassDeclarationSyntax GetComWrappersUnwrapper() + { + return ClassDeclaration("ComWrappersUnwrapper") + .AddModifiers(Token(SyntaxKind.SealedKeyword), + Token(SyntaxKind.UnsafeKeyword), + Token(SyntaxKind.StaticKeyword), + Token(SyntaxKind.FileKeyword)) + .AddMembers( + MethodDeclaration( + PredefinedType(Token(SyntaxKind.ObjectKeyword)), + Identifier("GetComObjectForUnmanagedWrapper")) + .AddModifiers(Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword)) + .AddParameterListParameters( + Parameter(Identifier("ptr")) + .WithType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))))) + .WithBody(body: Body())); + + static BlockSyntax Body() + { + var invocation = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("ComWrappers"), + IdentifierName("ComInterfaceDispatch")), + GenericName( + Identifier("GetInstance"), + TypeArgumentList( + SeparatedList( + new[] { PredefinedType(Token(SyntaxKind.ObjectKeyword)) }))))) + .AddArgumentListArguments( + Argument( + null, + Token(SyntaxKind.None), + CastExpression( + PointerType( + QualifiedName( + IdentifierName("ComWrappers"), + IdentifierName("ComInterfaceDispatch"))), + IdentifierName("ptr")))); + + return Block(ReturnStatement(invocation)); + } + } + + /// + /// + /// file static class UnmanagedObjectUnwrapper + /// { + /// public static object GetObjectForUnmanagedWrapper(void* ptr) where T : IUnmanagedObjectUnwrapper + /// { + /// return T.GetObjectForUnmanagedWrapper(ptr); + /// } + /// } + /// + /// + public static ClassDeclarationSyntax UnmanagedObjectUnwrapper { get; } = GetUnmanagedObjectUnwrapper(); + + private static ClassDeclarationSyntax GetUnmanagedObjectUnwrapper() + { + const string tUnwrapper = "TUnwrapper"; + return ClassDeclaration("UnmanagedObjectUnwrapper") + .AddModifiers(Token(SyntaxKind.FileKeyword), + Token(SyntaxKind.StaticKeyword)) + .AddMembers( + MethodDeclaration( + PredefinedType(Token(SyntaxKind.ObjectKeyword)), + Identifier("GetObjectForUnmanagedWrapper")) + .AddModifiers(Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword)) + .AddTypeParameterListParameters( + TypeParameter(Identifier(tUnwrapper))) + .AddParameterListParameters( + Parameter(Identifier("ptr")) + .WithType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))))) + .AddConstraintClauses(TypeParameterConstraintClause(IdentifierName(tUnwrapper)) + .AddConstraints(TypeConstraint(ParseTypeName(TypeNames.IUnmanagedObjectUnwrapper)))) + .WithBody(body: Body())); + + static BlockSyntax Body() + { + var invocation = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("T"), + IdentifierName("GetObjectForUnmanagedWrapper"))) + .AddArgumentListArguments( + Argument( + null, + Token(SyntaxKind.None), + IdentifierName("ptr"))); + + return Block(ReturnStatement(invocation)); + } + + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs index 9c615fc35539e..482cc2bc5c9bd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs @@ -55,7 +55,7 @@ public ManagedToNativeVTableMethodGenerator( if (implicitThis) { ImmutableArray.Builder newArgTypes = ImmutableArray.CreateBuilder(argTypes.Length + 1); - newArgTypes.Add(new TypePositionInfo(SpecialTypeInfo.IntPtr, NoMarshallingInfo.Instance) + newArgTypes.Add(new TypePositionInfo(new PointerTypeInfo("void*", "void*", false), NoMarshallingInfo.Instance) { InstanceIdentifier = NativeThisParameterIdentifier, NativeIndex = 0 @@ -101,7 +101,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray { - // var (, ) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey<>(); + // var (, ) = ((IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(typeof()); ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, @@ -121,12 +121,9 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray new PointerTypeInfo("void*", "void*", false); public IEnumerable Generate(TypePositionInfo info, StubCodeContext context) { + Debug.Assert(info.MarshallingAttributeInfo is NativeThisInfo); + TypeSyntax unwrapperType = ((NativeThisInfo)info.MarshallingAttributeInfo).UnwrapperType; if (context.CurrentStage != StubCodeContext.Stage.Unmarshal) { yield break; @@ -37,20 +37,25 @@ public IEnumerable Generate(TypePositionInfo info, StubCodeCont (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info); - // = IUnmanagedVirtualMethodTableProvider.GetObjectForUnmanagedWrapper<>(); + // = ()UnmanagedObjectUnwrapper.GetObjectFormUnmanagedWrapper(); yield return ExpressionStatement( - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IdentifierName(managedIdentifier), - InvocationExpression( - MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - ParseTypeName(TypeNames.IUnmanagedVirtualMethodTableProvider), - GenericName(Identifier("GetObjectForUnmanagedWrapper"), - TypeArgumentList( - SingletonSeparatedList( - info.ManagedType.Syntax)))), - ArgumentList( - SingletonSeparatedList( - Argument(IdentifierName(nativeIdentifier))))))); + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(managedIdentifier), + CastExpression( + info.ManagedType.Syntax, + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ParseTypeName(TypeNames.UnmanagedObjectUnwrapper), + GenericName(Identifier("GetObjectForUnmanagedWrapper")) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + unwrapperType)))), + ArgumentList( + SingletonSeparatedList( + Argument(IdentifierName(nativeIdentifier)))))))); } public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => SignatureBehavior.NativeType; diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx index 82a2cc8fc4b48..97486573d6657 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx @@ -207,6 +207,9 @@ Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + All methods must be declared in the same partial definition of a 'GeneratedComInterface'-attributed interface type to ensure reliable calculation for virtual method table offsets. @@ -216,4 +219,4 @@ Method is declared in different partial declaration than the 'GeneratedComInterface' attribute. - \ No newline at end of file + diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf index d76b50277644b..71d862c004ff3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf @@ -52,6 +52,11 @@ Metoda {0} je obsažena v typu {1}, který není označen jako „partial“. Generování zdrojů volání P/Invoke bude metodu {0} ignorovat. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. Metody označené atributem LibraryImportAttribute by měly být typu „static“, „partial“ a non-generic. Generování zdrojů volání P/Invoke bude ignorovat metody typu non-„static“, non-„partial“ a generic. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf index ca522c0ef98d8..257dd88e36ddd 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf @@ -52,6 +52,11 @@ Die Methode \"{0}\" ist in einem Typ \"{1}\" enthalten, der nicht als \"partiell\" gekennzeichnet ist. Die P/Invoke-Quellgenerierung ignoriert die Methode \"{0}\". + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. Methoden, die mit \"LibraryImportAttribute\" gekennzeichnet sind, sollten \"statisch\", \"partiell\" und nicht generisch sein. Die P/Invoke-Quellgenerierung ignoriert Methoden, die nicht \"statisch\", nicht \"partiell\" oder generisch sind. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf index bc90cc71066f5..794313970139c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf @@ -52,6 +52,11 @@ El método “{0}” está contenido en un tipo “{1}” que no está marcado como “partial”. La generación de código fuente P/Invoke omitirá el método “{0}”. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. Los métodos marcados con “LibraryImportAttribute” deben ser “static”, “partial” y no genéricos. La generación de código fuente P/Invoke omitirá los métodos que no sean “static”, “partial” ni genéricos. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf index 528f1abf71cb6..6c434311b99e3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf @@ -52,6 +52,11 @@ La méthode « {0} » est contenue dans un type « {1} » qui n’est pas marqué comme étant « partial ». La génération source P/Invoke ignore la méthode « {0} ». + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. Les méthodes marquées avec « LibraryImportAttribute » doivent être « static », « partial » et non génériques. La génération de source P/Invoke ignore les méthodes qui ne sont pas statiques, non partielles ou génériques. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf index 2f90533ef4242..ce7c2e937610b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf @@ -52,6 +52,11 @@ Il metodo '{0}' è contenuto in un tipo '{1}' non contrassegnato come 'partial'. Durante la generazione dell'origine P/Invoke il metodo '{0}' verrà ignorato. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. I metodi contrassegnati con 'LibraryImportAttribute' devono essere 'static', 'partial' e non generici. Durante la generazione dell'origine P/Invoke i metodi non 'static', non 'partial' o generici verranno ignorati. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf index e3d41118db979..63722d9db9fc1 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf @@ -52,6 +52,11 @@ メソッド '{0}' は、'partial' とマークされていない型 '{1}' に含まれています。P/Invoke ソース生成はメソッド '{0}' を無視します。 + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. 'LibraryImportAttribute' でマークされたメソッドは、'static'、'partial'、非ジェネリックである必要があります。P/Invoke ソース生成では、非 'static'、非 'partial'、ジェネリックであるメソッドは無視されます。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf index 9be2cb9b9411e..75f4f04f9c9d2 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf @@ -52,6 +52,11 @@ 메서드 '{0}'은(는) 'partial'로 표시되지 않은 '{1}' 형식에 포함되어 있습니다. P/Invoke 소스 생성은 '{0}' 메서드를 무시합니다. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. 'LibraryImportAttribute'로 표시된 메소드는 'static', 'partial' 및 비제네릭이어야 합니다. P/Invoke 소스 생성은 'static'이 아니거나 'partial'이 아니거나 제네릭인 메서드를 무시합니다. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf index 3c87b15f7f125..40e1d34cf8d41 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf @@ -52,6 +52,11 @@ Metoda „{0}” jest zawarta w typie „{1}”, który nie jest oznaczony jako „częściowy”. Generowanie źródła funkcji P/Invoke zignoruje metodę „{0}”. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. Metody oznaczone jako atrybut „LibraryImportAttribute” powinny być „statyczne”, „częściowe” i nieogólne. Generowanie źródła funkcji P/Invoke zignoruje metody, które nie są „statyczne”, nie są „częściowe” lub ogólne. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf index aede0eca68a46..d34e17dee12da 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf @@ -52,6 +52,11 @@ O '{0}' está contido em um tipo '{1}' que não está marcado como 'partial'. A geração de origem P/Invoke ignorará o método '{0}'. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. Os métodos marcados com 'LibraryImportAttribute' devem ser 'static', 'partial' e não genéricos. A geração de origem P/Invoke ignorará os métodos que não são 'static', não-'partial' ou genéricos. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf index 68eb144a0545d..cfc51db786aab 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf @@ -52,6 +52,11 @@ Метод \"{0}\" содержится в типе \"{1}\", который не помечен как \"partial\". Метод \"{0}\" будет игнорироваться при создании источника в P/Invoke. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. Методы, помеченные атрибутом \"LibraryImportAttribute\", должны быть \"static\", \"partial\" и неуниверсальными. При создании источника в P/Invoke будут игнорироваться методы, отличные от \"static\", \"partial\" или универсальные. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf index 25fdb1a6a558a..294324c80a7ec 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf @@ -52,6 +52,11 @@ '{0}'metodu, 'partial' olarak işaretlenmemiş olan bir '{1}' türünün içinde yer alıyor. P/Invoke kaynak oluşturma işlemi, '{0}' metodunu yok sayacak. + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. 'LibraryImportAttribute' ile işaretlenmiş metotlar 'static', 'partial' ve genel olmayan özellikte olmalıdır. P/Invoke kaynak oluşturma 'static', 'partial' ve genel olmayan özellikteki metotlar dışında kalan metotları yok sayar. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf index 24d4d0792d337..ba7a01a4c23ce 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf @@ -52,6 +52,11 @@ 方法“{0}”包含在未标记为 “partial” 的类型“{1}”中。P/Invoke 源生成将忽略方法“{0}”。 + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. 标记为 “LibraryImportAttribute” 的方法应为 “static”、“partial” 和非泛型。P/Invoke 源生成将忽略非“static”、“non--partial” 或泛型的方法。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf index 2a320c338038b..e2a48f7c24a4a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf @@ -52,6 +52,11 @@ 方法 '{0}' 包含在未標示為 'partial' 的類型'{1}'中。產生 P/Invoke 来源會忽略方法'{0}'。 + + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. + + Methods marked with 'LibraryImportAttribute' should be 'static', 'partial', and non-generic. P/Invoke source generation will ignore methods that are non-'static', non-'partial', or generic. 以 'LibraryImportAttribute' 標示的方法應該是 'static'、'partial' 和非泛型。產生 P/Invoke 来源會忽略非'static'、non-'partial' 或一般的方法。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs index e231025943365..9ff05126a089f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs @@ -336,7 +336,7 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M var interfaceType = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol.ContainingType); - INamedTypeSymbol expectedUnmanagedInterfaceType = iUnmanagedInterfaceTypeType.Construct(symbol.ContainingType); + INamedTypeSymbol expectedUnmanagedInterfaceType = iUnmanagedInterfaceTypeType; bool implementsIUnmanagedInterfaceOfSelf = symbol.ContainingType.AllInterfaces.Any(iface => SymbolEqualityComparer.Default.Equals(iface, expectedUnmanagedInterfaceType)); if (!implementsIUnmanagedInterfaceOfSelf) @@ -344,6 +344,13 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M // TODO: Report invalid configuration } + var unmanagedObjectUnwrapper = symbol.ContainingType.GetAttributes().FirstOrDefault(att => att.AttributeClass.IsOfType(TypeNames.UnmanagedObjectUnwrapperAttribute)); + if (unmanagedObjectUnwrapper is null) + { + // TODO: report invalid configuration - or ensure that this will never happen at this point + } + var unwrapperSyntax = ParseTypeName(unmanagedObjectUnwrapper.AttributeClass.TypeArguments[0].ToDisplayString()); + MarshallingInfo exceptionMarshallingInfo = CreateExceptionMarshallingInfo(virtualMethodIndexAttr, symbol, environment.Compilation, generatorDiagnostics, virtualMethodIndexData); return new IncrementalMethodStubGenerationContext( @@ -357,7 +364,8 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.ManagedToUnmanaged), ComInterfaceGeneratorHelpers.CreateGeneratorFactory(environment, MarshalDirection.UnmanagedToManaged), interfaceType, - new SequenceEqualImmutableArray(generatorDiagnostics.Diagnostics.ToImmutableArray())); + new SequenceEqualImmutableArray(generatorDiagnostics.Diagnostics.ToImmutableArray()), + unwrapperSyntax); } private static MarshallingInfo CreateExceptionMarshallingInfo(AttributeData virtualMethodIndexAttr, ISymbol symbol, Compilation compilation, GeneratorDiagnostics diagnostics, VirtualMethodIndexCompilationData virtualMethodIndexData) @@ -497,7 +505,7 @@ private static ImmutableArray AddImplicitElementInfos(Incremen var elements = ImmutableArray.CreateBuilder(originalElements.Length + 2); - elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, NativeThisInfo.Instance) + elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, new NativeThisInfo(methodStub.UnwrapperSyntax)) { InstanceIdentifier = ThisParameterIdentifier, NativeIndex = 0, @@ -553,6 +561,15 @@ private static ImmutableArray AddImplicitElementInfos(Incremen return Diagnostic.Create(GeneratorDiagnostics.ReturnConfigurationNotSupported, methodSyntax.Identifier.GetLocation(), "ref return", method.ToDisplayString()); } + // Verify there is an [UnmanagedObjectUnwrapperAttribute] + if (!method.ContainingType.GetAttributes().Any(att => att.AttributeClass.IsOfType(TypeNames.UnmanagedObjectUnwrapperAttribute))) + //!method.ContainingType.GetAttributes().Any(att => + //att.AttributeClass.MetadataName == TypeNames.UnmanagedObjectUnwrapperAttribute.Substring(TypeNames.UnmanagedObjectUnwrapperAttribute.LastIndexOf('.') + 1) + //&& att.AttributeClass.OriginalDefinition.ToDisplayString().Substring(0, att.AttributeClass.OriginalDefinition.ToDisplayString().LastIndexOf(att.AttributeClass.ToDisplayString())) == TypeNames.UnmanagedObjectUnwrapperAttribute.Substring(0, TypeNames.UnmanagedObjectUnwrapperAttribute.LastIndexOf('.')))) + { + return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttribute, methodSyntax.Identifier.GetLocation(), method.Name); + } + return null; } @@ -574,20 +591,20 @@ private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping[] = (nint)()&ABI_; + // [] = (void*)()&ABI_; populateVtableMethod = populateVtableMethod.AddBodyStatements( ExpressionStatement( AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, ElementAccessExpression( IdentifierName(vtableParameter)) .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))), - CastExpression(IdentifierName("nint"), + CastExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))), CastExpression(functionPointerType, PrefixUnaryExpression(SyntaxKind.AddressOfExpression, IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}"))))))); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/NativeToManagedStubCodeContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/NativeToManagedStubCodeContext.cs index 98da341b18c64..64b16d9eb98ca 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/NativeToManagedStubCodeContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/NativeToManagedStubCodeContext.cs @@ -3,6 +3,8 @@ using System; using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Syntax; + namespace Microsoft.Interop { public sealed record NativeToManagedStubCodeContext : StubCodeContext 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 60c3f393af59a..0a5b8ac985a3e 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 @@ -36,9 +36,9 @@ public static class TypeNames public const string VirtualMethodIndexAttribute = "System.Runtime.InteropServices.Marshalling.VirtualMethodIndexAttribute"; - public const string IUnmanagedVirtualMethodTableProvider = "System.Runtime.InteropServices.IUnmanagedVirtualMethodTableProvider"; + public const string IUnmanagedVirtualMethodTableProvider = "System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider"; - public const string IUnmanagedInterfaceType_Metadata = "System.Runtime.InteropServices.IUnmanagedInterfaceType`1"; + public const string IUnmanagedInterfaceType_Metadata = "System.Runtime.InteropServices.Marshalling.IUnmanagedInterfaceType"; public const string System_Span_Metadata = "System.Span`1"; public const string System_Span = "System.Span"; @@ -105,5 +105,10 @@ public static string MarshalEx(InteropGenerationOptions options) public const string InterfaceTypeAttribute = "System.Runtime.InteropServices.InterfaceTypeAttribute"; public const string ComInterfaceTypeAttribute = "System.Runtime.InteropServices.ComInterfaceType"; public const string System_Runtime_InteropServices_ComWrappers_ComInterfaceDispatch = "System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch"; + public const string ComWrappersUnwrapper = "System.Runtime.InteropServices.Marshalling.ComWrappersUnwrapper"; + public const string UnmanagedObjectUnwrapperAttribute = "System.Runtime.InteropServices.Marshalling.UnmanagedObjectUnwrapperAttribute`1"; + + public const string IUnmanagedObjectUnwrapper = "System.Runtime.InteropServices.Marshalling.IUnmanagedObjectUnwrapper"; + public const string UnmanagedObjectUnwrapper = "System.Runtime.InteropServices.Marshalling.UnmanagedObjectUnwrapper"; } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs index 154c93170b63f..1affbba906e4b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs @@ -300,5 +300,25 @@ public static (ImmutableArray TypeArguments, ImmutableArray + /// Returns if the type is of the string representation of a type + /// + public static bool IsOfType(this INamedTypeSymbol type, string typeName) + { + if (typeName.Contains('<') || typeName.Contains('+') || typeName.Contains('/')) + throw new ArgumentException($"Cannot handle type name in the format provided: {typeName}", nameof(typeName)); + string[] typeNameParts = typeName.Split('.'); + INamespaceOrTypeSymbol current = type; + for (int i = typeNameParts.Length - 1; i >= 0; i--) + { + if (current == null) + return false; + if (current.MetadataName != typeNameParts[i]) + return false; + current = (INamespaceOrTypeSymbol)current.ContainingType ?? current.ContainingNamespace; + } + return true; + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ComObject.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ComObject.cs new file mode 100644 index 0000000000000..c66d4a72c7b97 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ComObject.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +using System.Diagnostics; + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// Base class for all COM source generated Runtime Callable Wrapper (RCWs). + /// + public sealed unsafe class ComObject : IDynamicInterfaceCastable, IUnmanagedVirtualMethodTableProvider + { + private readonly void* _instancePointer; + + /// + /// Initialize ComObject instance. + /// + /// Strategy for getting details + /// Interaction strategy for IUnknown + /// Caching strategy + /// Pointer to the identity IUnknown interface for the object. + internal ComObject(IIUnknownInterfaceDetailsStrategy interfaceDetailsStrategy, IIUnknownStrategy iunknownStrategy, IIUnknownCacheStrategy cacheStrategy, void* thisPointer) + { + InterfaceDetailsStrategy = interfaceDetailsStrategy; + IUnknownStrategy = iunknownStrategy; + CacheStrategy = cacheStrategy; + _instancePointer = IUnknownStrategy.CreateInstancePointer(thisPointer); + } + + ~ComObject() + { + CacheStrategy.Clear(IUnknownStrategy); + IUnknownStrategy.Release(_instancePointer); + } + + /// + /// Interface details strategy. + /// + private IIUnknownInterfaceDetailsStrategy InterfaceDetailsStrategy { get; } + + /// + /// IUnknown interaction strategy. + /// + private IIUnknownStrategy IUnknownStrategy { get; } + + /// + /// Caching strategy. + /// + private IIUnknownCacheStrategy CacheStrategy { get; } + + /// + /// Returns an IDisposable that can be used to perform a final release + /// on this COM object wrapper. + /// + /// + /// This property will only be non-null if the ComObject was created using + /// CreateObjectFlags.UniqueInstance. + /// + public IDisposable? FinalRelease { get; internal init; } + + /// + RuntimeTypeHandle IDynamicInterfaceCastable.GetInterfaceImplementation(RuntimeTypeHandle interfaceType) + { + if (!LookUpVTableInfo(interfaceType, out IIUnknownCacheStrategy.TableInfo info, out int qiResult)) + { + Marshal.ThrowExceptionForHR(qiResult); + } + return info.ManagedType; + } + + /// + bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) + { + if (!LookUpVTableInfo(interfaceType, out _, out int qiResult)) + { + if (throwIfNotImplemented) + { + Marshal.ThrowExceptionForHR(qiResult); + } + return false; + } + return true; + } + + private bool LookUpVTableInfo(RuntimeTypeHandle handle, out IIUnknownCacheStrategy.TableInfo result, out int qiHResult) + { + qiHResult = 0; + if (!CacheStrategy.TryGetTableInfo(handle, out result)) + { + IUnknownDerivedDetails? details = InterfaceDetailsStrategy.GetIUnknownDerivedDetails(handle); + if (details is null) + { + return false; + } + int hr = IUnknownStrategy.QueryInterface(_instancePointer, details.Iid, out void* ppv); + if (hr < 0) + { + qiHResult = hr; + return false; + } + + result = CacheStrategy.ConstructTableInfo(handle, details, ppv); + + // Update some local cache. If the update fails, we lost the race and + // then are responsible for calling Release(). + if (!CacheStrategy.TrySetTableInfo(handle, result)) + { + bool found = CacheStrategy.TryGetTableInfo(handle, out result); + Debug.Assert(found); + _ = IUnknownStrategy.Release(ppv); + } + } + + return true; + } + + /// + VirtualMethodTableInfo IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableInfoForKey(Type type) + { + if (!LookUpVTableInfo(type.TypeHandle, out IIUnknownCacheStrategy.TableInfo result, out int qiHResult)) + { + Marshal.ThrowExceptionForHR(qiHResult); + } + + return new(result.ThisPtr, result.Table); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ComWrappersUnwrapper.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ComWrappersUnwrapper.cs new file mode 100644 index 0000000000000..2b5ecc3c36360 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ComWrappersUnwrapper.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Types that are only needed for the VTable source generator or to provide abstract concepts that the COM generator would use under the hood. +// These are types that we can exclude from the API proposals and either inline into the generated code, provide as file-scoped types, or not provide publicly (indicated by comments on each type). + +namespace System.Runtime.InteropServices.Marshalling +{ + // This type implements the logic to get the managed object from the unmanaged "this" pointer. + // If we decide to not expose the VTable source generator, we don't need to expose this and we can just inline the logic + // into the generated code in the source generator. + internal sealed unsafe class ComWrappersUnwrapper : IUnmanagedObjectUnwrapper + { + public static object GetObjectForUnmanagedWrapper(void* ptr) + { + return ComWrappers.ComInterfaceDispatch.GetInstance((ComWrappers.ComInterfaceDispatch*)ptr); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/DefaultCaching.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/DefaultCaching.cs new file mode 100644 index 0000000000000..07152e03414a9 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/DefaultCaching.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Implementations of the COM strategy interfaces defined in Com.cs that we would want to ship (can be internal only if we don't want to allow users to provide their own implementations in v1). +using System.Collections.Generic; + +namespace System.Runtime.InteropServices.Marshalling +{ + internal sealed unsafe class DefaultCaching : IIUnknownCacheStrategy + { + // [TODO] Implement some smart/thread-safe caching + private readonly Dictionary _cache = new(); + + IIUnknownCacheStrategy.TableInfo IIUnknownCacheStrategy.ConstructTableInfo(RuntimeTypeHandle handle, IUnknownDerivedDetails details, void* ptr) + { + var obj = (void***)ptr; + return new IIUnknownCacheStrategy.TableInfo() + { + ThisPtr = obj, + Table = *obj, + ManagedType = details.Implementation.TypeHandle + }; + } + + bool IIUnknownCacheStrategy.TryGetTableInfo(RuntimeTypeHandle handle, out IIUnknownCacheStrategy.TableInfo info) + { + return _cache.TryGetValue(handle, out info); + } + + bool IIUnknownCacheStrategy.TrySetTableInfo(RuntimeTypeHandle handle, IIUnknownCacheStrategy.TableInfo info) + { + return _cache.TryAdd(handle, info); + } + + void IIUnknownCacheStrategy.Clear(IIUnknownStrategy unknownStrategy) + { + foreach (var info in _cache.Values) + { + _ = unknownStrategy.Release(info.ThisPtr); + } + _cache.Clear(); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/DefaultIUnknownInterfaceDetailsStrategy.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/DefaultIUnknownInterfaceDetailsStrategy.cs new file mode 100644 index 0000000000000..b1c3ff0f2afe5 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/DefaultIUnknownInterfaceDetailsStrategy.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices.Marshalling +{ + internal sealed class DefaultIUnknownInterfaceDetailsStrategy : IIUnknownInterfaceDetailsStrategy + { + public static readonly IIUnknownInterfaceDetailsStrategy Instance = new DefaultIUnknownInterfaceDetailsStrategy(); + + public IUnknownDerivedDetails? GetIUnknownDerivedDetails(RuntimeTypeHandle type) + { + return IUnknownDerivedDetails.GetFromAttribute(type); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ExceptionHResultMarshaller.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ExceptionHResultMarshaller.cs index 22748d6969090..10fcf6912cd34 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ExceptionHResultMarshaller.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ExceptionHResultMarshaller.cs @@ -8,13 +8,24 @@ using System.Text; using System.Threading.Tasks; +// This type is only needed for the VTable source generator or to provide abstract concepts that the COM generator would use under the hood. +// These are types that we can exclude from the API proposals and either inline into the generated code, provide as file-scoped types, or not provide publicly (indicated by comments on each type). + namespace System.Runtime.InteropServices.Marshalling { /// /// Marshals an exception object to the value of its converted to . /// /// The unmanaged type to convert the HResult to. - [CustomMarshaller(typeof(Exception), MarshalMode.UnmanagedToManagedOut, typeof(ExceptionDefaultMarshaller<>))] + /// + /// This type is used by the COM source generator to enable marshalling exceptions to the HResult of the exception. + /// We can skip the exposing the exception marshallers if we decide to not expose the VTable source generator. + /// In that case, we'd hard-code the implementations of these marshallers into the COM source generator. + /// +#pragma warning disable SYSLIB1055 // The managed type 'System.Exception' for entry-point marshaller type 'System.Runtime.InteropServices.Marshalling.ExceptionHResultMarshaller' must be a closed generic type, have the same arity as the managed type if it is a value marshaller, or have one additional generic parameter if it is a collection marshaller. + + [CustomMarshaller(typeof(Exception), MarshalMode.UnmanagedToManagedOut, typeof(ExceptionHResultMarshaller<>))] +#pragma warning restore SYSLIB1055 public static class ExceptionHResultMarshaller where T : unmanaged, INumber { diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/FreeThreadedStrategy.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/FreeThreadedStrategy.cs new file mode 100644 index 0000000000000..e318b42cc91f9 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/FreeThreadedStrategy.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Implementations of the COM strategy interfaces defined in Com.cs that we would want to ship (can be internal only if we don't want to allow users to provide their own implementations in v1). +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.Marshalling +{ + internal sealed unsafe class FreeThreadedStrategy : IIUnknownStrategy + { + public static readonly IIUnknownStrategy Instance = new FreeThreadedStrategy(); + + void* IIUnknownStrategy.CreateInstancePointer(void* unknown) + { + return unknown; + } + + unsafe int IIUnknownStrategy.QueryInterface(void* thisPtr, in Guid handle, out void* ppObj) + { + int hr = Marshal.QueryInterface((nint)thisPtr, ref Unsafe.AsRef(in handle), out nint ppv); + if (hr < 0) + { + ppObj = null; + } + else + { + ppObj = (void*)ppv; + } + return hr; + } + + unsafe int IIUnknownStrategy.Release(void* thisPtr) + => Marshal.Release((nint)thisPtr); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComInterfaceAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComInterfaceAttribute.cs index dfd26a12c6edc..d8a5ac0e84f90 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComInterfaceAttribute.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComInterfaceAttribute.cs @@ -5,22 +5,6 @@ namespace System.Runtime.InteropServices.Marshalling { public interface IComObjectWrapper { } - public class ComWrappers { } - - public class ComObject : IDynamicInterfaceCastable, IComObjectWrapper - { - public bool IsInterfaceImplemented(RuntimeTypeHandle th, bool b) => true; - public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle th) => th; - // Implement support for casting through IUnknown. - // No thread-affinity aware support. - // No IDispatch support. - // No aggregation support. - } - - public abstract class GeneratedComWrappersBase : ComWrappers - { - } - [AttributeUsage(AttributeTargets.Interface)] public class GeneratedComInterfaceAttribute : Attribute { diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComWrappersBase.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComWrappersBase.cs new file mode 100644 index 0000000000000..7f3f420b1ce8c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/GeneratedComWrappersBase.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +using System.Collections; + +namespace System.Runtime.InteropServices.Marshalling +{ + public abstract class GeneratedComWrappersBase : ComWrappers + { + protected virtual IIUnknownInterfaceDetailsStrategy CreateInterfaceDetailsStrategy() => DefaultIUnknownInterfaceDetailsStrategy.Instance; + + protected virtual IIUnknownStrategy CreateIUnknownStrategy() => FreeThreadedStrategy.Instance; + + protected virtual IIUnknownCacheStrategy CreateCacheStrategy() => new DefaultCaching(); + + protected override sealed unsafe object CreateObject(nint externalComObject, CreateObjectFlags flags) + { + if (flags.HasFlag(CreateObjectFlags.TrackerObject) + || flags.HasFlag(CreateObjectFlags.Aggregation)) + { + throw new NotSupportedException(); + } + + var rcw = new ComObject(CreateInterfaceDetailsStrategy(), CreateIUnknownStrategy(), CreateCacheStrategy(), (void*)externalComObject); + if (flags.HasFlag(CreateObjectFlags.UniqueInstance)) + { + // Set value on MyComObject to enable the FinalRelease option. + // This could also be achieved through an internal factory + // function on ComObject type. + } + return rcw; + } + + protected override sealed void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + + public ComObject GetOrCreateUniqueObjectForComInstance(nint comInstance, CreateObjectFlags flags) + { + if (flags.HasFlag(CreateObjectFlags.Unwrap)) + { + throw new ArgumentException("Cannot create a unique object if unwrapping a ComWrappers-based COM object is requested.", nameof(flags)); + } + return (ComObject)GetOrCreateObjectForComInstance(comInstance, flags | CreateObjectFlags.UniqueInstance); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownCacheStrategy.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownCacheStrategy.cs new file mode 100644 index 0000000000000..d043344b340d5 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownCacheStrategy.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// Unmanaged virtual method table look up strategy. + /// + public unsafe interface IIUnknownCacheStrategy + { + public readonly struct TableInfo + { + public void* ThisPtr { get; init; } + public void** Table { get; init; } + public RuntimeTypeHandle ManagedType { get; init; } + } + + /// + /// Construct a instance. + /// + /// RuntimeTypeHandle instance + /// Pointer to the instance to query + /// A instance + /// True if success, otherwise false. + TableInfo ConstructTableInfo(RuntimeTypeHandle handle, IUnknownDerivedDetails interfaceDetails, void* ptr); + + /// + /// Get associated . + /// + /// RuntimeTypeHandle instance + /// A instance + /// True if found, otherwise false. + bool TryGetTableInfo(RuntimeTypeHandle handle, out TableInfo info); + + /// + /// Set associated . + /// + /// RuntimeTypeHandle instance + /// A instance + /// True if set, otherwise false. + bool TrySetTableInfo(RuntimeTypeHandle handle, TableInfo info); + + /// + /// Clear the cache + /// + /// The to use for clearing + void Clear(IIUnknownStrategy unknownStrategy); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownInterfaceDetailsStrategy.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownInterfaceDetailsStrategy.cs new file mode 100644 index 0000000000000..cd9f84931448b --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownInterfaceDetailsStrategy.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// Strategy for acquiring interface details. + /// + public interface IIUnknownInterfaceDetailsStrategy + { + /// + /// Given a get the IUnknown details. + /// + /// RuntimeTypeHandle instance + /// Details if type is known. + IUnknownDerivedDetails? GetIUnknownDerivedDetails(RuntimeTypeHandle type); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownInterfaceType.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownInterfaceType.cs new file mode 100644 index 0000000000000..ccffb1e9b87e6 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownInterfaceType.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +namespace System.Runtime.InteropServices.Marshalling +{ + public interface IIUnknownInterfaceType : IUnmanagedInterfaceType + { + public abstract static Guid Iid { get; } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownStrategy.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownStrategy.cs new file mode 100644 index 0000000000000..597c811321f21 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IIUnknownStrategy.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// IUnknown interaction strategy. + /// + public unsafe interface IIUnknownStrategy + { + /// + /// Create an instance pointer that represents the provided IUnknown instance. + /// + /// The IUnknown instance. + /// A pointer representing the unmanaged instance. + /// + /// This method is used to create an instance pointer that can be used to interact with the other members of this interface. + /// For example, this method can return an IAgileReference instance for the provided IUnknown instance + /// that can be used in the QueryInterface and Release methods to enable creating thread-local instance pointers to us + /// through the IAgileReference APIs instead of directly calling QueryInterface on the IUnknown. + /// + public void* CreateInstancePointer(void* unknown); + + /// + /// Perform a QueryInterface() for an IID on the unmanaged instance. + /// + /// A pointer representing the unmanaged instance. + /// The IID (Interface ID) to query for. + /// The resulting interface + /// Returns an HRESULT represents the success of the operation + /// + public int QueryInterface(void* instancePtr, in Guid iid, out void* ppObj); + + /// + /// Perform a Release() call on the supplied unmanaged instance. + /// + /// A pointer representing the unmanaged instance. + /// The current reference count. + /// + public int Release(void* instancePtr); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs new file mode 100644 index 0000000000000..9addfc0019497 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +namespace System.Runtime.InteropServices.Marshalling +{ + [AttributeUsage(AttributeTargets.Interface)] + public class IUnknownDerivedAttribute : Attribute, IUnknownDerivedDetails + where T : IIUnknownInterfaceType + where TImpl : T + { + public IUnknownDerivedAttribute() + { + } + + /// + public Guid Iid => T.Iid; + + /// + public Type Implementation => typeof(TImpl); + + /// + public unsafe void* VirtualMethodTableManagedImplementation => T.VirtualMethodTableManagedImplementation; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedDetails.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedDetails.cs new file mode 100644 index 0000000000000..ecd5a5456bdf2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedDetails.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM source generator and implements part of the COM-specific interactions. +// This API need to be exposed to implement the COM source generator in one form or another. + +using System.Reflection; + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// Details for the IUnknown derived interface. + /// + public interface IUnknownDerivedDetails + { + /// + /// Interface ID. + /// + public Guid Iid { get; } + + /// + /// Managed type used to project the IUnknown derived interface. + /// + public Type Implementation { get; } + + /// + /// A pointer to the virtual method table to enable unmanaged callers to call a managed implementation of the interface. + /// + public unsafe void* VirtualMethodTableManagedImplementation { get; } + + internal static IUnknownDerivedDetails? GetFromAttribute(RuntimeTypeHandle handle) + { + var type = Type.GetTypeFromHandle(handle); + if (type is null) + { + return null; + } + return (IUnknownDerivedDetails?)type.GetCustomAttribute(typeof(IUnknownDerivedAttribute<,>)); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedInterfaceType.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedInterfaceType.cs new file mode 100644 index 0000000000000..cbccd8d48de71 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedInterfaceType.cs @@ -0,0 +1,29 @@ +// 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.Text; +using System.Threading.Tasks; + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// This interface allows another interface to define that it represents a managed projection of an unmanaged interface from some unmanaged type system. + /// + /// The managed interface. + /// The type of a value that can represent types from the corresponding unmanaged type system. + public unsafe interface IUnmanagedInterfaceType + { + /// + /// Get a pointer to the virtual method table of managed implementations of the unmanaged interface type. + /// + /// A pointer to the virtual method table of managed implementations of the unmanaged interface type + /// + /// Implementation will be provided by a source generator if not explicitly implemented. + /// This property can return null. If it does, then the interface is not supported for passing managed implementations to unmanaged code. + /// + public abstract static void* VirtualMethodTableManagedImplementation { get; } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedObjectUnwrapper.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedObjectUnwrapper.cs new file mode 100644 index 0000000000000..51cc0647bc104 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedObjectUnwrapper.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is only needed for the VTable source generator or to provide abstract concepts that the COM generator would use under the hood. +// These are types that we can exclude from the API proposals and either inline into the generated code, provide as file-scoped types, or not provide publicly (indicated by comments on each type). + +using System.Numerics; + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// A factory to create an unmanaged "this pointer" from a managed object and to get a managed object from an unmanaged "this pointer". + /// + /// + /// This interface would be used by the VTable source generator to enable users to indicate how to get the managed object from the "this pointer". + /// We can hard-code the ComWrappers logic here if we don't want to ship this interface. + /// + public unsafe interface IUnmanagedObjectUnwrapper + { + /// + /// Get the object wrapped by . + /// + /// A an unmanaged "this pointer". + /// The object wrapped by . + public static abstract object GetObjectForUnmanagedWrapper(void* ptr); + } +} 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 99e4309726ca9..bcf46ba206265 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedVirtualMethodTableProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnmanagedVirtualMethodTableProvider.cs @@ -1,56 +1,12 @@ // 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.Text; -using System.Threading.Tasks; - -namespace System.Runtime.InteropServices +// This type is for the COM Source Generator and defines basic vtable interactions that we would need in the COM source generator in one form or another. +namespace System.Runtime.InteropServices.Marshalling { /// - /// Information about a virtual method table and the unmanaged instance pointer. + /// This interface allows an object to provide information about a virtual method table for a managed interface to enable invoking methods in the virtual method table. /// - public readonly ref struct VirtualMethodTableInfo - { - /// - /// Construct a from a given instance pointer and table memory. - /// - /// The pointer to the instance. - /// The block of memory that represents the virtual method table. - public VirtualMethodTableInfo(IntPtr thisPointer, ReadOnlySpan virtualMethodTable) - { - ThisPointer = thisPointer; - VirtualMethodTable = virtualMethodTable; - } - - /// - /// The unmanaged instance pointer - /// - public IntPtr ThisPointer { get; } - - /// - /// The virtual method table. - /// - public ReadOnlySpan VirtualMethodTable { get; } - - /// - /// Deconstruct this structure into its two fields. - /// - /// The result - /// The result - public void Deconstruct(out IntPtr thisPointer, out ReadOnlySpan virtualMethodTable) - { - thisPointer = ThisPointer; - virtualMethodTable = VirtualMethodTable; - } - } - - /// - /// 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 { /// @@ -58,98 +14,6 @@ public unsafe interface IUnmanagedVirtualMethodTableProvider /// /// The managed type for the unmanaged interface. /// The virtual method table information for the unmanaged interface. - protected VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type); - - /// - /// Get the information about the virtual method table for the given unmanaged interface type. - /// - /// 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 - { - return GetVirtualMethodTableInfoForKey(typeof(TUnmanagedInterfaceType)); - } - - /// - /// Get the length of the virtual method table for the given unmanaged interface type. - /// - /// The 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 - { - return TUnmanagedInterfaceType.VirtualMethodTableLength; - } - - /// - /// Get a pointer to the virtual method table of managed implementations of the unmanaged interface type. - /// - /// 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 - { - return TUnmanagedInterfaceType.VirtualMethodTableManagedImplementation; - } - - /// - /// Get a pointer that wraps a managed implementation of an unmanaged interface that can be passed to unmanaged code. - /// - /// The managed type that represents the unmanaged interface. - /// 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 - { - return TUnmanagedInterfaceType.GetUnmanagedWrapperForObject(obj); - } - - /// - /// Get the object wrapped by . - /// - /// The managed type that represents the unmanaged interface. - /// A pointer-sized value returned by or . - /// The object wrapped by . - public static TUnmanagedInterfaceType GetObjectForUnmanagedWrapper(void* ptr) - where TUnmanagedInterfaceType : IUnmanagedInterfaceType - { - return TUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(ptr); - } - } - - /// - /// This interface allows another interface to define that it represents a managed projection of an unmanaged interface from some unmanaged type system. - /// - /// 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 - { - /// - /// Get the length of the virtual method table for the given unmanaged interface type. - /// - /// The length of the virtual method table for the unmanaged interface. - public static abstract int VirtualMethodTableLength { get; } - - /// - /// Get a pointer to the virtual method table of managed implementations of the unmanaged interface type. - /// - /// A pointer to the virtual method table of managed implementations of the unmanaged interface type - public static abstract void* VirtualMethodTableManagedImplementation { get; } - - /// - /// Get a pointer that wraps a managed implementation of an unmanaged interface that can be passed to unmanaged code. - /// - /// The managed object that implements the unmanaged interface. - /// A pointer-sized value that can be passed to unmanaged code that represents - public static abstract void* GetUnmanagedWrapperForObject(TInterface obj); - - /// - /// Get the object wrapped by . - /// - /// A pointer-sized value returned by or . - /// The object wrapped by . - public static abstract TInterface GetObjectForUnmanagedWrapper(void* ptr); + public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/UnmanagedObjectUnwrapper.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/UnmanagedObjectUnwrapper.cs new file mode 100644 index 0000000000000..4494942d20a30 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/UnmanagedObjectUnwrapper.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices.Marshalling; + +// This type should be inlined in the COM source generator and made internal +// This type allows the generated code to call a private explicit implementation of IUnmanagedObjectUnwrapper.GetObjectForUnmanagedWrapper +public unsafe static class UnmanagedObjectUnwrapper +{ + public static object GetObjectForUnmanagedWrapper(void* ptr) where T : IUnmanagedObjectUnwrapper + { + return T.GetObjectForUnmanagedWrapper(ptr); + } + + // This type is provided for the unit tests that only confirm the generated code compiles + public class TestUnwrapper : IUnmanagedObjectUnwrapper + { + public static object GetObjectForUnmanagedWrapper(void* ptr) => throw new NotImplementedException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/UnmanagedObjectUnwrapperAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/UnmanagedObjectUnwrapperAttribute.cs new file mode 100644 index 0000000000000..128eb12cc9d1e --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/UnmanagedObjectUnwrapperAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type only needed for the VTable source generator or to provide abstract concepts that the COM generator would use under the hood. +// These are types that we can exclude from the API proposals and either inline into the generated code, provide as file-scoped types, or not provide publicly (indicated by comments on each type). + +namespace System.Runtime.InteropServices.Marshalling +{ + // This attribute provides the mechanism for the VTable source generator to know which type to use to get the managed object + // from the unmanaged "this" pointer. If we decide to not expose VirtualMethodIndexAttribute, we don't need to expose this. + [AttributeUsage(AttributeTargets.Interface)] + public class UnmanagedObjectUnwrapperAttribute : Attribute + where TMapper : IUnmanagedObjectUnwrapper + { + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/VirtualMethodIndexAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/VirtualMethodIndexAttribute.cs index 3110fac55cd07..a248a79d3bc63 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/VirtualMethodIndexAttribute.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/VirtualMethodIndexAttribute.cs @@ -10,6 +10,8 @@ namespace System.Runtime.InteropServices.Marshalling { + // This type is only needed for the VTable source generator or to provide abstract concepts that the COM generator would use under the hood. + // These are types that we can exclude from the API proposals and either inline into the generated code, provide as file-scoped types, or not provide publicly (indicated by comments on each type). [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class VirtualMethodIndexAttribute : Attribute { diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/VirtualMethodTableInfo.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/VirtualMethodTableInfo.cs new file mode 100644 index 0000000000000..498102f8b78b3 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/VirtualMethodTableInfo.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This type is for the COM Source Generator and defines basic vtable interactions that we would need in the COM source generator in one form or another. +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// Information about a virtual method table and the unmanaged instance pointer. + /// + public readonly unsafe struct VirtualMethodTableInfo + { + /// + /// Construct a from a given instance pointer and table memory. + /// + /// The pointer to the instance. + /// The block of memory that represents the virtual method table. + public VirtualMethodTableInfo(void* thisPointer, void** virtualMethodTable) + { + ThisPointer = thisPointer; + VirtualMethodTable = virtualMethodTable; + } + + /// + /// The unmanaged instance pointer + /// + public void* ThisPointer { get; } + + /// + /// The virtual method table. + /// + public void** VirtualMethodTable { get; } + + /// + /// Deconstruct this structure into its two fields. + /// + /// The result + /// The result + public void Deconstruct(out void* thisPointer, out void** virtualMethodTable) + { + thisPointer = ThisPointer; + virtualMethodTable = VirtualMethodTable; + } + } +} 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 55174ae53b317..e6214e759450e 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ImplicitThisTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/ImplicitThisTests.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +using ComInterfaceGenerator.Tests; using Xunit; namespace ComInterfaceGenerator.Tests @@ -13,34 +14,23 @@ internal unsafe partial class NativeExportsNE { internal partial class ImplicitThis { - internal partial interface INativeObject : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapperAttribute>] + internal partial interface INativeObject : IUnmanagedInterfaceType { - static int IUnmanagedInterfaceType.VirtualMethodTableLength => 2; - - 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*) * 2); + static void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation { get { if (s_vtable[0] == null) { - Native.PopulateUnmanagedVirtualMethodTable(new Span(s_vtable, IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength())); + Native.PopulateUnmanagedVirtualMethodTable(s_vtable); } return s_vtable; } } - static void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(INativeObject obj) - { - return VTableGCHandlePair.Allocate(obj); - } - - static INativeObject IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) - { - return VTableGCHandlePair.GetObject(ptr); - } - [VirtualMethodIndex(0, ImplicitThisParameter = true)] int GetData(); [VirtualMethodIndex(1, ImplicitThisParameter = true)] @@ -60,7 +50,7 @@ public NativeObject(void* pointer) public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type) { Assert.Equal(typeof(INativeObject), type); - return new VirtualMethodTableInfo((IntPtr)_pointer, new ReadOnlySpan(*(void**)_pointer, 2)); + return new VirtualMethodTableInfo(_pointer, *(void***)_pointer); } public void Dispose() @@ -113,7 +103,7 @@ public unsafe void ValidateImplicitThisUnmanagedToManagedFunctionCallsSucceed() ManagedObjectImplementation impl = new ManagedObjectImplementation(startingValue); - void* wrapper = IUnmanagedVirtualMethodTableProvider.GetUnmanagedWrapperForObject(impl); + void* wrapper = VTableGCHandlePair.Allocate(impl); Assert.Equal(startingValue, NativeExportsNE.ImplicitThis.GetNativeObjectData(wrapper)); NativeExportsNE.ImplicitThis.SetNativeObjectData(wrapper, newValue); 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 fd0d0a4498ed9..e86ae016aed35 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/NoImplicitThisTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/NoImplicitThisTests.cs @@ -12,14 +12,10 @@ internal unsafe partial class NativeExportsNE { internal partial class NoImplicitThis { - internal partial interface IStaticMethodTable : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapperAttribute>] + internal partial interface IStaticMethodTable : IUnmanagedInterfaceType { - static int IUnmanagedInterfaceType.VirtualMethodTableLength => 2; - static void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; - - static void* IUnmanagedInterfaceType.GetUnmanagedWrapperForObject(IStaticMethodTable obj) => null; - - static IStaticMethodTable IUnmanagedInterfaceType.GetObjectForUnmanagedWrapper(void* ptr) => null; + static void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; [VirtualMethodIndex(0, Direction = MarshalDirection.ManagedToUnmanaged, ImplicitThisParameter = false)] int Add(int x, int y); @@ -40,7 +36,7 @@ public StaticMethodTable(void* vtableStart) public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(Type type) { Assert.Equal(typeof(IStaticMethodTable), type); - return new VirtualMethodTableInfo(IntPtr.Zero, new ReadOnlySpan(_vtableStart, IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableLength())); + return new VirtualMethodTableInfo((void*)null, (void**)_vtableStart); } } 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 c6ee2cd505d0c..30a794697ed08 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/VTableGCHandlePair.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/VTableGCHandlePair.cs @@ -2,15 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace ComInterfaceGenerator.Tests { - public static unsafe class VTableGCHandlePair - where TUnmanagedInterface : IUnmanagedInterfaceType + public unsafe class VTableGCHandlePair : IUnmanagedObjectUnwrapper + 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*) * 2); unmanaged[0] = TUnmanagedInterface.VirtualMethodTableManagedImplementation; unmanaged[1] = (void*)GCHandle.ToIntPtr(GCHandle.Alloc(obj)); return unmanaged; @@ -26,5 +27,8 @@ public static TUnmanagedInterface GetObject(void* pair) { return (TUnmanagedInterface)GCHandle.FromIntPtr((nint)((void**)pair)[1]).Target; } + + static object IUnmanagedObjectUnwrapper.GetObjectForUnmanagedWrapper(void* ptr) => + (TUnmanagedInterface)GCHandle.FromIntPtr((nint)((void**)ptr)[1]).Target; } } 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 d8033b561084e..72fe813259d87 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,7 +23,8 @@ public async Task NoSpecifiedCallConvForwardsDefault() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] @@ -49,7 +50,8 @@ public async Task SuppressGCTransitionAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [SuppressGCTransitionAttribute] @@ -76,7 +78,8 @@ public async Task EmptyUnmanagedCallConvAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [UnmanagedCallConv] @@ -103,7 +106,8 @@ public async Task SimpleUnmanagedCallConvAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] @@ -130,7 +134,8 @@ public async Task ComplexUnmanagedCallConvAttributeForwarded() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl), typeof(CallConvMemberFunction) })] @@ -163,7 +168,8 @@ public async Task ComplexUnmanagedCallConvAttributeWithSuppressGCTransitionForwa using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [SuppressGCTransition] 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 ef02fb3181843..9314731648316 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 @@ -18,10 +18,7 @@ partial interface INativeAPI """; 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; + static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; """; public static string NativeInterfaceUsage() => @" @@ -36,7 +33,8 @@ sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType { [VirtualMethodIndex(0)] void Method(); @@ -46,7 +44,8 @@ partial interface INativeAPI : IUnmanagedInterfaceType using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType { [VirtualMethodIndex(0, ImplicitThisParameter = false)] void Method(); @@ -57,7 +56,8 @@ partial interface INativeAPI : IUnmanagedInterfaceType using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType { [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] @@ -89,7 +89,8 @@ public static string BasicParametersAndModifiers(string typeName, string preDecl [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0)] {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); @@ -102,7 +103,8 @@ public static string BasicParametersAndModifiersManagedToUnmanaged(string typeNa [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, Direction = MarshalDirection.ManagedToUnmanaged)] {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); @@ -116,7 +118,8 @@ public static string BasicParametersAndModifiersNoRef(string typeName, string pr [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0)] {typeName} Method({typeName} value, in {typeName} inValue, out {typeName} outValue); @@ -126,7 +129,8 @@ public static string BasicParametersAndModifiersNoImplicitThis(string typeName) using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = false)] {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue); @@ -140,7 +144,8 @@ public static string MarshalUsingCollectionCountInfoParametersAndModifiers(strin using System.Runtime.InteropServices.Marshalling; [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0)] [return:MarshalUsing(ConstantElementCount=10)] @@ -159,7 +164,8 @@ public static string BasicReturnTypeComExceptionHandling(string typeName, string using System.Runtime.InteropServices.Marshalling; {preDeclaration} -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ExceptionMarshalling = ExceptionMarshalling.Com)] {typeName} Method(); @@ -171,7 +177,8 @@ public static string BasicReturnTypeCustomExceptionHandling(string typeName, str using System.Runtime.InteropServices.Marshalling; {preDeclaration} -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, CustomExceptionMarshallingType = typeof({customExceptionType}))] {typeName} Method(); diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs index 7f34b242e2bed..d93701cfc5b3f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs @@ -35,8 +35,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -54,8 +55,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -74,8 +76,10 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -94,8 +98,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -114,8 +119,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -134,8 +140,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -154,8 +161,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -174,8 +182,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -198,8 +207,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -223,8 +233,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -244,8 +255,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -265,8 +277,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -290,8 +303,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -315,8 +329,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -340,8 +355,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -365,8 +381,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -390,8 +407,9 @@ interface IFoo void Bar() {} } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -421,8 +439,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -444,8 +463,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -467,8 +487,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -494,8 +515,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -521,8 +543,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -548,8 +571,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -575,8 +599,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; @@ -602,8 +627,9 @@ void Bar() {} [GeneratedComInterface(typeof(MyComWrappers))] partial interface IFoo { } - public partial class MyComWrappers : GeneratedComWrappersBase + public unsafe partial class MyComWrappers : GeneratedComWrappersBase { + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) {count = 0; return null;} } """; 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 8ad0d298d7a92..dc683b4b1afc1 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 @@ -27,10 +27,7 @@ partial interface INativeAPI """; 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; + static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null; """; public static abstract string NativeInterfaceUsage(); @@ -42,7 +39,8 @@ static string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifier [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +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); @@ -55,7 +53,8 @@ static string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifier [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +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); @@ -67,7 +66,8 @@ static string ICustomMarshallingSignatureTestProvider.BasicParameterByValue(stri using System.Runtime.InteropServices.Marshalling; {preDeclaration} -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] void Method({typeName} value); @@ -81,7 +81,8 @@ static string ICustomMarshallingSignatureTestProvider.BasicParameterWithByRefMod [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] void Method({modifier} {typeName} value); @@ -92,7 +93,8 @@ static string ICustomMarshallingSignatureTestProvider.BasicReturnType(string typ using System.Runtime.InteropServices.Marshalling; {preDeclaration} -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] {typeName} Method(); @@ -103,7 +105,8 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingParametersAndM using System.Runtime.InteropServices.Marshalling; {preDeclaration} -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return: MarshalUsing(typeof({marshallerTypeName}))] @@ -119,7 +122,8 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionCoun using System.Runtime.InteropServices.Marshalling; [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(ConstantElementCount=10)] @@ -138,7 +142,8 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionPara [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(typeof({marshallerType}), ConstantElementCount=10)] @@ -158,7 +163,8 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionRetu [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] int Method( @@ -174,7 +180,8 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionOutC [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] int Method( @@ -190,7 +197,8 @@ static string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionRetu [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(ConstantElementCount = 10)] @@ -205,7 +213,8 @@ static string ICustomMarshallingSignatureTestProvider.CustomElementMarshalling(s [assembly:DisableRuntimeMarshalling] -partial interface INativeAPI : IUnmanagedInterfaceType +[UnmanagedObjectUnwrapper] +partial interface INativeAPI : IUnmanagedInterfaceType {{ [VirtualMethodIndex(0, ImplicitThisParameter = {TProvider.ImplicitThisParameter.ToString().ToLowerInvariant()}, Direction = MarshalDirection.{TProvider.Direction})] [return:MarshalUsing(ConstantElementCount=10)] 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 6fa6406e0f4b9..fefc9223a2f0d 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,7 +23,8 @@ public async Task NativeInterfaceNestedInUserInterface() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] @@ -49,7 +50,8 @@ public async Task NativeInterfaceInheritsFromUserInterface() using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] @@ -75,7 +77,8 @@ public async Task NativeInterfaceHasDynamicInterfaceCastableImplementationAttrib using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)] @@ -106,7 +109,8 @@ public async Task NativeInterfaceHasStaticMethodWithSameNameWithExpectedPrefixWi using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; - partial interface INativeAPI : IUnmanagedInterfaceType + [UnmanagedObjectUnwrapper] + partial interface INativeAPI : IUnmanagedInterfaceType { {{CodeSnippets.INativeAPI_IUnmanagedInterfaceTypeMethodImpl}} [VirtualMethodIndex(0)]