diff --git a/src/Microsoft.Windows.CsWin32/Generator.Com.cs b/src/Microsoft.Windows.CsWin32/Generator.Com.cs index 2a5ad7fb..77d4007e 100644 --- a/src/Microsoft.Windows.CsWin32/Generator.Com.cs +++ b/src/Microsoft.Windows.CsWin32/Generator.Com.cs @@ -114,6 +114,8 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type IdentifierNameSyntax hrLocal = IdentifierName("__hr"); StatementSyntax returnSOK = ReturnStatement(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, HresultTypeSyntax, IdentifierName("S_OK"))); + this.MainGenerator.RequestInteropType("Windows.Win32.Foundation", "HRESULT", context); + // It is imperative that we generate methods for all base interfaces as well, ahead of any implemented by *this* interface. var allMethods = new List(); while (!baseTypes.IsEmpty) @@ -134,11 +136,13 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type allMethods.AddRange(typeDef.GetMethods().Select(m => new QualifiedMethodDefinitionHandle(this, m))); int methodCounter = 0; HashSet helperMethodsInStruct = new(); - ISet declaredProperties = this.GetDeclarableProperties( - allMethods.Select(qh => qh.Reader.GetMethodDefinition(qh.MethodHandle)), - originalIfaceName, - allowNonConsecutiveAccessors: true, - context); + HashSet declaredProperties = new(StringComparer.Ordinal); + declaredProperties.UnionWith( + from method in allMethods + group method by method.Generator into methodsByMetadata + let methodDefs = methodsByMetadata.Select(qh => qh.Reader.GetMethodDefinition(qh.MethodHandle)) + from property in methodsByMetadata.Key.GetDeclarableProperties(methodDefs, originalIfaceName, allowNonConsecutiveAccessors: true, context) + select property); ISet? ifaceDeclaredProperties = ccwThisParameter is not null ? this.GetDeclarableProperties(allMethods.Select(qh => qh.Reader.GetMethodDefinition(qh.MethodHandle)), originalIfaceName, allowNonConsecutiveAccessors: false, context) : null; foreach (QualifiedMethodDefinitionHandle methodDefHandle in allMethods) @@ -189,7 +193,7 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type // We can declare this method as a property accessor if it represents a property. // We must also confirm that the property type is the same in both cases, because sometimes they aren't (e.g. IUIAutomationProxyFactoryEntry.ClassName). - if (this.TryGetPropertyAccessorInfo(methodDefinition.Method, originalIfaceName, context, out IdentifierNameSyntax? propertyName, out SyntaxKind? accessorKind, out TypeSyntax? propertyType) && + if (methodDefinition.Generator.TryGetPropertyAccessorInfo(methodDefinition.Method, originalIfaceName, context, out IdentifierNameSyntax? propertyName, out SyntaxKind? accessorKind, out TypeSyntax? propertyType) && declaredProperties.Contains(propertyName.Identifier.ValueText)) { StatementSyntax ThrowOnHRFailure(ExpressionSyntax hrExpression) => ExpressionStatement(InvocationExpression( @@ -257,7 +261,7 @@ StatementSyntax ThrowOnHRFailure(ExpressionSyntax hrExpression) => ExpressionSta else { BlockSyntax body; - bool preserveSig = this.UsePreserveSigForComMethod(methodDefinition.Method, signature, ifaceName.Identifier.ValueText, methodName); + bool preserveSig = methodDefinition.Generator.UsePreserveSigForComMethod(methodDefinition.Method, signature, ifaceName.Identifier.ValueText, methodName); if (preserveSig) { // return ... diff --git a/src/Microsoft.Windows.CsWin32/Generator.cs b/src/Microsoft.Windows.CsWin32/Generator.cs index f652eaa3..ce63ce14 100644 --- a/src/Microsoft.Windows.CsWin32/Generator.cs +++ b/src/Microsoft.Windows.CsWin32/Generator.cs @@ -200,7 +200,7 @@ internal Generator MainGenerator private bool WideCharOnly => this.options.WideCharOnly; - private string Namespace => this.InputAssemblyName; + private string Namespace => this.MetadataIndex.CommonNamespace; private SyntaxKind Visibility => this.options.Public ? SyntaxKind.PublicKeyword : SyntaxKind.InternalKeyword; diff --git a/test/Microsoft.Windows.CsWin32.Tests/ExternalMetadata/NOTICE.md b/test/Microsoft.Windows.CsWin32.Tests/ExternalMetadata/NOTICE.md new file mode 100644 index 00000000..5d20d7df --- /dev/null +++ b/test/Microsoft.Windows.CsWin32.Tests/ExternalMetadata/NOTICE.md @@ -0,0 +1,5 @@ +This folder contains winmd files obtained from other sources which we use for testing. + +Metadata | Source +--|-- +ServiceFabric.winmd | [youyuanwu/fabric-metadata](https://github.com/youyuanwu/fabric-metadata/raw/a1bcca6ad6f6a772c9e5ff4bdba80ae5e5f24cfc/.windows/winmd/ServiceFabric.winmd) diff --git a/test/Microsoft.Windows.CsWin32.Tests/ExternalMetadata/ServiceFabric.winmd b/test/Microsoft.Windows.CsWin32.Tests/ExternalMetadata/ServiceFabric.winmd new file mode 100644 index 00000000..2b08c060 Binary files /dev/null and b/test/Microsoft.Windows.CsWin32.Tests/ExternalMetadata/ServiceFabric.winmd differ diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs index 548d0b18..fefce5fb 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs @@ -10,6 +10,7 @@ public abstract class GeneratorTestBase : IDisposable, IAsyncLifetime protected static readonly string WdkMetadataPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!, "Windows.Wdk.winmd"); protected static readonly string[] DefaultMetadataPaths = new[] { MetadataPath, WdkMetadataPath }; ////protected static readonly string DiaMetadataPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!, "Microsoft.Dia.winmd"); + protected static readonly string ServiceFabricMetadataPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!, "ExternalMetadata", "ServiceFabric.winmd"); protected static readonly string ApiDocsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!, "apidocs.msgpack"); protected readonly ITestOutputHelper logger; diff --git a/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj b/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj index f65224b7..f04b13ff 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj +++ b/test/Microsoft.Windows.CsWin32.Tests/Microsoft.Windows.CsWin32.Tests.csproj @@ -24,6 +24,9 @@ PreserveNewest + + PreserveNewest + diff --git a/test/Microsoft.Windows.CsWin32.Tests/MultiMetadataTests.cs b/test/Microsoft.Windows.CsWin32.Tests/MultiMetadataTests.cs new file mode 100644 index 00000000..349d7440 --- /dev/null +++ b/test/Microsoft.Windows.CsWin32.Tests/MultiMetadataTests.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +public class MultiMetadataTests : GeneratorTestBase +{ + public MultiMetadataTests(ITestOutputHelper logger) + : base(logger) + { + } + + [Theory, PairwiseData] + public void BasicServiceFabric(bool allowMarshaling) + { + this.generator = this.CreateSuperGenerator(DefaultMetadataPaths.Concat(new[] { ServiceFabricMetadataPath }).ToArray(), DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling }); + Assert.True(this.generator.TryGenerate("IFabricStringResult", CancellationToken.None)); + this.CollectGeneratedCode(this.generator); + this.AssertNoDiagnostics(); + } +}