Change the ComInterfaceGenerator to output source in one file per interface and use...
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Tue, 7 Mar 2023 21:03:33 +0000 (13:03 -0800)
committerGitHub <noreply@github.com>
Tue, 7 Mar 2023 21:03:33 +0000 (13:03 -0800)
30 files changed:
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalMethodStubGenerationContext.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/IncrementalValuesProviderExtensions.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/Strings.resx
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.cs.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.de.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.es.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.fr.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.it.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ja.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ko.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pl.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.pt-BR.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.ru.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.tr.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hans.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Resources/xlf/Strings.zh-Hant.xlf
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VirtualMethodPointerStubGenerator.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VtableIndexStubGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentNode.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs
src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/IUnknownDerivedAttribute.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAttributeProvider.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/VTableGeneratorOutputShape.cs [moved from src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/NativeInterfaceShape.cs with 84% similarity]

index a2c9391..659570a 100644 (file)
@@ -2,12 +2,10 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-using System.Collections.Generic;
 using System.Collections.Immutable;
-using System.Diagnostics;
+using System.IO;
 using System.Linq;
 using System.Threading;
-using System.Xml.Linq;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -18,21 +16,33 @@ namespace Microsoft.Interop
     [Generator]
     public sealed class ComInterfaceGenerator : IIncrementalGenerator
     {
-        private sealed record ComInterfaceContext(int MethodStartIndex, Guid InterfaceId);
+        private sealed record ComInterfaceContext(
+            ManagedTypeInfo InterfaceType,
+            ContainingSyntaxContext TypeDefinitionContext,
+            ContainingSyntax InterfaceTypeSyntax,
+            int MethodStartIndex,
+            Guid InterfaceId);
+
+        private sealed record class GeneratedStubCodeContext(
+            ManagedTypeInfo OriginalDefiningType,
+            ContainingSyntaxContext ContainingSyntaxContext,
+            SyntaxEquivalentNode<MethodDeclarationSyntax> Stub,
+            SequenceEqualImmutableArray<Diagnostic> Diagnostics) : GeneratedMethodContextBase(OriginalDefiningType, Diagnostics);
+
+        private sealed record SkippedStubContext(ManagedTypeInfo OriginalDefiningType) : GeneratedMethodContextBase(OriginalDefiningType, new(ImmutableArray<Diagnostic>.Empty));
 
         public static class StepNames
         {
             public const string CalculateStubInformation = nameof(CalculateStubInformation);
             public const string GenerateManagedToNativeStub = nameof(GenerateManagedToNativeStub);
             public const string GenerateNativeToManagedStub = nameof(GenerateNativeToManagedStub);
+            public const string GenerateManagedToNativeInterfaceImplementation = nameof(GenerateManagedToNativeInterfaceImplementation);
+            public const string GenerateNativeToManagedVTableMethods = nameof(GenerateNativeToManagedVTableMethods);
+            public const string GenerateNativeToManagedVTable = nameof(GenerateNativeToManagedVTable);
+            public const string GenerateInterfaceInformation = nameof(GenerateInterfaceInformation);
+            public const string GenerateIUnknownDerivedAttribute = nameof(GenerateIUnknownDerivedAttribute);
         }
 
-        private static readonly ContainingSyntax NativeTypeContainingSyntax = new(
-                                    TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.PartialKeyword)),
-                                    SyntaxKind.InterfaceDeclaration,
-                                    Identifier("Native"),
-                                    null);
-
         public void Initialize(IncrementalGeneratorInitializationContext context)
         {
             // Get all methods with the [GeneratedComInterface] attribute.
@@ -60,25 +70,33 @@ namespace Microsoft.Interop
             {
                 // Start at offset 3 as 0-2 are IUnknown.
                 // TODO: Calculate starting offset based on base types.
-                // TODO: Extract IID from source.
-                return new ComInterfaceContext(3, Guid.Empty);
+                Guid? guid = null;
+                var guidAttr = data.Symbol.GetAttributes().Where(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_GuidAttribute).SingleOrDefault();
+                if (guidAttr is not null)
+                {
+                    string? guidstr = guidAttr.ConstructorArguments.SingleOrDefault().Value as string;
+                    if (guidstr is not null)
+                        guid = new Guid(guidstr);
+                }
+                return new ComInterfaceContext(
+                    ManagedTypeInfo.CreateTypeInfoForTypeSymbol(data.Symbol),
+                    new ContainingSyntaxContext(data.Syntax),
+                    new ContainingSyntax(data.Syntax.Modifiers, data.Syntax.Kind(), data.Syntax.Identifier, data.Syntax.TypeParameterList),
+                    3,
+                    guid ?? Guid.Empty);
             });
 
-            context.RegisterSourceOutput(invalidTypeDiagnostics, static (context, invalidType) =>
-            {
-                context.ReportDiagnostic(invalidType.Diagnostic);
-            });
+            context.RegisterDiagnostics(invalidTypeDiagnostics.Select((data, ct) => data.Diagnostic));
 
             // Zip the incremental interface context back with the symbols and syntax for the interface
             // to calculate the methods to generate.
             // The generator infrastructure preserves ordering of the tables once Select statements are in use,
             // so we can rely on the order matching here.
-            var methodsWithDiagnostics = interfacesToGenerate
+            var interfacesWithMethods = interfacesToGenerate
                 .Zip(interfaceContexts)
-                .SelectMany(static (data, ct) =>
+                .Select(static (data, ct) =>
             {
                 var (interfaceData, interfaceContext) = data;
-                ContainingSyntaxContext containingSyntax = new(interfaceData.Syntax);
                 Location interfaceLocation = interfaceData.Syntax.GetLocation();
                 var methods = ImmutableArray.CreateBuilder<(MethodDeclarationSyntax Syntax, IMethodSymbol Symbol, int Index, Diagnostic? Diagnostic)>();
                 int methodVtableOffset = interfaceContext.MethodStartIndex;
@@ -120,9 +138,35 @@ namespace Microsoft.Interop
                         }
                     }
                 }
-                return methods.ToImmutable();
+                return (Interface: interfaceContext, Methods: methods.ToImmutable());
             });
 
+            var interfaceWithMethodsContexts = interfacesWithMethods
+                .Where(data => data.Methods.Length > 0)
+                .Select(static (data, ct) => data.Interface);
+
+            // Marker interfaces are COM interfaces that don't have any methods.
+            // The lack of methods breaks the mechanism we use later to stitch back together interface-level data
+            // and method-level data, but that's okay because marker interfaces are much simpler.
+            // We'll handle them seperately because they are so simple.
+            var markerInterfaces = interfacesWithMethods
+                .Where(data => data.Methods.Length == 0)
+                .Select(static (data, ct) => data.Interface);
+
+            var markerInterfaceIUnknownDerived = markerInterfaces.Select(static (context, ct) => GenerateIUnknownDerivedAttributeApplication(context))
+                .WithComparer(SyntaxEquivalentComparer.Instance)
+                .SelectNormalized();
+
+            context.RegisterSourceOutput(markerInterfaces.Zip(markerInterfaceIUnknownDerived), (context, data) =>
+            {
+                var (interfaceContext, iUnknownDerivedAttributeApplication) = data;
+                context.AddSource(
+                    interfaceContext.InterfaceType.FullTypeName.Replace("global::", ""),
+                    GenerateMarkerInterfaceSource(interfaceContext) + iUnknownDerivedAttributeApplication);
+            });
+
+            var methodsWithDiagnostics = interfacesWithMethods.SelectMany(static (data, ct) => data.Methods);
+
             // Split the methods we want to generate and the ones we don't into two separate groups.
             var methodsToGenerate = methodsWithDiagnostics.Where(static data => data.Diagnostic is null);
             var invalidMethodDiagnostics = methodsWithDiagnostics.Where(static data => data.Diagnostic is not null);
@@ -149,55 +193,159 @@ namespace Microsoft.Interop
                 .WithTrackingName(StepNames.CalculateStubInformation);
 
             // Generate the code for the managed-to-unmanaged stubs and the diagnostics from code-generation.
-            IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray<Diagnostic>)> generateManagedToNativeStub = generateStubInformation
-                .Where(data => data.VtableIndexData.Direction is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional)
+            var generateManagedToNativeStub = generateStubInformation
                 .Select(
-                    static (data, ct) => VtableIndexStubGenerator.GenerateManagedToNativeStub(data)
+                    static (data, ct) =>
+                    {
+                        if (data.VtableIndexData.Direction is not (MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional))
+                        {
+                            return (GeneratedMethodContextBase)new SkippedStubContext(data.OriginalDefiningType);
+                        }
+                        var (methodStub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateManagedToNativeStub(data);
+                        return new GeneratedStubCodeContext(data.TypeKeyOwner, data.ContainingSyntaxContext, new(methodStub), new(diagnostics));
+                    }
                 )
-                .WithComparer(Comparers.GeneratedSyntax)
                 .WithTrackingName(StepNames.GenerateManagedToNativeStub);
 
-            context.RegisterDiagnostics(generateManagedToNativeStub.SelectMany((stubInfo, ct) => stubInfo.Item2));
+            context.RegisterDiagnostics(generateManagedToNativeStub.SelectMany((stubInfo, ct) => stubInfo.Diagnostics.Array));
 
-            context.RegisterConcatenatedSyntaxOutputs(generateManagedToNativeStub.Select((data, ct) => data.Item1), "ManagedToNativeStubs.g.cs");
+            var managedToNativeInterfaceImplementations = generateManagedToNativeStub
+                .Collect()
+                .SelectMany(static (stubs, ct) => GroupContextsForInterfaceGeneration(stubs))
+                .Select(static (interfaceGroup, ct) => GenerateImplementationInterface(interfaceGroup.Array))
+                .WithTrackingName(StepNames.GenerateManagedToNativeInterfaceImplementation)
+                .WithComparer(SyntaxEquivalentComparer.Instance)
+                .SelectNormalized();
 
             // Filter the list of all stubs to only the stubs that requested unmanaged-to-managed stub generation.
             IncrementalValuesProvider<IncrementalMethodStubGenerationContext> nativeToManagedStubContexts =
                 generateStubInformation
-                .Where(data => data.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional);
+                .Where(static data => data.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional);
 
             // Generate the code for the unmanaged-to-managed stubs and the diagnostics from code-generation.
-            IncrementalValuesProvider<(MemberDeclarationSyntax, ImmutableArray<Diagnostic>)> generateNativeToManagedStub = nativeToManagedStubContexts
+            var generateNativeToManagedStub = generateStubInformation
                 .Select(
-                    static (data, ct) => VtableIndexStubGenerator.GenerateNativeToManagedStub(data)
+                    static (data, ct) =>
+                    {
+                        if (data.VtableIndexData.Direction is not (MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional))
+                        {
+                            return (GeneratedMethodContextBase)new SkippedStubContext(data.OriginalDefiningType);
+                        }
+                        var (methodStub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateNativeToManagedStub(data);
+                        return new GeneratedStubCodeContext(data.OriginalDefiningType, data.ContainingSyntaxContext, new(methodStub), new(diagnostics));
+                    }
                 )
-                .WithComparer(Comparers.GeneratedSyntax)
                 .WithTrackingName(StepNames.GenerateNativeToManagedStub);
 
-            context.RegisterDiagnostics(generateNativeToManagedStub.SelectMany((stubInfo, ct) => stubInfo.Item2));
-
-            context.RegisterConcatenatedSyntaxOutputs(generateNativeToManagedStub.Select((data, ct) => data.Item1), "NativeToManagedStubs.g.cs");
+            context.RegisterDiagnostics(generateNativeToManagedStub.SelectMany((stubInfo, ct) => stubInfo.Diagnostics.Array));
 
-            // Generate the native interface metadata for each interface that contains a method with the [VirtualMethodIndex] attribute.
-            IncrementalValuesProvider<MemberDeclarationSyntax> generateNativeInterface = generateStubInformation
-                .Select(static (context, ct) => context.ContainingSyntaxContext)
+            var nativeToManagedVtableMethods = generateNativeToManagedStub
                 .Collect()
-                .SelectMany(static (syntaxContexts, ct) => syntaxContexts.Distinct())
-                .Select(static (context, ct) => GenerateNativeInterfaceMetadata(context));
+                .SelectMany(static (stubs, ct) => GroupContextsForInterfaceGeneration(stubs))
+                .Select(static (interfaceGroup, ct) => GenerateImplementationVTableMethods(interfaceGroup.Array))
+                .WithTrackingName(StepNames.GenerateNativeToManagedVTableMethods)
+                .WithComparer(SyntaxEquivalentComparer.Instance)
+                .SelectNormalized();
+
+            // Generate the native interface metadata for each [GeneratedComInterface]-attributed interface.
+            var nativeInterfaceInformation = interfaceWithMethodsContexts
+                .Select(static (context, ct) => GenerateInterfaceInformation(context))
+                .WithTrackingName(StepNames.GenerateInterfaceInformation)
+                .WithComparer(SyntaxEquivalentComparer.Instance)
+                .SelectNormalized();
+
+            // Generate a method named CreateManagedVirtualFunctionTable on the native interface implementation
+            // that allocates and fills in the memory for the vtable.
+            var nativeToManagedVtables =
+                generateStubInformation
+                .Collect()
+                .SelectMany(static (data, ct) => GroupContextsForInterfaceGeneration(data.CastArray<GeneratedMethodContextBase>()))
+                .Select(static (vtable, ct) => GenerateImplementationVTable(ImmutableArray.CreateRange(vtable.Array.Cast<IncrementalMethodStubGenerationContext>())))
+                .WithTrackingName(StepNames.GenerateNativeToManagedVTable)
+                .WithComparer(SyntaxEquivalentComparer.Instance)
+                .SelectNormalized();
+
+            var iUnknownDerivedAttributeApplication = interfaceWithMethodsContexts
+                .Select(static (context, ct) => GenerateIUnknownDerivedAttributeApplication(context))
+                .WithTrackingName(StepNames.GenerateIUnknownDerivedAttribute)
+                .WithComparer(SyntaxEquivalentComparer.Instance)
+                .SelectNormalized();
+
+            var filesToGenerate = interfaceWithMethodsContexts
+                .Zip(nativeInterfaceInformation)
+                .Zip(managedToNativeInterfaceImplementations)
+                .Zip(nativeToManagedVtableMethods)
+                .Zip(nativeToManagedVtables)
+                .Zip(iUnknownDerivedAttributeApplication)
+                .Select(static (data, ct) =>
+                {
+                    var (((((interfaceContext, interfaceInfo), managedToNativeStubs), nativeToManagedStubs), nativeToManagedVtable), iUnknownDerivedAttribute) = data;
+
+                    using StringWriter source = new();
+                    interfaceInfo.WriteTo(source);
+                    // Two newlines looks cleaner than one
+                    source.WriteLine();
+                    source.WriteLine();
+                    // TODO: Merge the three InterfaceImplementation partials? We have them all right here.
+                    managedToNativeStubs.WriteTo(source);
+                    source.WriteLine();
+                    source.WriteLine();
+                    nativeToManagedStubs.WriteTo(source);
+                    source.WriteLine();
+                    source.WriteLine();
+                    nativeToManagedVtable.WriteTo(source);
+                    source.WriteLine();
+                    source.WriteLine();
+                    iUnknownDerivedAttribute.WriteTo(source);
+                    return new { TypeName = interfaceContext.InterfaceType.FullTypeName, Source = source.ToString() };
+                });
+
+            context.RegisterSourceOutput(filesToGenerate, (context, data) =>
+            {
+                context.AddSource(data.TypeName.Replace("global::", ""), data.Source);
+            });
+        }
 
-            context.RegisterConcatenatedSyntaxOutputs(generateNativeInterface, "NativeInterfaces.g.cs");
+        private static string GenerateMarkerInterfaceSource(ComInterfaceContext iface) => $$"""
+            file unsafe class InterfaceInformation : global::System.Runtime.InteropServices.Marshalling.IIUnknownInterfaceType
+            {
+                public static global::System.Guid Iid => new(new global::System.ReadOnlySpan<byte>(new byte[] { {{string.Join(",", iface.InterfaceId.ToByteArray())}} }));
 
-            // Generate a method named PopulateUnmanagedVirtualMethodTable on the native interface implementation
-            // that fills in a span with the addresses of the unmanaged-to-managed stub functions at their correct
-            // indices.
-            IncrementalValuesProvider<MemberDeclarationSyntax> populateVTable =
-                nativeToManagedStubContexts
-                .Collect()
-                .SelectMany(static (data, ct) => data.GroupBy(stub => stub.ContainingSyntaxContext))
-                .Select(static (vtable, ct) => GeneratePopulateVTableMethod(vtable));
+                private static void** m_vtable;
 
-            context.RegisterConcatenatedSyntaxOutputs(populateVTable, "PopulateVTable.g.cs");
-        }
+                public static void* VirtualMethodTableManagedImplementation
+                {
+                    get
+                    {
+                        if (m_vtable == null)
+                        {
+                            nint* vtable = (nint*)global::System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(typeof({{iface.InterfaceType.FullTypeName}}), sizeof(nint) * 3);
+                            global::System.Runtime.InteropServices.ComWrappers.GetIUnknownImpl(out vtable[0], out vtable[1], out vtable[2]);
+                            m_vtable = (void**)vtable;
+                        }
+                        return m_vtable;
+                    }
+                }
+            }
+
+            [global::System.Runtime.InteropServices.DynamicInterfaceCastableImplementation]
+            file interface InterfaceImplementation : {{iface.InterfaceType.FullTypeName}}
+            {}
+            """;
+
+        private static readonly AttributeSyntax s_iUnknownDerivedAttributeTemplate =
+            Attribute(
+                GenericName(TypeNames.IUnknownDerivedAttribute)
+                    .AddTypeArgumentListArguments(
+                        IdentifierName("InterfaceInformation"),
+                        IdentifierName("InterfaceImplementation")));
+
+        private static MemberDeclarationSyntax GenerateIUnknownDerivedAttributeApplication(ComInterfaceContext context)
+            => context.TypeDefinitionContext.WrapMemberInContainingSyntaxWithUnsafeModifier(
+                TypeDeclaration(context.InterfaceTypeSyntax.TypeKind, context.InterfaceTypeSyntax.Identifier)
+                    .WithModifiers(context.InterfaceTypeSyntax.Modifiers)
+                    .WithTypeParameterList(context.InterfaceTypeSyntax.TypeParameters)
+                    .AddAttributeLists(AttributeList(SingletonSeparatedList(s_iUnknownDerivedAttributeTemplate))));
 
         private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, CancellationToken ct)
         {
@@ -287,6 +435,16 @@ namespace Microsoft.Interop
                     return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers, syntax.Identifier.GetLocation(), type.Name, typeDecl.Identifier);
                 }
             }
+            var guidAttr = type.GetAttributes().Where(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_InteropServices_GuidAttribute).SingleOrDefault();
+            var interfaceTypeAttr = type.GetAttributes().Where(attr => attr.AttributeClass.ToDisplayString() == TypeNames.InterfaceTypeAttribute).SingleOrDefault();
+            // Assume interfaceType is IUnknown for now
+            if (interfaceTypeAttr is not null
+                && (guidAttr is null
+                    || guidAttr.ConstructorArguments.SingleOrDefault().Value as string is null))
+            {
+                return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedInterfaceMissingGuidAttribute, syntax.Identifier.GetLocation(), type.ToDisplayString());
+                // Missing Guid
+            }
 
             return null;
         }
@@ -311,47 +469,257 @@ namespace Microsoft.Interop
             return null;
         }
 
-        private static MemberDeclarationSyntax GenerateNativeInterfaceMetadata(ContainingSyntaxContext context)
+        private static ImmutableArray<SequenceEqualImmutableArray<GeneratedMethodContextBase>> GroupContextsForInterfaceGeneration(ImmutableArray<GeneratedMethodContextBase> contexts)
+        {
+            // We can end up with an empty set of contexts here as the compiler will call a SelectMany
+            // after a Collect with no input entries
+            if (contexts.IsEmpty)
+            {
+                return ImmutableArray<SequenceEqualImmutableArray<GeneratedMethodContextBase>>.Empty;
+            }
+
+            ImmutableArray<SequenceEqualImmutableArray<GeneratedMethodContextBase>>.Builder allGroupsBuilder = ImmutableArray.CreateBuilder<SequenceEqualImmutableArray<GeneratedMethodContextBase>>();
+
+            // Due to how the source generator driver processes the input item tables and our limitation that methods on COM interfaces can only be defined in a single partial definition of the type,
+            // we can guarantee that the method contexts are ordered as follows:
+            // - I1.M1
+            // - I1.M2
+            // - I1.M3
+            // - I2.M1
+            // - I2.M2
+            // - I2.M3
+            // - I3.M1
+            // - etc...
+            // This enable us to group our contexts by their containing syntax rather simply.
+            ManagedTypeInfo? lastSeenDefiningType = null;
+            ImmutableArray<GeneratedMethodContextBase>.Builder groupBuilder = ImmutableArray.CreateBuilder<GeneratedMethodContextBase>();
+            foreach (var context in contexts)
+            {
+                if (lastSeenDefiningType is null || lastSeenDefiningType == context.OriginalDefiningType)
+                {
+                    groupBuilder.Add(context);
+                }
+                else
+                {
+                    allGroupsBuilder.Add(new(groupBuilder.ToImmutable()));
+                    groupBuilder.Clear();
+                    groupBuilder.Add(context);
+                }
+                lastSeenDefiningType = context.OriginalDefiningType;
+            }
+
+            allGroupsBuilder.Add(new(groupBuilder.ToImmutable()));
+            return allGroupsBuilder.ToImmutable();
+        }
+
+        private static readonly InterfaceDeclarationSyntax ImplementationInterfaceTemplate = InterfaceDeclaration("InterfaceImplementation")
+                .WithModifiers(TokenList(Token(SyntaxKind.FileKeyword), Token(SyntaxKind.UnsafeKeyword), Token(SyntaxKind.PartialKeyword)));
+        private static InterfaceDeclarationSyntax GenerateImplementationInterface(ImmutableArray<GeneratedMethodContextBase> interfaceGroup)
+        {
+            var definingType = interfaceGroup[0].OriginalDefiningType;
+            return ImplementationInterfaceTemplate
+                .AddBaseListTypes(SimpleBaseType(definingType.Syntax))
+                .WithMembers(List<MemberDeclarationSyntax>(interfaceGroup.OfType<GeneratedStubCodeContext>().Select(context => context.Stub.Node)))
+                .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(ParseName(TypeNames.System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute)))));
+        }
+        private static InterfaceDeclarationSyntax GenerateImplementationVTableMethods(ImmutableArray<GeneratedMethodContextBase> interfaceGroup)
         {
-            return context.WrapMemberInContainingSyntaxWithUnsafeModifier(
-                InterfaceDeclaration("Native")
-                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.PartialKeyword)))
-                .WithBaseList(BaseList(SingletonSeparatedList((BaseTypeSyntax)SimpleBaseType(IdentifierName(context.ContainingSyntax[0].Identifier)))))
-                .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(ParseName(TypeNames.System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute))))));
+            return ImplementationInterfaceTemplate
+                .WithMembers(List<MemberDeclarationSyntax>(interfaceGroup.OfType<GeneratedStubCodeContext>().Select(context => context.Stub.Node)));
         }
 
-        private const string VTableParameterName = "vtable";
-        private static readonly MethodDeclarationSyntax ManagedVirtualMethodTableImplementationSyntaxTemplate =
-            MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)),
-                "PopulateUnmanagedVirtualMethodTable")
-                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword)))
-                .AddParameterListParameters(
-                    Parameter(Identifier(VTableParameterName))
-                    .WithType(GenericName(TypeNames.System_Span).AddTypeArgumentListArguments(IdentifierName("nint"))));
+        private static readonly TypeSyntax VoidStarStarSyntax = PointerType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))));
 
-        private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping<ContainingSyntaxContext, IncrementalMethodStubGenerationContext> vtableMethods)
+        private const string CreateManagedVirtualFunctionTableMethodName = "CreateManagedVirtualFunctionTable";
+
+        private static readonly MethodDeclarationSyntax CreateManagedVirtualFunctionTableMethodTemplate = MethodDeclaration(VoidStarStarSyntax, CreateManagedVirtualFunctionTableMethodName)
+            .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword));
+        private static InterfaceDeclarationSyntax GenerateImplementationVTable(ImmutableArray<IncrementalMethodStubGenerationContext> interfaceMethodStubs)
         {
-            ContainingSyntaxContext containingSyntax = vtableMethods.Key.AddContainingSyntax(NativeTypeContainingSyntax);
-            MethodDeclarationSyntax populateVtableMethod = ManagedVirtualMethodTableImplementationSyntaxTemplate;
+            const string vtableLocalName = "vtable";
+            var interfaceType = interfaceMethodStubs[0].OriginalDefiningType;
+
+            ImmutableArray<IncrementalMethodStubGenerationContext> vtableExposedContexts = interfaceMethodStubs
+                .Where(c => c.VtableIndexData.Direction is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional)
+                .ToImmutableArray();
 
-            foreach (var method in vtableMethods)
+            // If none of the methods are exposed as part of the vtable, then don't emit
+            // a vtable (return null).
+            if (vtableExposedContexts.Length == 0)
             {
-                FunctionPointerTypeSyntax functionPointerType = VtableIndexStubGenerator.GenerateUnmanagedFunctionPointerTypeForMethod(method);
+                return ImplementationInterfaceTemplate
+                .AddMembers(
+                    CreateManagedVirtualFunctionTableMethodTemplate
+                        .WithBody(
+                            Block(
+                                ReturnStatement(LiteralExpression(SyntaxKind.NullLiteralExpression)))));
+            }
 
-                // <vtableParameter>[<index>] = (nint)(<functionPointerType>)&ABI_<methodIdentifier>;
-                populateVtableMethod = populateVtableMethod.AddBodyStatements(
+            // void** vtable = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(<interfaceType>, sizeof(void*) * <interfaceMethodStubs.Array.Length>);
+            var vtableDeclarationStatement =
+                LocalDeclarationStatement(
+                    VariableDeclaration(
+                        VoidStarStarSyntax,
+                        SingletonSeparatedList(
+                            VariableDeclarator(vtableLocalName)
+                            .WithInitializer(
+                                EqualsValueClause(
+                                    CastExpression(VoidStarStarSyntax,
+                                        InvocationExpression(
+                                            MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+                                                ParseTypeName(TypeNames.System_Runtime_CompilerServices_RuntimeHelpers),
+                                                IdentifierName("AllocateTypeAssociatedMemory")))
+                                        .AddArgumentListArguments(
+                                            Argument(TypeOfExpression(interfaceType.Syntax)),
+                                            Argument(
+                                                BinaryExpression(
+                                                    SyntaxKind.MultiplyExpression,
+                                                    SizeOfExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword)))),
+                                                    LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(interfaceMethodStubs.Length)))))))))));
+
+            var fillIUnknownSlots = Block()
+                .AddStatements(
+                    // nint v0, v1, v2;
+                    LocalDeclarationStatement(VariableDeclaration(ParseTypeName("nint"))
+                        .AddVariables(
+                            VariableDeclarator("v0"),
+                            VariableDeclarator("v1"),
+                            VariableDeclarator("v2")
+                        )),
+                    // ComWrappers.GetIUnknownImpl(out v0, out v1, out v2);
                     ExpressionStatement(
-                        AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+                        InvocationExpression(
+                            MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+                                ParseTypeName(TypeNames.System_Runtime_InteropServices_ComWrappers),
+                                IdentifierName("GetIUnknownImpl")))
+                        .AddArgumentListArguments(
+                            Argument(IdentifierName("v0"))
+                                    .WithRefKindKeyword(Token(SyntaxKind.OutKeyword)),
+                            Argument(IdentifierName("v1"))
+                                .WithRefKindKeyword(Token(SyntaxKind.OutKeyword)),
+                            Argument(IdentifierName("v2"))
+                                .WithRefKindKeyword(Token(SyntaxKind.OutKeyword)))),
+                    // m_vtable[0] = (void*)v0;
+                    ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
                             ElementAccessExpression(
-                                IdentifierName(VTableParameterName))
-                            .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))),
-                            CastExpression(IdentifierName("nint"),
-                                CastExpression(functionPointerType,
-                                    PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
-                                        IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}")))))));
-            }
+                                IdentifierName(vtableLocalName),
+                                BracketedArgumentList(
+                                    SingletonSeparatedList(
+                                        Argument(
+                                            LiteralExpression(
+                                                SyntaxKind.NumericLiteralExpression,
+                                                Literal(0)))))),
+                            CastExpression(
+                                PointerType(
+                                    PredefinedType(Token(SyntaxKind.VoidKeyword))),
+                                IdentifierName("v0")))),
+                    // m_vtable[1] = (void*)v1;
+                    ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+                            ElementAccessExpression(
+                                IdentifierName(vtableLocalName),
+                                BracketedArgumentList(
+                                    SingletonSeparatedList(
+                                        Argument(
+                                            LiteralExpression(
+                                                SyntaxKind.NumericLiteralExpression,
+                                                Literal(1)))))),
+                            CastExpression(
+                                PointerType(
+                                    PredefinedType(Token(SyntaxKind.VoidKeyword))),
+                                IdentifierName("v1")))),
+                    // m_vtable[2] = (void*)v2;
+                    ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+                            ElementAccessExpression(
+                                IdentifierName(vtableLocalName),
+                                BracketedArgumentList(
+                                    SingletonSeparatedList(
+                                        Argument(
+                                            LiteralExpression(
+                                                SyntaxKind.NumericLiteralExpression,
+                                                Literal(2)))))),
+                            CastExpression(
+                                PointerType(
+                                    PredefinedType(Token(SyntaxKind.VoidKeyword))),
+                                IdentifierName("v2")))));
+
+            var vtableSlotAssignments = VirtualMethodPointerStubGenerator.GenerateVirtualMethodTableSlotAssignments(interfaceMethodStubs, vtableLocalName);
+
+            return ImplementationInterfaceTemplate
+                .AddMembers(
+                    CreateManagedVirtualFunctionTableMethodTemplate
+                        .WithBody(
+                            Block(
+                                vtableDeclarationStatement,
+                                fillIUnknownSlots,
+                                vtableSlotAssignments,
+                                ReturnStatement(IdentifierName(vtableLocalName)))));
+        }
+
+        private static readonly ClassDeclarationSyntax InterfaceInformationTypeTemplate =
+            ClassDeclaration("InterfaceInformation")
+            .AddModifiers(Token(SyntaxKind.FileKeyword), Token(SyntaxKind.UnsafeKeyword))
+            .AddBaseListTypes(SimpleBaseType(ParseTypeName(TypeNames.IIUnknownInterfaceType)));
+
+        private static ClassDeclarationSyntax GenerateInterfaceInformation(ComInterfaceContext context)
+        {
+            const string vtableFieldName = "_vtable";
+            return InterfaceInformationTypeTemplate
+                .AddMembers(
+                    // public static System.Guid Iid { get; } = new(<embeddedDataBlob>);
+                    PropertyDeclaration(ParseTypeName(TypeNames.System_Guid), "Iid")
+                        .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
+                        .AddAccessorListAccessors(
+                            AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(Token(SyntaxKind.SemicolonToken)))
+                        .WithInitializer(
+                            EqualsValueClause(
+                                ImplicitObjectCreationExpression()
+                                    .AddArgumentListArguments(
+                                        Argument(CreateEmbeddedDataBlobCreationStatement(context.InterfaceId.ToByteArray())))))
+                        .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
+                    // private static void** _vtable;
+                    FieldDeclaration(VariableDeclaration(VoidStarStarSyntax, SingletonSeparatedList(VariableDeclarator(vtableFieldName))))
+                        .AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword)),
+                    // public static void* VirtualMethodTableManagedImplementation => _vtable != null ? _vtable : (_vtable = InterfaceImplementation.CreateManagedVirtualMethodTable());
+                    PropertyDeclaration(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))), "VirtualMethodTableManagedImplementation")
+                        .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
+                        .WithExpressionBody(
+                            ArrowExpressionClause(
+                                ConditionalExpression(
+                                    BinaryExpression(SyntaxKind.EqualsExpression,
+                                        IdentifierName(vtableFieldName),
+                                        LiteralExpression(SyntaxKind.NullLiteralExpression)),
+                                    IdentifierName(vtableFieldName),
+                                    ParenthesizedExpression(
+                                        AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+                                            IdentifierName(vtableFieldName),
+                                            InvocationExpression(
+                                                MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+                                                    IdentifierName("InterfaceImplementation"),
+                                                    IdentifierName(CreateManagedVirtualFunctionTableMethodName))))))))
+                        .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
+                    );
+
+            static ExpressionSyntax CreateEmbeddedDataBlobCreationStatement(ReadOnlySpan<byte> bytes)
+            {
+                var literals = new LiteralExpressionSyntax[bytes.Length];
 
-            return containingSyntax.WrapMemberInContainingSyntaxWithUnsafeModifier(populateVtableMethod);
+                for (int i = 0; i < bytes.Length; i++)
+                {
+                    literals[i] = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(bytes[i]));
+                }
+
+                // new System.ReadOnlySpan<byte>(new[] { <byte literals> } )
+                return ObjectCreationExpression(
+                    GenericName(TypeNames.System_ReadOnlySpan)
+                        .AddTypeArgumentListArguments(PredefinedType(Token(SyntaxKind.ByteKeyword))))
+                    .AddArgumentListArguments(
+                        Argument(
+                            ArrayCreationExpression(
+                                    ArrayType(PredefinedType(Token(SyntaxKind.ByteKeyword)), SingletonList(ArrayRankSpecifier())),
+                                    InitializerExpression(
+                                        SyntaxKind.ArrayInitializerExpression,
+                                        SeparatedList<ExpressionSyntax>(literals)))));
+            }
         }
     }
 }
index c8f55e8..777680d 100644 (file)
@@ -21,6 +21,7 @@ namespace Microsoft.Interop
             public const string TypeNotSupported = Prefix + "1051";
             public const string ConfigurationNotSupported = Prefix + "1052";
             public const string MethodNotDeclaredInAttributedInterface = Prefix + "1091";
+            public const string InvalidGeneratedComInterfaceAttributeUsage = Prefix + "1092";
         }
 
         private const string Category = "ComInterfaceGenerator";
@@ -175,11 +176,21 @@ namespace Microsoft.Interop
                 isEnabledByDefault: true,
                 description: GetResourceString(nameof(SR.MethodNotDeclaredInAttributedInterfaceDescription)));
 
+        public static readonly DiagnosticDescriptor InvalidAttributedInterfaceMissingGuidAttribute =
+            new DiagnosticDescriptor(
+                Ids.InvalidGeneratedComInterfaceAttributeUsage,
+                GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageTitle)),
+                GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute)),
+                Category,
+                DiagnosticSeverity.Error,
+                isEnabledByDefault: true,
+                description: GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageDescription)));
 
         private readonly List<Diagnostic> _diagnostics = new List<Diagnostic>();
 
         public IEnumerable<Diagnostic> Diagnostics => _diagnostics;
 
+
         /// <summary>
         /// Report diagnostic for invalid configuration for string marshalling.
         /// </summary>
index e437df7..94f094a 100644 (file)
@@ -4,9 +4,12 @@
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp.Syntax;
 using System;
+using System.Collections.Immutable;
 
 namespace Microsoft.Interop
 {
+    internal abstract record GeneratedMethodContextBase(ManagedTypeInfo OriginalDefiningType, SequenceEqualImmutableArray<Diagnostic> Diagnostics);
+
     internal sealed record IncrementalMethodStubGenerationContext(
         SignatureContext SignatureContext,
         ContainingSyntaxContext ContainingSyntaxContext,
@@ -19,5 +22,5 @@ namespace Microsoft.Interop
         MarshallingGeneratorFactoryKey<(TargetFramework TargetFramework, Version TargetFrameworkVersion)> UnmanagedToManagedGeneratorFactory,
         ManagedTypeInfo TypeKeyOwner,
         SequenceEqualImmutableArray<Diagnostic> Diagnostics,
-        TypeSyntax UnwrapperSyntax);
+        TypeSyntax UnwrapperSyntax) : GeneratedMethodContextBase(TypeKeyOwner, Diagnostics);
 }
index f240567..870368e 100644 (file)
@@ -30,5 +30,11 @@ namespace Microsoft.Interop
                     return builder.MoveToImmutable();
                 });
         }
+
+        public static IncrementalValuesProvider<TNode> SelectNormalized<TNode>(this IncrementalValuesProvider<TNode> provider)
+            where TNode : SyntaxNode
+        {
+            return provider.Select((node, ct) => node.NormalizeWhitespace());
+        }
     }
 }
index 9748657..4838380 100644 (file)
-<?xml version="1.0" encoding="utf-8"?>
-<root>
-  <!-- 
-    Microsoft ResX Schema 
-    
-    Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
-    associated with the data types.
-    
-    Example:
-    
-    ... ado.net/XML headers & schema ...
-    <resheader name="resmimetype">text/microsoft-resx</resheader>
-    <resheader name="version">2.0</resheader>
-    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
-    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
-    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
-    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
-    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
-        <value>[base64 mime encoded serialized .NET Framework object]</value>
-    </data>
-    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
-        <comment>This is a comment</comment>
-    </data>
-                
-    There are any number of "resheader" rows that contain simple 
-    name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
-    mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
-    extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
-    read any of the formats listed below.
-    
-    mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
-            : and then encoded with base64 encoding.
-    
-    mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
-            : and then encoded with base64 encoding.
-
-    mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
-            : using a System.ComponentModel.TypeConverter
-            : and then encoded with base64 encoding.
-    -->
-  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-    <xsd:element name="root" msdata:IsDataSet="true">
-      <xsd:complexType>
-        <xsd:choice maxOccurs="unbounded">
-          <xsd:element name="metadata">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-              </xsd:sequence>
-              <xsd:attribute name="name" use="required" type="xsd:string" />
-              <xsd:attribute name="type" type="xsd:string" />
-              <xsd:attribute name="mimetype" type="xsd:string" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="assembly">
-            <xsd:complexType>
-              <xsd:attribute name="alias" type="xsd:string" />
-              <xsd:attribute name="name" type="xsd:string" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="data">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="resheader">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" />
-            </xsd:complexType>
-          </xsd:element>
-        </xsd:choice>
-      </xsd:complexType>
-    </xsd:element>
-  </xsd:schema>
-  <resheader name="resmimetype">
-    <value>text/microsoft-resx</value>
-  </resheader>
-  <resheader name="version">
-    <value>2.0</value>
-  </resheader>
-  <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <data name="ConfigurationNotSupportedDescription" xml:space="preserve">
-    <value>Source-generated P/Invokes will ignore any configuration that is not supported.</value>
-  </data>
-  <data name="ConfigurationNotSupportedMessage" xml:space="preserve">
-    <value>The '{0}' configuration is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>
-  </data>
-  <data name="ConfigurationNotSupportedMessageMarshallingInfo" xml:space="preserve">
-    <value>The specified marshalling configuration is not supported by source-generated P/Invokes. {0}.</value>
-  </data>
-  <data name="ConfigurationNotSupportedMessageParameter" xml:space="preserve">
-    <value>The specified '{0}' configuration for parameter '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>
-  </data>
-  <data name="ConfigurationNotSupportedMessageReturn" xml:space="preserve">
-    <value>The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>
-  </data>
-  <data name="ConfigurationNotSupportedMessageValue" xml:space="preserve">
-    <value>The specified value '{0}' for '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>
-  </data>
-  <data name="ConfigurationNotSupportedTitle" xml:space="preserve">
-    <value>Specified configuration is not supported by source-generated P/Invokes.</value>
-  </data>
-  <data name="InvalidStringMarshallingConfigurationDescription" xml:space="preserve">
-    <value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</value>
-  </data>
-  <data name="InvalidStringMarshallingConfigurationMessage" xml:space="preserve">
-    <value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1}</value>
-    <comment>{1} is a message containing additional details about what is not valid</comment>
-  </data>
-  <data name="InvalidStringMarshallingConfigurationMissingCustomType" xml:space="preserve">
-    <value>'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'.</value>
-  </data>
-  <data name="InvalidStringMarshallingConfigurationNotCustom" xml:space="preserve">
-    <value>'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified.</value>
-  </data>
-  <data name="TypeNotSupportedDescription" xml:space="preserve">
-    <value>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</value>
-  </data>
-  <data name="TypeNotSupportedMessageParameter" xml:space="preserve">
-    <value>The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter '{1}'.</value>
-  </data>
-  <data name="TypeNotSupportedMessageParameterWithDetails" xml:space="preserve">
-    <value>{0} The generated source will not handle marshalling of parameter '{1}'.</value>
-    <comment>{0} is a message containing additional details about what is not supported
-{1} is the name of the parameter</comment>
-  </data>
-  <data name="TypeNotSupportedMessageReturn" xml:space="preserve">
-    <value>The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of the return value of method '{1}'.</value>
-  </data>
-  <data name="TypeNotSupportedMessageReturnWithDetails" xml:space="preserve">
-    <value>{0} The generated source will not handle marshalling of the return value of method '{1}'.</value>
-    <comment>{0} is a message containing additional details about what is not supported
-{1} is the name of the method</comment>
-  </data>
-  <data name="TypeNotSupportedTitle" xml:space="preserve">
-    <value>Specified type is not supported by source-generated P/Invokes</value>
-  </data>
-  <data name="InvalidAttributedMethodContainingTypeMissingModifiersMessage" xml:space="preserve">
-    <value>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</value>
-  </data>
-  <data name="InvalidAttributedMethodDescription" xml:space="preserve">
-    <value>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.</value>
-  </data>
-  <data name="InvalidAttributedMethodSignatureMessage" xml:space="preserve">
-    <value>Method '{0}' should be 'static', 'partial', and non-generic when marked with 'LibraryImportAttribute'. P/Invoke source generation will ignore method '{0}'.</value>
-  </data>
-  <data name="InvalidVirtualMethodIndexAttributeUsage" xml:space="preserve">
-    <value>Invalid 'VirtualMethodIndexAttribute' usage</value>
-  </data>
-  <data name="InvalidExceptionMarshallingConfigurationDescription" xml:space="preserve">
-    <value>The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' is invalid.</value>
-  </data>
-  <data name="InvalidExceptionMarshallingConfigurationMessage" xml:space="preserve">
-    <value>The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' on method '{0}' is invalid. {1}</value>
-    <comment>{1} is a message containing additional details about what is not valid</comment>
-  </data>
-  <data name="InvalidExceptionMarshallingConfigurationMissingCustomType" xml:space="preserve">
-    <value>'ExceptionMarshallingCustomType' must be specified when 'ExceptionMarshalling' is set to 'ExceptionMarshalling.Custom'.</value>
-  </data>
-  <data name="InvalidExceptionMarshallingConfigurationNotCustom" xml:space="preserve">
-    <value>'ExceptionMarshalling' should be set to 'ExceptionMarshalling.Custom' when 'ExceptionMarshallingCustomType' is specified.</value>
-  </data>
-  <data name="InvalidExceptionMarshallingValue" xml:space="preserve">
-    <value>The provided value is not a known flag of the 'ExceptionMarshalling' enum.</value>
-  </data>
-  <data name="InterfaceTypeNotSupportedTitle" xml:space="preserve">
-    <value>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</value>
-  </data>
-  <data name="InterfaceTypeNotSupportedMessage" xml:space="preserve">
-    <value>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</value>
-  </data>
-  <data name="InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttributeMessage" xml:space="preserve">
-    <value>Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. </value>
-  </data>
-  <data name="MethodNotDeclaredInAttributedInterfaceDescription" xml:space="preserve">
-    <value>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.</value>
-  </data>
-  <data name="MethodNotDeclaredInAttributedInterfaceMessage" xml:space="preserve">
-    <value>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</value>
-  </data>
-  <data name="MethodNotDeclaredInAttributedInterfaceTitle" xml:space="preserve">
-    <value>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</value>
-  </data>
-</root>
+<?xml version="1.0" encoding="utf-8"?>\r
+<root>\r
+  <!-- \r
+    Microsoft ResX Schema \r
+    \r
+    Version 2.0\r
+    \r
+    The primary goals of this format is to allow a simple XML format \r
+    that is mostly human readable. The generation and parsing of the \r
+    various data types are done through the TypeConverter classes \r
+    associated with the data types.\r
+    \r
+    Example:\r
+    \r
+    ... ado.net/XML headers & schema ...\r
+    <resheader name="resmimetype">text/microsoft-resx</resheader>\r
+    <resheader name="version">2.0</resheader>\r
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>\r
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>\r
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">\r
+        <value>[base64 mime encoded serialized .NET Framework object]</value>\r
+    </data>\r
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">\r
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r
+        <comment>This is a comment</comment>\r
+    </data>\r
+                \r
+    There are any number of "resheader" rows that contain simple \r
+    name/value pairs.\r
+    \r
+    Each data row contains a name, and value. The row also contains a \r
+    type or mimetype. Type corresponds to a .NET class that support \r
+    text/value conversion through the TypeConverter architecture. \r
+    Classes that don't support this are serialized and stored with the \r
+    mimetype set.\r
+    \r
+    The mimetype is used for serialized objects, and tells the \r
+    ResXResourceReader how to depersist the object. This is currently not \r
+    extensible. For a given mimetype the value must be set accordingly:\r
+    \r
+    Note - application/x-microsoft.net.object.binary.base64 is the format \r
+    that the ResXResourceWriter will generate, however the reader can \r
+    read any of the formats listed below.\r
+    \r
+    mimetype: application/x-microsoft.net.object.binary.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r
+            : and then encoded with base64 encoding.\r
+    \r
+    mimetype: application/x-microsoft.net.object.soap.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r
+            : and then encoded with base64 encoding.\r
+\r
+    mimetype: application/x-microsoft.net.object.bytearray.base64\r
+    value   : The object must be serialized into a byte array \r
+            : using a System.ComponentModel.TypeConverter\r
+            : and then encoded with base64 encoding.\r
+    -->\r
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">\r
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />\r
+    <xsd:element name="root" msdata:IsDataSet="true">\r
+      <xsd:complexType>\r
+        <xsd:choice maxOccurs="unbounded">\r
+          <xsd:element name="metadata">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" use="required" type="xsd:string" />\r
+              <xsd:attribute name="type" type="xsd:string" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="assembly">\r
+            <xsd:complexType>\r
+              <xsd:attribute name="alias" type="xsd:string" />\r
+              <xsd:attribute name="name" type="xsd:string" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="data">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />\r
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="resheader">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+        </xsd:choice>\r
+      </xsd:complexType>\r
+    </xsd:element>\r
+  </xsd:schema>\r
+  <resheader name="resmimetype">\r
+    <value>text/microsoft-resx</value>\r
+  </resheader>\r
+  <resheader name="version">\r
+    <value>2.0</value>\r
+  </resheader>\r
+  <resheader name="reader">\r
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <resheader name="writer">\r
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <data name="ConfigurationNotSupportedDescription" xml:space="preserve">\r
+    <value>Source-generated P/Invokes will ignore any configuration that is not supported.</value>\r
+  </data>\r
+  <data name="ConfigurationNotSupportedMessage" xml:space="preserve">\r
+    <value>The '{0}' configuration is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>\r
+  </data>\r
+  <data name="ConfigurationNotSupportedMessageMarshallingInfo" xml:space="preserve">\r
+    <value>The specified marshalling configuration is not supported by source-generated P/Invokes. {0}.</value>\r
+  </data>\r
+  <data name="ConfigurationNotSupportedMessageParameter" xml:space="preserve">\r
+    <value>The specified '{0}' configuration for parameter '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>\r
+  </data>\r
+  <data name="ConfigurationNotSupportedMessageReturn" xml:space="preserve">\r
+    <value>The specified '{0}' configuration for the return value of method '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>\r
+  </data>\r
+  <data name="ConfigurationNotSupportedMessageValue" xml:space="preserve">\r
+    <value>The specified value '{0}' for '{1}' is not supported by source-generated P/Invokes. If the specified configuration is required, use a regular `DllImport` instead.</value>\r
+  </data>\r
+  <data name="ConfigurationNotSupportedTitle" xml:space="preserve">\r
+    <value>Specified configuration is not supported by source-generated P/Invokes.</value>\r
+  </data>\r
+  <data name="InvalidStringMarshallingConfigurationDescription" xml:space="preserve">\r
+    <value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</value>\r
+  </data>\r
+  <data name="InvalidStringMarshallingConfigurationMessage" xml:space="preserve">\r
+    <value>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' on method '{0}' is invalid. {1}</value>\r
+    <comment>{1} is a message containing additional details about what is not valid</comment>\r
+  </data>\r
+  <data name="InvalidStringMarshallingConfigurationMissingCustomType" xml:space="preserve">\r
+    <value>'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'.</value>\r
+  </data>\r
+  <data name="InvalidStringMarshallingConfigurationNotCustom" xml:space="preserve">\r
+    <value>'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified.</value>\r
+  </data>\r
+  <data name="TypeNotSupportedDescription" xml:space="preserve">\r
+    <value>For types that are not supported by source-generated P/Invokes, the resulting P/Invoke will rely on the underlying runtime to marshal the specified type.</value>\r
+  </data>\r
+  <data name="TypeNotSupportedMessageParameter" xml:space="preserve">\r
+    <value>The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter '{1}'.</value>\r
+  </data>\r
+  <data name="TypeNotSupportedMessageParameterWithDetails" xml:space="preserve">\r
+    <value>{0} The generated source will not handle marshalling of parameter '{1}'.</value>\r
+    <comment>{0} is a message containing additional details about what is not supported\r
+{1} is the name of the parameter</comment>\r
+  </data>\r
+  <data name="TypeNotSupportedMessageReturn" xml:space="preserve">\r
+    <value>The type '{0}' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of the return value of method '{1}'.</value>\r
+  </data>\r
+  <data name="TypeNotSupportedMessageReturnWithDetails" xml:space="preserve">\r
+    <value>{0} The generated source will not handle marshalling of the return value of method '{1}'.</value>\r
+    <comment>{0} is a message containing additional details about what is not supported\r
+{1} is the name of the method</comment>\r
+  </data>\r
+  <data name="TypeNotSupportedTitle" xml:space="preserve">\r
+    <value>Specified type is not supported by source-generated P/Invokes</value>\r
+  </data>\r
+  <data name="InvalidAttributedMethodContainingTypeMissingModifiersMessage" xml:space="preserve">\r
+    <value>Method '{0}' is contained in a type '{1}' that is not marked 'partial'. P/Invoke source generation will ignore method '{0}'.</value>\r
+  </data>\r
+  <data name="InvalidAttributedMethodDescription" xml:space="preserve">\r
+    <value>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.</value>\r
+  </data>\r
+  <data name="InvalidAttributedMethodSignatureMessage" xml:space="preserve">\r
+    <value>Method '{0}' should be 'static', 'partial', and non-generic when marked with 'LibraryImportAttribute'. P/Invoke source generation will ignore method '{0}'.</value>\r
+  </data>\r
+  <data name="InvalidVirtualMethodIndexAttributeUsage" xml:space="preserve">\r
+    <value>Invalid 'VirtualMethodIndexAttribute' usage</value>\r
+  </data>\r
+  <data name="InvalidExceptionMarshallingConfigurationDescription" xml:space="preserve">\r
+    <value>The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' is invalid.</value>\r
+  </data>\r
+  <data name="InvalidExceptionMarshallingConfigurationMessage" xml:space="preserve">\r
+    <value>The configuration of 'ExceptionMarshalling' and 'ExceptionMarshallingCustomType' on method '{0}' is invalid. {1}</value>\r
+    <comment>{1} is a message containing additional details about what is not valid</comment>\r
+  </data>\r
+  <data name="InvalidExceptionMarshallingConfigurationMissingCustomType" xml:space="preserve">\r
+    <value>'ExceptionMarshallingCustomType' must be specified when 'ExceptionMarshalling' is set to 'ExceptionMarshalling.Custom'.</value>\r
+  </data>\r
+  <data name="InvalidExceptionMarshallingConfigurationNotCustom" xml:space="preserve">\r
+    <value>'ExceptionMarshalling' should be set to 'ExceptionMarshalling.Custom' when 'ExceptionMarshallingCustomType' is specified.</value>\r
+  </data>\r
+  <data name="InvalidExceptionMarshallingValue" xml:space="preserve">\r
+    <value>The provided value is not a known flag of the 'ExceptionMarshalling' enum.</value>\r
+  </data>\r
+  <data name="InterfaceTypeNotSupportedTitle" xml:space="preserve">\r
+    <value>'GeneratedComInterfaceType' does not support the 'ComInterfaceType' value supplied to 'InterfaceTypeAttribute' on the same type.</value>\r
+  </data>\r
+  <data name="InterfaceTypeNotSupportedMessage" xml:space="preserve">\r
+    <value>Using 'GeneratedComInterfaceAttribute' and 'InterfaceTypeAttribute' is not supported with 'ComInterfaceType' value '{0}'.</value>\r
+  </data>\r
+  <data name="InvalidAttributedMethodContainingTypeMissingUnmanagedObjectUnwrapperAttributeMessage" xml:space="preserve">\r
+    <value>Containing type of method with VirtualMethodIndexAttribute does not have a UnmanagedObjectUnwrapperAttribute. </value>\r
+  </data>\r
+  <data name="MethodNotDeclaredInAttributedInterfaceDescription" xml:space="preserve">\r
+    <value>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.</value>\r
+  </data>\r
+  <data name="MethodNotDeclaredInAttributedInterfaceMessage" xml:space="preserve">\r
+    <value>The method '{0}' is declared on a different partial definition of the interface '{1}' than the definition that has the 'GeneratedComInterface' attribute</value>\r
+  </data>\r
+  <data name="MethodNotDeclaredInAttributedInterfaceTitle" xml:space="preserve">\r
+    <value>Method is declared in different partial declaration than the 'GeneratedComInterface' attribute.</value>\r
+  </data>\r
+  <data name="InvalidGeneratedComInterfaceAttributeUsageDescription" xml:space="preserve">\r
+    <value>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</value>\r
+  </data>\r
+  <data name="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute" xml:space="preserve">\r
+    <value>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</value>\r
+  </data>\r
+  <data name="InvalidGeneratedComInterfaceAttributeUsageTitle" xml:space="preserve">\r
+    <value>Invalid 'GeneratedComInterfaceAttribute' usage.</value>\r
+  </data>\r
+</root>\r
\ No newline at end of file
index 2500fbf..e09326f 100644 (file)
         <target state="translated">Zadaná hodnota není známý příznak výčtu ExceptionMarsnuming.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">Konfigurace StringMarshalling a StringMarshallingCustomType je neplatná.</target>
index a18b8e2..2d3cb6f 100644 (file)
         <target state="translated">Der angegebene Wert ist kein bekanntes Flag der „ExceptionMarshalling“-Enumeration.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">Die Konfiguration von \"StringMarshalling\" und \"StringMarshallingCustomType\" ist ungültig.</target>
index 0f24506..8c3e75e 100644 (file)
         <target state="translated">El valor proporcionado no es una marca conocida de la enumeración “ExceptionMarshalling”.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">La configuración de “StringMarshalling” y “StringMarshallingCustomType” no es válida.</target>
index c732bf0..b784a2c 100644 (file)
         <target state="translated">La valeur fournie n’est pas un indicateur connu de l’énumération « ExceptionMarshalling ».</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">La configuration de « StringMarshalling » et de « StringMarshallingCustomType » n’est pas valide.</target>
index b8204ea..2a05223 100644 (file)
         <target state="translated">Il valore specificato non è un flag noto dell'enumerazione 'ExceptionMarshalling'.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">La configurazione di 'StringMarshalling' e 'StringMarshallingCustomType' non è valida.</target>
index 57e2961..410641b 100644 (file)
         <target state="translated">指定された値は、'ExceptionMarshalling' 列挙型の既知のフラグではありません。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">'StringMarshalling' と 'StringMarshallingCustomType' の構成が無効です。</target>
index 9d14554..cc21fc0 100644 (file)
         <target state="translated">제공된 값은 'ExceptionMarshalling' 열거형의 알려진 플래그가 아닙니다.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">'StringMarshalling' 및 'StringMarshallingCustomType'의 구성이 잘못되었습니다.</target>
index f11c3fe..01369f4 100644 (file)
         <target state="translated">Podana wartość nie jest znaną flagą wyliczenia „'ExceptionMarshalling”.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">Konfiguracja elementów „StringMarshalling” i „StringMarshallingCustomType” jest nieprawidłowa.</target>
index 7740470..6863fce 100644 (file)
         <target state="translated">O valor fornecido não é um sinalizador conhecido da enumeração 'ExceptionMarshalling'.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">A configuração de 'StringMarshalling' e 'StringMarshallingCustomType' é inválida.</target>
index 8ca45f5..0cca0a0 100644 (file)
         <target state="translated">Указанное значение не является известным флагом перечисления ExceptionMarshalling.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">Конфигурация \"StringMarshalling\" и \"StringMarshallingCustomType\" недопустима.</target>
index 7483677..8516282 100644 (file)
         <target state="translated">Sağlanan değer bilinen bir 'ExceptionMarshalling' sabit listesi bayrağı değil.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">'StringMarshalling' ve 'StringMarshallingCustomType' yapılandırması geçersiz.</target>
index 48f7ef0..9d7ba65 100644 (file)
         <target state="translated">提供的值不是 “ExceptionMarshalling” 枚举的已知标志。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">“StringMarshalling” 和 “StringMarshallingCustomType” 的配置无效。</target>
index 9e6a835..8581994 100644 (file)
         <target state="translated">提供的值不是 'ExceptionMarshalling' 列舉的已知旗標。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageDescription">
+        <source>Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interfaces attributed with 'GeneratedComInterfaceAttribute' must be partial and must specify a GUID with 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageMissingGuidAttribute">
+        <source>Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</source>
+        <target state="new">Interface '{0}' is attributed with 'GeneratedComInterfaceAttribute' but is missing 'System.Runtime.InteropServices.GuidAttribute'.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InvalidGeneratedComInterfaceAttributeUsageTitle">
+        <source>Invalid 'GeneratedComInterfaceAttribute' usage.</source>
+        <target state="new">Invalid 'GeneratedComInterfaceAttribute' usage.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="InvalidStringMarshallingConfigurationDescription">
         <source>The configuration of 'StringMarshalling' and 'StringMarshallingCustomType' is invalid.</source>
         <target state="translated">'StringMarshalling' 和 'StringMarshallingCustomType' 的設定無效。</target>
diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VirtualMethodPointerStubGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/VirtualMethodPointerStubGenerator.cs
new file mode 100644 (file)
index 0000000..c878616
--- /dev/null
@@ -0,0 +1,200 @@
+// 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.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+using Microsoft.CodeAnalysis;
+using System.Diagnostics;
+
+namespace Microsoft.Interop
+{
+    internal static class VirtualMethodPointerStubGenerator
+    {
+        public static (MethodDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateManagedToNativeStub(
+            IncrementalMethodStubGenerationContext methodStub)
+        {
+            var diagnostics = new GeneratorDiagnostics();
+
+            // Generate stub code
+            var stubGenerator = new ManagedToNativeVTableMethodGenerator(
+                methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFramework,
+                methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFrameworkVersion,
+                methodStub.SignatureContext.ElementTypeInformation,
+                methodStub.VtableIndexData.SetLastError,
+                methodStub.VtableIndexData.ImplicitThisParameter,
+                (elementInfo, ex) =>
+                {
+                    diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails);
+                },
+                methodStub.ManagedToUnmanagedGeneratorFactory.GeneratorFactory);
+
+            BlockSyntax code = stubGenerator.GenerateStubBody(
+                methodStub.VtableIndexData.Index,
+                methodStub.CallingConvention.Array,
+                methodStub.TypeKeyOwner.Syntax);
+
+            // The owner type will always be an interface type, so the syntax will always be a NameSyntax as it's the name of a named type
+            // with no additional decorators.
+            Debug.Assert(methodStub.TypeKeyOwner.Syntax is NameSyntax);
+
+            return (
+                PrintGeneratedSource(
+                    methodStub.StubMethodSyntaxTemplate,
+                    methodStub.SignatureContext,
+                    code)
+                    .WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifier((NameSyntax)methodStub.TypeKeyOwner.Syntax)),
+                methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
+        }
+        private static MethodDeclarationSyntax PrintGeneratedSource(
+            ContainingSyntax stubMethodSyntax,
+            SignatureContext stub,
+            BlockSyntax stubCode)
+        {
+            // Create stub function
+            return MethodDeclaration(stub.StubReturnType, stubMethodSyntax.Identifier)
+                .AddAttributeLists(stub.AdditionalAttributes.ToArray())
+                .WithModifiers(stubMethodSyntax.Modifiers.StripTriviaFromTokens())
+                .WithParameterList(ParameterList(SeparatedList(stub.StubParameters)))
+                .WithBody(stubCode);
+        }
+
+        private const string ThisParameterIdentifier = "@this";
+
+        public static (MethodDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateNativeToManagedStub(
+            IncrementalMethodStubGenerationContext methodStub)
+        {
+            var diagnostics = new GeneratorDiagnostics();
+            ImmutableArray<TypePositionInfo> elements = AddImplicitElementInfos(methodStub);
+
+            // Generate stub code
+            var stubGenerator = new UnmanagedToManagedStubGenerator(
+                methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFramework,
+                methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion,
+                elements,
+                (elementInfo, ex) =>
+                {
+                    diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails);
+                },
+                methodStub.UnmanagedToManagedGeneratorFactory.GeneratorFactory);
+
+            BlockSyntax code = stubGenerator.GenerateStubBody(
+                MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+                    IdentifierName(ThisParameterIdentifier),
+                    IdentifierName(methodStub.StubMethodSyntaxTemplate.Identifier)));
+
+            (ParameterListSyntax unmanagedParameterList, TypeSyntax returnType, _) = stubGenerator.GenerateAbiMethodSignatureData();
+
+            AttributeSyntax unmanagedCallersOnlyAttribute = Attribute(
+                ParseName(TypeNames.UnmanagedCallersOnlyAttribute));
+
+            if (methodStub.CallingConvention.Array.Length != 0)
+            {
+                unmanagedCallersOnlyAttribute = unmanagedCallersOnlyAttribute.AddArgumentListArguments(
+                    AttributeArgument(
+                        ImplicitArrayCreationExpression(
+                            InitializerExpression(SyntaxKind.CollectionInitializerExpression,
+                                SeparatedList<ExpressionSyntax>(
+                                    methodStub.CallingConvention.Array.Select(callConv => TypeOfExpression(ParseName($"System.Runtime.CompilerServices.CallConv{callConv.Name.ValueText}")))))))
+                    .WithNameEquals(NameEquals(IdentifierName("CallConvs"))));
+            }
+
+            MethodDeclarationSyntax unmanagedToManagedStub =
+                MethodDeclaration(returnType, $"ABI_{methodStub.StubMethodSyntaxTemplate.Identifier.Text}")
+                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword)))
+                .WithParameterList(unmanagedParameterList)
+                .AddAttributeLists(AttributeList(SingletonSeparatedList(unmanagedCallersOnlyAttribute)))
+                .WithBody(code);
+
+            return (
+                unmanagedToManagedStub,
+                methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
+        }
+        private static ImmutableArray<TypePositionInfo> AddImplicitElementInfos(IncrementalMethodStubGenerationContext methodStub)
+        {
+            ImmutableArray<TypePositionInfo> originalElements = methodStub.SignatureContext.ElementTypeInformation;
+
+            var elements = ImmutableArray.CreateBuilder<TypePositionInfo>(originalElements.Length + 2);
+
+            elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, new NativeThisInfo(methodStub.UnwrapperSyntax))
+            {
+                InstanceIdentifier = ThisParameterIdentifier,
+                NativeIndex = 0,
+            });
+            foreach (TypePositionInfo element in originalElements)
+            {
+                elements.Add(element with
+                {
+                    NativeIndex = TypePositionInfo.IncrementIndex(element.NativeIndex)
+                });
+            }
+
+            if (methodStub.ExceptionMarshallingInfo != NoMarshallingInfo.Instance)
+            {
+                elements.Add(
+                    new TypePositionInfo(
+                        new ReferenceTypeInfo($"global::{TypeNames.System_Exception}", TypeNames.System_Exception),
+                        methodStub.ExceptionMarshallingInfo)
+                    {
+                        InstanceIdentifier = "__exception",
+                        ManagedIndex = TypePositionInfo.ExceptionIndex,
+                        NativeIndex = TypePositionInfo.ReturnIndex
+                    });
+            }
+
+            return elements.ToImmutable();
+        }
+
+        public static BlockSyntax GenerateVirtualMethodTableSlotAssignments(IEnumerable<IncrementalMethodStubGenerationContext> vtableMethods, string vtableIdentifier)
+        {
+            List<StatementSyntax> statements = new();
+            foreach (var method in vtableMethods)
+            {
+                FunctionPointerTypeSyntax functionPointerType = GenerateUnmanagedFunctionPointerTypeForMethod(method);
+
+                // <vtableParameter>[<index>] = (void*)(<functionPointerType>)&ABI_<methodIdentifier>;
+                statements.Add(
+                    ExpressionStatement(
+                        AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+                            ElementAccessExpression(
+                                IdentifierName(vtableIdentifier))
+                            .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))),
+                            CastExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))),
+                                CastExpression(functionPointerType,
+                                    PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
+                                        IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}")))))));
+            }
+
+            return Block(statements);
+        }
+
+        private static FunctionPointerTypeSyntax GenerateUnmanagedFunctionPointerTypeForMethod(IncrementalMethodStubGenerationContext method)
+        {
+            var stubGenerator = new UnmanagedToManagedStubGenerator(
+                method.UnmanagedToManagedGeneratorFactory.Key.TargetFramework,
+                method.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion,
+                AddImplicitElementInfos(method),
+                // Swallow diagnostics here since the diagnostics will be reported by the unmanaged->managed stub generation
+                (elementInfo, ex) => { },
+                method.UnmanagedToManagedGeneratorFactory.GeneratorFactory);
+
+            List<FunctionPointerParameterSyntax> functionPointerParameters = new();
+            var (paramList, retType, _) = stubGenerator.GenerateAbiMethodSignatureData();
+            functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type)));
+            // We add the return type as the last "parameter" here as that's what the function pointer syntax requires.
+            functionPointerParameters.Add(FunctionPointerParameter(retType));
+
+            // delegate* unmanaged<...>
+            ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> callConv = method.CallingConvention.Array;
+            FunctionPointerTypeSyntax functionPointerType = FunctionPointerType(
+                    FunctionPointerCallingConvention(Token(SyntaxKind.UnmanagedKeyword), callConv.IsEmpty ? null : FunctionPointerUnmanagedCallingConventionList(SeparatedList(callConv))),
+                    FunctionPointerParameterList(SeparatedList(functionPointerParameters)));
+            return functionPointerType;
+        }
+    }
+}
index 9ff0512..dda8473 100644 (file)
@@ -155,30 +155,6 @@ namespace Microsoft.Interop
             }
             return callingConventions.ToImmutable();
         }
-
-        private static SyntaxTokenList StripTriviaFromModifiers(SyntaxTokenList tokenList)
-        {
-            SyntaxToken[] strippedTokens = new SyntaxToken[tokenList.Count];
-            for (int i = 0; i < tokenList.Count; i++)
-            {
-                strippedTokens[i] = tokenList[i].WithoutTrivia();
-            }
-            return new SyntaxTokenList(strippedTokens);
-        }
-
-        private static MethodDeclarationSyntax PrintGeneratedSource(
-            ContainingSyntax stubMethodSyntax,
-            SignatureContext stub,
-            BlockSyntax stubCode)
-        {
-            // Create stub function
-            return MethodDeclaration(stub.StubReturnType, stubMethodSyntax.Identifier)
-                .AddAttributeLists(stub.AdditionalAttributes.ToArray())
-                .WithModifiers(StripTriviaFromModifiers(stubMethodSyntax.Modifiers))
-                .WithParameterList(ParameterList(SeparatedList(stub.StubParameters)))
-                .WithBody(stubCode);
-        }
-
         private static VirtualMethodIndexCompilationData? ProcessVirtualMethodIndexAttribute(AttributeData attrData)
         {
             // Found the attribute, but it has an error so report the error.
@@ -409,129 +385,30 @@ namespace Microsoft.Interop
             return NoMarshallingInfo.Instance;
         }
 
-        internal static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateManagedToNativeStub(
+        private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateManagedToNativeStub(
             IncrementalMethodStubGenerationContext methodStub)
         {
-            var diagnostics = new GeneratorDiagnostics();
-
-            // Generate stub code
-            var stubGenerator = new ManagedToNativeVTableMethodGenerator(
-                methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFramework,
-                methodStub.ManagedToUnmanagedGeneratorFactory.Key.TargetFrameworkVersion,
-                methodStub.SignatureContext.ElementTypeInformation,
-                methodStub.VtableIndexData.SetLastError,
-                methodStub.VtableIndexData.ImplicitThisParameter,
-                (elementInfo, ex) =>
-                {
-                    diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails);
-                },
-                methodStub.ManagedToUnmanagedGeneratorFactory.GeneratorFactory);
-
-            BlockSyntax code = stubGenerator.GenerateStubBody(
-                methodStub.VtableIndexData.Index,
-                methodStub.CallingConvention.Array,
-                methodStub.TypeKeyOwner.Syntax);
+            var (stub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateManagedToNativeStub(methodStub);
 
             return (
                 methodStub.ContainingSyntaxContext.AddContainingSyntax(
                     NativeTypeContainingSyntax)
                 .WrapMemberInContainingSyntaxWithUnsafeModifier(
-                    PrintGeneratedSource(
-                        methodStub.StubMethodSyntaxTemplate,
-                        methodStub.SignatureContext,
-                        code)
-                    .WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifier(IdentifierName(methodStub.ContainingSyntaxContext.ContainingSyntax[0].Identifier)))),
-                methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
+                    stub),
+                methodStub.Diagnostics.Array.AddRange(diagnostics));
         }
 
-        private const string ThisParameterIdentifier = "@this";
-
-        internal static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateNativeToManagedStub(
+        private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateNativeToManagedStub(
             IncrementalMethodStubGenerationContext methodStub)
         {
-            var diagnostics = new GeneratorDiagnostics();
-            ImmutableArray<TypePositionInfo> elements = AddImplicitElementInfos(methodStub);
-
-            // Generate stub code
-            var stubGenerator = new UnmanagedToManagedStubGenerator(
-                methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFramework,
-                methodStub.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion,
-                elements,
-                (elementInfo, ex) =>
-                {
-                    diagnostics.ReportMarshallingNotSupported(methodStub.DiagnosticLocation, elementInfo, ex.NotSupportedDetails);
-                },
-                methodStub.UnmanagedToManagedGeneratorFactory.GeneratorFactory);
-
-            BlockSyntax code = stubGenerator.GenerateStubBody(
-                MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
-                    IdentifierName(ThisParameterIdentifier),
-                    IdentifierName(methodStub.StubMethodSyntaxTemplate.Identifier)));
-
-            (ParameterListSyntax unmanagedParameterList, TypeSyntax returnType, _) = stubGenerator.GenerateAbiMethodSignatureData();
-
-            AttributeSyntax unmanagedCallersOnlyAttribute = Attribute(
-                ParseName(TypeNames.UnmanagedCallersOnlyAttribute));
-
-            if (methodStub.CallingConvention.Array.Length != 0)
-            {
-                unmanagedCallersOnlyAttribute = unmanagedCallersOnlyAttribute.AddArgumentListArguments(
-                    AttributeArgument(
-                        ImplicitArrayCreationExpression(
-                            InitializerExpression(SyntaxKind.CollectionInitializerExpression,
-                                SeparatedList<ExpressionSyntax>(
-                                    methodStub.CallingConvention.Array.Select(callConv => TypeOfExpression(ParseName($"System.Runtime.CompilerServices.CallConv{callConv.Name.ValueText}")))))))
-                    .WithNameEquals(NameEquals(IdentifierName("CallConvs"))));
-            }
-
-            MethodDeclarationSyntax unmanagedToManagedStub =
-                MethodDeclaration(returnType, $"ABI_{methodStub.StubMethodSyntaxTemplate.Identifier.Text}")
-                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword)))
-                .WithParameterList(unmanagedParameterList)
-                .AddAttributeLists(AttributeList(SingletonSeparatedList(unmanagedCallersOnlyAttribute)))
-                .WithBody(code);
+            var (stub, diagnostics) = VirtualMethodPointerStubGenerator.GenerateNativeToManagedStub(methodStub);
 
             return (
                 methodStub.ContainingSyntaxContext.AddContainingSyntax(
                     NativeTypeContainingSyntax)
                 .WrapMemberInContainingSyntaxWithUnsafeModifier(
-                    unmanagedToManagedStub),
-                methodStub.Diagnostics.Array.AddRange(diagnostics.Diagnostics));
-        }
-
-        private static ImmutableArray<TypePositionInfo> AddImplicitElementInfos(IncrementalMethodStubGenerationContext methodStub)
-        {
-            ImmutableArray<TypePositionInfo> originalElements = methodStub.SignatureContext.ElementTypeInformation;
-
-            var elements = ImmutableArray.CreateBuilder<TypePositionInfo>(originalElements.Length + 2);
-
-            elements.Add(new TypePositionInfo(methodStub.TypeKeyOwner, new NativeThisInfo(methodStub.UnwrapperSyntax))
-            {
-                InstanceIdentifier = ThisParameterIdentifier,
-                NativeIndex = 0,
-            });
-            foreach (TypePositionInfo element in originalElements)
-            {
-                elements.Add(element with
-                {
-                    NativeIndex = TypePositionInfo.IncrementIndex(element.NativeIndex)
-                });
-            }
-
-            if (methodStub.ExceptionMarshallingInfo != NoMarshallingInfo.Instance)
-            {
-                elements.Add(
-                    new TypePositionInfo(
-                        new ReferenceTypeInfo($"global::{TypeNames.System_Exception}", TypeNames.System_Exception),
-                        methodStub.ExceptionMarshallingInfo)
-                    {
-                        InstanceIdentifier = "__exception",
-                        ManagedIndex = TypePositionInfo.ExceptionIndex,
-                        NativeIndex = TypePositionInfo.ReturnIndex
-                    });
-            }
-
-            return elements.ToImmutable();
+                    stub),
+                methodStub.Diagnostics.Array.AddRange(diagnostics));
         }
 
         private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method)
@@ -584,57 +461,18 @@ namespace Microsoft.Interop
 
         private static MemberDeclarationSyntax GeneratePopulateVTableMethod(IGrouping<ContainingSyntaxContext, IncrementalMethodStubGenerationContext> vtableMethods)
         {
-            const string vtableParameter = "vtable";
             ContainingSyntaxContext containingSyntax = vtableMethods.Key.AddContainingSyntax(NativeTypeContainingSyntax);
+
+            const string vtableParameter = "vtable";
             MethodDeclarationSyntax populateVtableMethod = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)),
                 "PopulateUnmanagedVirtualMethodTable")
-                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword)))
+                .WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.UnsafeKeyword)))
                 .AddParameterListParameters(
                     Parameter(Identifier(vtableParameter))
                     .WithType(PointerType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))))));
 
-            foreach (var method in vtableMethods)
-            {
-                FunctionPointerTypeSyntax functionPointerType = GenerateUnmanagedFunctionPointerTypeForMethod(method);
-
-                // <vtableParameter>[<index>] = (void*)(<functionPointerType>)&ABI_<methodIdentifier>;
-                populateVtableMethod = populateVtableMethod.AddBodyStatements(
-                    ExpressionStatement(
-                        AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
-                            ElementAccessExpression(
-                                IdentifierName(vtableParameter))
-                            .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.VtableIndexData.Index)))),
-                            CastExpression(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword))),
-                                CastExpression(functionPointerType,
-                                    PrefixUnaryExpression(SyntaxKind.AddressOfExpression,
-                                        IdentifierName($"ABI_{method.StubMethodSyntaxTemplate.Identifier}")))))));
-            }
-
-            return containingSyntax.WrapMemberInContainingSyntaxWithUnsafeModifier(populateVtableMethod);
-        }
-
-        internal static FunctionPointerTypeSyntax GenerateUnmanagedFunctionPointerTypeForMethod(IncrementalMethodStubGenerationContext method)
-        {
-            var stubGenerator = new UnmanagedToManagedStubGenerator(
-                method.UnmanagedToManagedGeneratorFactory.Key.TargetFramework,
-                method.UnmanagedToManagedGeneratorFactory.Key.TargetFrameworkVersion,
-                AddImplicitElementInfos(method),
-                // Swallow diagnostics here since the diagnostics will be reported by the unmanaged->managed stub generation
-                (elementInfo, ex) => { },
-                method.UnmanagedToManagedGeneratorFactory.GeneratorFactory);
-
-            List<FunctionPointerParameterSyntax> functionPointerParameters = new();
-            var (paramList, retType, _) = stubGenerator.GenerateAbiMethodSignatureData();
-            functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type)));
-            // We add the return type as the last "parameter" here as that's what the function pointer syntax requires.
-            functionPointerParameters.Add(FunctionPointerParameter(retType));
-
-            // delegate* unmanaged<...>
-            ImmutableArray<FunctionPointerUnmanagedCallingConventionSyntax> callConv = method.CallingConvention.Array;
-            FunctionPointerTypeSyntax functionPointerType = FunctionPointerType(
-                    FunctionPointerCallingConvention(Token(SyntaxKind.UnmanagedKeyword), callConv.IsEmpty ? null : FunctionPointerUnmanagedCallingConventionList(SeparatedList(callConv))),
-                    FunctionPointerParameterList(SeparatedList(functionPointerParameters)));
-            return functionPointerType;
+            return containingSyntax.WrapMembersInContainingSyntaxWithUnsafeModifier(
+                populateVtableMethod.WithBody(VirtualMethodPointerStubGenerator.GenerateVirtualMethodTableSlotAssignments(vtableMethods, vtableParameter)));
         }
     }
 }
diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentNode.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SyntaxEquivalentNode.cs
new file mode 100644 (file)
index 0000000..9a9de4d
--- /dev/null
@@ -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.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Interop
+{
+    public record struct SyntaxEquivalentNode<T>(T Node)
+        where T : SyntaxNode
+    {
+        public bool Equals(SyntaxEquivalentNode<T> other)
+        {
+            return SyntaxEquivalentComparer.Instance.Equals(Node, other.Node);
+        }
+
+        public override int GetHashCode() => throw new UnreachableException();
+    }
+}
index 0a5b8ac..c74ca0b 100644 (file)
@@ -101,10 +101,25 @@ namespace Microsoft.Interop
 
         public const string System_Runtime_InteropServices_DynamicInterfaceCastableImplementationAttribute = "System.Runtime.InteropServices.DynamicInterfaceCastableImplementationAttribute";
 
+        public const string System_Guid = "System.Guid";
+
+        public const string System_Runtime_CompilerServices_RuntimeHelpers = "System.Runtime.CompilerServices.RuntimeHelpers";
+
         public const string GeneratedComInterfaceAttribute = "System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute";
+
         public const string InterfaceTypeAttribute = "System.Runtime.InteropServices.InterfaceTypeAttribute";
+
         public const string ComInterfaceTypeAttribute = "System.Runtime.InteropServices.ComInterfaceType";
+
+        public const string System_Runtime_InteropServices_GuidAttribute = "System.Runtime.InteropServices.GuidAttribute";
+
+        public const string System_Runtime_InteropServices_ComWrappers = "System.Runtime.InteropServices.ComWrappers";
+
         public const string System_Runtime_InteropServices_ComWrappers_ComInterfaceDispatch = "System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch";
+
+        public const string IIUnknownInterfaceType = "System.Runtime.InteropServices.Marshalling.IIUnknownInterfaceType";
+        public const string IUnknownDerivedAttribute = "System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute";
+
         public const string ComWrappersUnwrapper = "System.Runtime.InteropServices.Marshalling.ComWrappersUnwrapper";
         public const string UnmanagedObjectUnwrapperAttribute = "System.Runtime.InteropServices.Marshalling.UnmanagedObjectUnwrapperAttribute`1";
 
index 9addfc0..5c45200 100644 (file)
@@ -9,7 +9,6 @@ namespace System.Runtime.InteropServices.Marshalling
     [AttributeUsage(AttributeTargets.Interface)]
     public class IUnknownDerivedAttribute<T, TImpl> : Attribute, IUnknownDerivedDetails
         where T : IIUnknownInterfaceType
-        where TImpl : T
     {
         public IUnknownDerivedAttribute()
         {
index 59829ca..a5e4481 100644 (file)
@@ -204,7 +204,7 @@ namespace ComInterfaceGenerator.Unit.Tests
 
             INamedTypeSymbol generatedInterfaceImplementation = Assert.Single(userDefinedInterface.GetTypeMembers("Native"));
 
-            IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"{userDefinedInterfaceName}.{methodName}").OfType<IMethodSymbol>());
+            IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"global::{userDefinedInterfaceName}.{methodName}").OfType<IMethodSymbol>());
 
             SyntaxNode emittedImplementationSyntax = await methodImplementation.DeclaringSyntaxReferences[0].GetSyntaxAsync();
 
index db03aaa..0553592 100644 (file)
@@ -50,13 +50,6 @@ namespace ComInterfaceGenerator.Unit.Tests
         public static readonly string DisableRuntimeMarshalling = "[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]";
         public static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;";
 
-        public static string NativeInterfaceUsage() => @"
-// Try using the generated native interface
-sealed class NativeAPI : IUnmanagedVirtualMethodTableProvider, INativeAPI.Native
-{
-    public VirtualMethodTableInfo GetVirtualMethodTableInfoForKey(System.Type type) => throw null;
-}
-";
 
         public string SpecifiedMethodIndexNoExplicitParameters => $@"
 using System.Runtime.InteropServices;
@@ -68,7 +61,7 @@ partial interface INativeAPI
 {{
     {VirtualMethodIndex(0)}
     void Method();
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string SpecifiedMethodIndexNoExplicitParametersNoImplicitThis => $@"
 using System.Runtime.InteropServices;
@@ -81,7 +74,7 @@ partial interface INativeAPI
     {VirtualMethodIndex(0, ImplicitThisParameter: false)}
     void Method();
 
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string SpecifiedMethodIndexNoExplicitParametersCallConvWithCallingConventions => $@"
 using System.Runtime.CompilerServices;
@@ -113,7 +106,7 @@ partial interface INativeAPI
     [SuppressGCTransition]
     {VirtualMethodIndex(4)}
     void Method4();
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string BasicParametersAndModifiers(string typeName, string preDeclaration = "") => $@"
 using System.Runtime.CompilerServices;
@@ -129,7 +122,7 @@ partial interface INativeAPI
 {{
     {VirtualMethodIndex(0)}
     {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue);
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string BasicParametersAndModifiersManagedToUnmanaged(string typeName, string preDeclaration = "") => $@"
 using System.Runtime.CompilerServices;
@@ -145,7 +138,7 @@ partial interface INativeAPI
 {{
     {VirtualMethodIndex(0, Direction: MarshalDirection.ManagedToUnmanaged)}
     {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue);
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         public string BasicParametersAndModifiers<T>() => BasicParametersAndModifiers(typeof(T).FullName!);
         public string BasicParametersAndModifiersNoRef(string typeName, string preDeclaration = "") => $@"
 using System.Runtime.CompilerServices;
@@ -161,7 +154,7 @@ partial interface INativeAPI
 {{
     {VirtualMethodIndex(0)}
     {typeName} Method({typeName} value, in {typeName} inValue, out {typeName} outValue);
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string BasicParametersAndModifiersNoImplicitThis(string typeName) => $@"
 using System.Runtime.CompilerServices;
@@ -174,7 +167,7 @@ partial interface INativeAPI
 {{
     {VirtualMethodIndex(0, ImplicitThisParameter: false)}
     {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue);
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string BasicParametersAndModifiersNoImplicitThis<T>() => BasicParametersAndModifiersNoImplicitThis(typeof(T).FullName!);
         public string MarshalUsingCollectionCountInfoParametersAndModifiers<T>() => MarshalUsingCollectionCountInfoParametersAndModifiers(typeof(T).ToString());
@@ -197,7 +190,7 @@ partial interface INativeAPI
         [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
         [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
         out int pOutSize);
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string BasicReturnTypeComExceptionHandling(string typeName, string preDeclaration = "") => $@"
 using System.Runtime.CompilerServices;
@@ -211,7 +204,7 @@ partial interface INativeAPI
 {{
     {VirtualMethodIndex(0, ExceptionMarshalling : ExceptionMarshalling.Com)}
     {typeName} Method();
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public string BasicReturnTypeCustomExceptionHandling(string typeName, string customExceptionType, string preDeclaration = "") => $@"
 using System.Runtime.CompilerServices;
@@ -225,7 +218,7 @@ partial interface INativeAPI
 {{
     {VirtualMethodIndex(0, ExceptionMarshallingType : Type.GetType(customExceptionType))}
     {typeName} Method();
-}}" + NativeInterfaceUsage() + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + _attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         public class ManagedToUnmanaged : IVirtualMethodIndexSignatureProvider
         {
@@ -233,8 +226,6 @@ partial interface INativeAPI
 
             public bool ImplicitThisParameter => true;
 
-            public string NativeInterfaceUsage() => CodeSnippets.NativeInterfaceUsage();
-
             public ManagedToUnmanaged(IComInterfaceAttributeProvider attributeProvider)
             {
                 AttributeProvider = attributeProvider;
@@ -248,8 +239,6 @@ partial interface INativeAPI
 
             public bool ImplicitThisParameter => false;
 
-            public string NativeInterfaceUsage() => CodeSnippets.NativeInterfaceUsage();
-
             public ManagedToUnmanagedNoImplicitThis(IComInterfaceAttributeProvider attributeProvider)
             {
                 AttributeProvider = attributeProvider;
@@ -263,10 +252,6 @@ partial interface INativeAPI
 
             public bool ImplicitThisParameter => true;
 
-            // Unmanaged-to-managed-only stubs don't provide implementations for the interface, so we don't want to try to use the generated nested interface
-            // since it won't have managed implementations for the methods
-            public string NativeInterfaceUsage() => string.Empty;
-
             public UnmanagedToManaged(IComInterfaceAttributeProvider attributeProvider)
             {
                 AttributeProvider = attributeProvider;
@@ -280,8 +265,6 @@ partial interface INativeAPI
 
             public bool ImplicitThisParameter => true;
 
-            public string NativeInterfaceUsage() => CodeSnippets.NativeInterfaceUsage();
-
             public Bidirectional(IComInterfaceAttributeProvider attributeProvider)
             {
                 AttributeProvider = attributeProvider;
diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs
new file mode 100644 (file)
index 0000000..c2ea6ee
--- /dev/null
@@ -0,0 +1,145 @@
+// 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.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.Interop.UnitTests;
+using Xunit;
+
+namespace ComInterfaceGenerator.Unit.Tests
+{
+    public class ComInterfaceGeneratorOutputShape
+    {
+        [Fact]
+        public async Task SingleComInterface()
+        {
+            string source = """
+                using System.Runtime.InteropServices;
+                using System.Runtime.InteropServices.Marshalling;
+
+                [GeneratedComInterface]
+                partial interface INativeAPI
+                {
+                    void Method();
+                    void Method2();
+                }
+                """;
+            Compilation comp = await TestUtils.CreateCompilation(source);
+            TestUtils.AssertPreSourceGeneratorCompilation(comp);
+
+            var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator());
+            TestUtils.AssertPostSourceGeneratorCompilation(newComp);
+            // We'll create one syntax tree for the new interface.
+            Assert.Equal(comp.SyntaxTrees.Count() + 1, newComp.SyntaxTrees.Count());
+
+            VerifyShape(newComp, "INativeAPI");
+        }
+
+        [Fact]
+        public async Task MultipleComInterfaces()
+        {
+            string source = $$"""
+                using System.Runtime.InteropServices;
+                using System.Runtime.InteropServices.Marshalling;
+                
+                [GeneratedComInterface]
+                partial interface I
+                {
+                    void Method();
+                    void Method2();
+                }
+                [GeneratedComInterface]
+                partial interface J
+                {
+                    void Method();
+                    void Method2();
+                }
+                """;
+            Compilation comp = await TestUtils.CreateCompilation(source);
+            TestUtils.AssertPreSourceGeneratorCompilation(comp);
+
+            var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator());
+            TestUtils.AssertPostSourceGeneratorCompilation(newComp);
+            // We'll create one syntax tree per user-defined interface.
+            Assert.Equal(comp.SyntaxTrees.Count() + 2, newComp.SyntaxTrees.Count());
+
+            VerifyShape(newComp, "I");
+            VerifyShape(newComp, "J");
+        }
+
+        [Fact]
+        public async Task EmptyComInterface()
+        {
+            string source = $$"""
+                using System.Runtime.InteropServices;
+                using System.Runtime.InteropServices.Marshalling;
+                
+                [GeneratedComInterface]
+                partial interface I
+                {
+                    void Method();
+                    void Method2();
+                }
+                [GeneratedComInterface]
+                partial interface Empty
+                {
+                }
+                [GeneratedComInterface]
+                partial interface J
+                {
+                    void Method();
+                    void Method2();
+                }
+                """;
+            Compilation comp = await TestUtils.CreateCompilation(source);
+            TestUtils.AssertPreSourceGeneratorCompilation(comp);
+
+            var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator());
+            TestUtils.AssertPostSourceGeneratorCompilation(newComp);
+            // We'll create one syntax tree per user-defined interface.
+            Assert.Equal(comp.SyntaxTrees.Count() + 3, newComp.SyntaxTrees.Count());
+
+            VerifyShape(newComp, "I");
+            VerifyShape(newComp, "J");
+        }
+
+        private static void VerifyShape(Compilation comp, string userDefinedInterfaceMetadataName)
+        {
+            INamedTypeSymbol? userDefinedInterface = comp.Assembly.GetTypeByMetadataName(userDefinedInterfaceMetadataName);
+            Assert.NotNull(userDefinedInterface);
+
+            INamedTypeSymbol? iUnknownDerivedAttributeType = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute`2");
+
+            Assert.NotNull(iUnknownDerivedAttributeType);
+
+            AttributeData iUnknownDerivedAttribute = Assert.Single(
+                userDefinedInterface.GetAttributes(),
+                attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, iUnknownDerivedAttributeType));
+
+            Assert.Collection(Assert.IsAssignableFrom<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
+                infoType =>
+                {
+                    Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
+                },
+                implementationType =>
+                {
+                    Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(implementationType).IsFileLocal);
+                    Assert.Contains(userDefinedInterface, implementationType.Interfaces, SymbolEqualityComparer.Default);
+                    Assert.Contains(implementationType.GetAttributes(), attr => attr.AttributeClass?.ToDisplayString() == typeof(DynamicInterfaceCastableImplementationAttribute).FullName);
+                    Assert.All(userDefinedInterface.GetMembers().OfType<IMethodSymbol>().Where(method => method.IsAbstract && !method.IsStatic),
+                        method =>
+                        {
+                            Assert.NotNull(implementationType.FindImplementationForInterfaceMember(method));
+                        });
+                });
+        }
+    }
+}
index cae2083..1f4e361 100644 (file)
@@ -337,8 +337,6 @@ namespace ComInterfaceGenerator.Unit.Tests
             {
                 // Duplicate 'using'
                 "CS0105",
-                // ComWrappersUnwrapper is inaccessible due to its protection level
-                "CS0122",
                 // Variable assigned to but never read
                 "CS0219"
             };
index d23ec74..83bac9f 100644 (file)
@@ -22,8 +22,8 @@ namespace ComInterfaceGenerator.Unit.Tests
 
         public string UnmanagedObjectUnwrapper(Type t) => "";
 
-        public string GeneratedComInterface => "[global::System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute]";
+        public string GeneratedComInterface => @$"[global::System.Runtime.InteropServices.Marshalling.GeneratedComInterface, global::System.Runtime.InteropServices.Guid(""0A52B77C-E08B-4274-A1F4-1A2BF2C07E60"")]";
 
         public string AdditionalUserRequiredInterfaces(string userDefinedInterfaceName) => "";
     }
-}
\ No newline at end of file
+}
index 451d513..97b73a1 100644 (file)
@@ -21,8 +21,6 @@ namespace ComInterfaceGenerator.Unit.Tests
         public static readonly string DisableRuntimeMarshalling = "[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling]";
         public static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;";
 
-        string NativeInterfaceUsage();
-
         string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifiers(string typeName, string preDeclaration) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -37,7 +35,7 @@ partial interface INativeAPI
 {{
     {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
     {typeName} Method({typeName} value, in {typeName} inValue, ref {typeName} refValue, out {typeName} outValue);
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.BasicParametersAndModifiersNoRef(string typeName, string preDeclaration) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -52,7 +50,7 @@ partial interface INativeAPI
 {{
     {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
     {typeName} Method({typeName} value, in {typeName} inValue, out {typeName} outValue);
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         string ICustomMarshallingSignatureTestProvider.BasicParameterByValue(string typeName, string preDeclaration) => $@"
 using System.Runtime.CompilerServices;
@@ -66,7 +64,7 @@ partial interface INativeAPI
 {{
     {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
     void Method({typeName} value);
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         string ICustomMarshallingSignatureTestProvider.BasicParameterWithByRefModifier(string modifier, string typeName, string preDeclaration) => $@"
 using System.Runtime.CompilerServices;
@@ -82,7 +80,7 @@ partial interface INativeAPI
 {{
     {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
     void Method({modifier} {typeName} value);
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.BasicReturnType(string typeName, string preDeclaration) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -95,7 +93,7 @@ partial interface INativeAPI
 {{
     {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
     {typeName} Method();
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.MarshalUsingParametersAndModifiers(string typeName, string marshallerTypeName, string preDeclaration) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -113,7 +111,7 @@ partial interface INativeAPI
         [MarshalUsing(typeof({marshallerTypeName}))] in {typeName} pIn,
         [MarshalUsing(typeof({marshallerTypeName}))] ref {typeName} pRef,
         [MarshalUsing(typeof({marshallerTypeName}))] out {typeName} pOut);
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionCountInfoParametersAndModifiers(string collectionType) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -133,7 +131,7 @@ partial interface INativeAPI
         [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
         [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
         out int pOutSize);
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionParametersAndModifiers(string collectionType, string marshallerType) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -155,7 +153,7 @@ partial interface INativeAPI
         [MarshalUsing(typeof({marshallerType}), CountElementName = ""pOutSize"")] out {collectionType} pOut,
         out int pOutSize
         );
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionReturnValueLength(string collectionType, string marshallerType) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -171,7 +169,7 @@ partial interface INativeAPI
     int Method(
         [MarshalUsing(typeof({marshallerType}), CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out {collectionType} pOut
         );
-}}" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+}}" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
 
         string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionOutConstantLength(string collectionType, string predeclaration) => $@"
 using System.Runtime.CompilerServices;
@@ -190,7 +188,7 @@ partial interface INativeAPI
         [MarshalUsing(ConstantElementCount = 10)] out {collectionType} pOut
         );
 }}
-" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.MarshalUsingCollectionReturnConstantLength(string collectionType, string predeclaration) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -207,7 +205,7 @@ partial interface INativeAPI
     [return:MarshalUsing(ConstantElementCount = 10)]
     {collectionType} Method();
 }}
-" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
         string ICustomMarshallingSignatureTestProvider.CustomElementMarshalling(string collectionType, string elementMarshaller, string predeclaration) => $@"
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -232,6 +230,6 @@ partial interface INativeAPI
         out int pOutSize
         );
 }}
-" + NativeInterfaceUsage() + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
+" + AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI");
     }
 }
@@ -14,7 +14,7 @@ using Xunit;
 
 namespace ComInterfaceGenerator.Unit.Tests
 {
-    public class NativeInterfaceShape
+    public class VTableGeneratorOutputShape
     {
         [Fact]
         public async Task NativeInterfaceNestedInUserInterface()
@@ -32,7 +32,6 @@ namespace ComInterfaceGenerator.Unit.Tests
                 }
                 """;
             Compilation comp = await TestUtils.CreateCompilation(source);
-            // Allow the Native nested type name to be missing in the pre-source-generator compilation
             TestUtils.AssertPreSourceGeneratorCompilation(comp);
 
             var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());
@@ -59,7 +58,6 @@ namespace ComInterfaceGenerator.Unit.Tests
                 }
                 """;
             Compilation comp = await TestUtils.CreateCompilation(source);
-            // Allow the Native nested type name to be missing in the pre-source-generator compilation
             TestUtils.AssertPreSourceGeneratorCompilation(comp);
 
             var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());
@@ -71,7 +69,7 @@ namespace ComInterfaceGenerator.Unit.Tests
         }
 
         [Fact]
-        public async Task NativeInterfaceHasDynamicInterfaceCastableImplementationAttribute()
+        public async Task NativeInterfaceImplementsUserInterfaceMethods()
         {
             string source = $$"""
                 using System.Runtime.InteropServices;
@@ -84,9 +82,34 @@ namespace ComInterfaceGenerator.Unit.Tests
                     [VirtualMethodIndex(0)]
                     void Method();
                 }
+
+                // Ensure we can implement the native interface without defining any implementations.
+                class C : INativeAPI.Native {}
                 """;
             Compilation comp = await TestUtils.CreateCompilation(source);
             // Allow the Native nested type name to be missing in the pre-source-generator compilation
+            TestUtils.AssertPreSourceGeneratorCompilation(comp, "CS0426");
+
+            var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());
+            TestUtils.AssertPostSourceGeneratorCompilation(newComp);
+        }
+
+        [Fact]
+        public async Task NativeInterfaceHasDynamicInterfaceCastableImplementationAttribute()
+        {
+            string source = $$"""
+                using System.Runtime.InteropServices;
+                using System.Runtime.InteropServices.Marshalling;
+
+                [UnmanagedObjectUnwrapper<UnmanagedObjectUnwrapper.TestUnwrapper>]
+                partial interface INativeAPI : IUnmanagedInterfaceType
+                {
+                    static unsafe void* IUnmanagedInterfaceType.VirtualMethodTableManagedImplementation => null;
+                    [VirtualMethodIndex(0)]
+                    void Method();
+                }
+                """;
+            Compilation comp = await TestUtils.CreateCompilation(source);
             TestUtils.AssertPreSourceGeneratorCompilation(comp);
 
             var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());
@@ -118,7 +141,6 @@ namespace ComInterfaceGenerator.Unit.Tests
                 }
                 """;
             Compilation comp = await TestUtils.CreateCompilation(source);
-            // Allow the Native nested type name to be missing in the pre-source-generator compilation
             TestUtils.AssertPreSourceGeneratorCompilation(comp);
 
             var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());