diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/AddMarshalAsToElementFixer.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/AddMarshalAsToElementFixer.cs new file mode 100644 index 00000000000000..dc1037aa527607 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/AddMarshalAsToElementFixer.cs @@ -0,0 +1,79 @@ +// 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.Composition; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Editing; + +namespace Microsoft.Interop.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + public sealed class AddMarshalAsToElementFixer : CodeFixProvider + { + public override FixAllProvider? GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(GeneratorDiagnostics.Ids.NotRecommendedGeneratedComInterfaceUsage); + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Get the syntax root and semantic model + Document doc = context.Document; + SyntaxNode? root = await doc.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root == null) + return; + + SyntaxNode node = root.FindNode(context.Span); + + foreach (var diagnostic in context.Diagnostics) + { + if (!diagnostic.Properties.TryGetValue(GeneratorDiagnosticProperties.AddMarshalAsAttribute, out string? addMarshalAsAttribute)) + { + continue; + } + + foreach (var unmanagedType in addMarshalAsAttribute.Split(',')) + { + string unmanagedTypeName = unmanagedType.Trim(); + context.RegisterCodeFix( + CodeAction.Create( + $"Add [MarshalAs(UnmanagedType.{unmanagedTypeName})]", + async ct => + { + DocumentEditor editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false); + + SyntaxGenerator gen = editor.Generator; + + SyntaxNode marshalAsAttribute = gen.Attribute( + TypeNames.System_Runtime_InteropServices_MarshalAsAttribute, + gen.AttributeArgument( + gen.MemberAccessExpression( + gen.DottedName(TypeNames.System_Runtime_InteropServices_UnmanagedType), + gen.IdentifierName(unmanagedTypeName.Trim())))); + + if (node.IsKind(SyntaxKind.MethodDeclaration)) + { + editor.AddReturnAttribute(node, marshalAsAttribute); + } + else + { + editor.AddAttribute(node, marshalAsAttribute); + } + + return editor.GetChangedDocument(); + }, + $"AddUnmanagedType.{unmanagedTypeName}"), + diagnostic); + } + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceFixer.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceFixer.cs index 081b6081f9dc9d..8db74fa70244c9 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceFixer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceFixer.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -111,6 +112,12 @@ private static async Task ConvertComImportToGeneratedComInterfaceAsync(DocumentE var generatedDeclaration = member; generatedDeclaration = AddExplicitDefaultBoolMarshalling(gen, method, generatedDeclaration, "VariantBool"); + + if (method.MethodImplementationFlags.HasFlag(MethodImplAttributes.PreserveSig)) + { + generatedDeclaration = AddHResultStructAsErrorMarshalling(gen, method, generatedDeclaration); + } + editor.ReplaceNode(member, generatedDeclaration); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs index 8967e6b3e69b32..703f8f584cbaf2 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs @@ -211,6 +211,13 @@ private static MemberDeclarationSyntax GenerateIUnknownDerivedAttributeApplicati .WithTypeParameterList(context.ContainingSyntax.TypeParameters) .AddAttributeLists(AttributeList(SingletonSeparatedList(s_iUnknownDerivedAttributeTemplate)))); + private static bool IsHResultLikeType(ManagedTypeInfo type) + { + string typeName = type.FullTypeName.Split('.', ':')[^1]; + return typeName.Equals("hr", StringComparison.OrdinalIgnoreCase) + || typeName.Equals("hresult", StringComparison.OrdinalIgnoreCase); + } + private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, ManagedTypeInfo owningInterface, CancellationToken ct) { ct.ThrowIfCancellationRequested(); @@ -280,7 +287,7 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M { if ((returnSwappedSignatureElements[i].ManagedType is SpecialTypeInfo { SpecialType: SpecialType.System_Int32 or SpecialType.System_Enum } or EnumTypeInfo && returnSwappedSignatureElements[i].MarshallingAttributeInfo.Equals(NoMarshallingInfo.Instance)) - || (returnSwappedSignatureElements[i].ManagedType.FullTypeName.Split('.', ':').LastOrDefault()?.ToLowerInvariant() is "hr" or "hresult")) + || (IsHResultLikeType(returnSwappedSignatureElements[i].ManagedType))) { generatorDiagnostics.ReportDiagnostic(DiagnosticInfo.Create(GeneratorDiagnostics.ComMethodManagedReturnWillBeOutVariable, symbol.Locations[0])); } @@ -310,6 +317,23 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M }) }; } + else + { + // If our method is PreserveSig, we will notify the user if they are returning a type that may be an HRESULT type + // that is defined as a structure. These types used to work with built-in COM interop, but they do not work with + // source-generated interop as we now use the MemberFunction calling convention, which is more correct. + TypePositionInfo? managedReturnInfo = signatureContext.ElementTypeInformation.FirstOrDefault(e => e.IsManagedReturnPosition); + if (managedReturnInfo is { MarshallingAttributeInfo: UnmanagedBlittableMarshallingInfo, ManagedType: ValueTypeInfo valueType } + && IsHResultLikeType(valueType)) + { + generatorDiagnostics.ReportDiagnostic(DiagnosticInfo.Create( + GeneratorDiagnostics.HResultTypeWillBeTreatedAsStruct, + symbol.Locations[0], + ImmutableDictionary.Empty.Add(GeneratorDiagnosticProperties.AddMarshalAsAttribute, "Error"), + valueType.DiagnosticFormattedName)); + } + } + var direction = GetDirectionFromOptions(generatedComInterfaceAttributeData.Options); // Ensure the size of collections are known at marshal / unmarshal in time. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.csproj b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.csproj index 4a24c42119ed4c..02660ec8b2c745 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.csproj +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.csproj @@ -27,6 +27,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGeneratorHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGeneratorHelpers.cs index 62c4d533a81e12..f69377f7592ea6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGeneratorHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGeneratorHelpers.cs @@ -32,6 +32,8 @@ internal static class ComInterfaceGeneratorHelpers InteropGenerationOptions interopGenerationOptions = new(UseMarshalType: true); generatorFactory = new MarshalAsMarshallingGeneratorFactory(interopGenerationOptions, generatorFactory); + generatorFactory = new StructAsHResultMarshallerFactory(generatorFactory); + IMarshallingGeneratorFactory elementFactory = new AttributedMarshallingModelGeneratorFactory( // Since the char type in an array will not be part of the P/Invoke signature, we can // use the regular blittable marshaller in all cases. diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs index 418009d192a3bb..c920d59c00aeee 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs @@ -474,6 +474,16 @@ public class Ids DiagnosticSeverity.Info, isEnabledByDefault: true); + /// + public static readonly DiagnosticDescriptor HResultTypeWillBeTreatedAsStruct = + new DiagnosticDescriptor( + Ids.NotRecommendedGeneratedComInterfaceUsage, + GetResourceString(nameof(SR.HResultTypeWillBeTreatedAsStructTitle)), + GetResourceString(nameof(SR.HResultTypeWillBeTreatedAsStructMessage)), + Category, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + /// /// Report diagnostic for invalid configuration for string marshalling. /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Marshallers/StructAsHResultMarshallerFactory.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Marshallers/StructAsHResultMarshallerFactory.cs new file mode 100644 index 00000000000000..909a501041749f --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Marshallers/StructAsHResultMarshallerFactory.cs @@ -0,0 +1,116 @@ +// 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.Runtime.InteropServices; +using System.Text; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Microsoft.Interop +{ + internal sealed class StructAsHResultMarshallerFactory : IMarshallingGeneratorFactory + { + private static readonly Marshaller s_marshaller = new(); + + private readonly IMarshallingGeneratorFactory _inner; + + public StructAsHResultMarshallerFactory(IMarshallingGeneratorFactory inner) + { + _inner = inner; + } + + public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) + { + // Value type with MarshalAs(UnmanagedType.Error), to be marshalled as an unmanaged HRESULT. + if (info is { ManagedType: ValueTypeInfo, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.Error, _) }) + { + return ResolvedGenerator.Resolved(s_marshaller); + } + + return _inner.Create(info, context); + } + + private sealed class Marshaller : IMarshallingGenerator + { + public ManagedTypeInfo AsNativeType(TypePositionInfo info) => SpecialTypeInfo.Int32; + + public IEnumerable Generate(TypePositionInfo info, StubCodeContext context) + { + var (managed, unmanaged) = context.GetIdentifiers(info); + + switch (context.CurrentStage) + { + case StubCodeContext.Stage.Marshal: + if (MarshallerHelpers.GetMarshalDirection(info, context) is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional) + { + // unmanaged = Unsafe.BitCast(managed); + yield return ExpressionStatement( + AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + IdentifierName(unmanaged), + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseTypeName(TypeNames.System_Runtime_CompilerServices_Unsafe), + GenericName(Identifier("BitCast"), + TypeArgumentList( + SeparatedList( + new[] + { + info.ManagedType.Syntax, + AsNativeType(info).Syntax + })))), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(managed))))))); + } + break; + case StubCodeContext.Stage.Unmarshal: + if (MarshallerHelpers.GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional) + { + // managed = Unsafe.BitCast(unmanaged); + yield return ExpressionStatement( + AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + IdentifierName(managed), + InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + ParseTypeName(TypeNames.System_Runtime_CompilerServices_Unsafe), + GenericName(Identifier("BitCast"), + TypeArgumentList( + SeparatedList( + new[] + { + AsNativeType(info).Syntax, + info.ManagedType.Syntax + })))), + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(unmanaged))))))); + } + break; + default: + break; + } + } + + public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) + { + return info.IsByRef ? SignatureBehavior.PointerToNativeType : SignatureBehavior.NativeType; + } + + public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) + { + if (info.IsByRef) + { + return ValueBoundaryBehavior.AddressOfNativeIdentifier; + } + + return ValueBoundaryBehavior.NativeIdentifier; + } + + public bool IsSupported(TargetFramework target, Version version) => target == TargetFramework.Net && version.Major >= 8; + + public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, TypePositionInfo info, StubCodeContext context, out GeneratorDiagnostic? diagnostic) + => ByValueMarshalKindSupportDescriptor.Default.GetSupport(marshalKind, info, context, out diagnostic); + + public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/ConvertToSourceGeneratedInteropFixer.cs b/src/libraries/System.Runtime.InteropServices/gen/Common/ConvertToSourceGeneratedInteropFixer.cs index 7422d08060f3e3..ec77b75f7dc5c3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/ConvertToSourceGeneratedInteropFixer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/ConvertToSourceGeneratedInteropFixer.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; @@ -259,5 +260,34 @@ static SyntaxNode GenerateMarshalAsUnmanagedTypeBoolAttribute(SyntaxGenerator ge generator.DottedName(TypeNames.System_Runtime_InteropServices_UnmanagedType), generator.IdentifierName(unmanagedTypeMemberIdentifier)))); } + + protected static SyntaxNode AddHResultStructAsErrorMarshalling(SyntaxGenerator generator, IMethodSymbol methodSymbol, SyntaxNode generatedDeclaration) + { + if (methodSymbol.ReturnType is { TypeKind: TypeKind.Struct } + && IsHResultLikeType(methodSymbol.ReturnType) + && !methodSymbol.GetReturnTypeAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_InteropServices_MarshalAsAttribute)) + { + generatedDeclaration = generator.AddReturnAttributes(generatedDeclaration, + GeneratedMarshalAsUnmanagedTypeErrorAttribute(generator)); + } + + return generatedDeclaration; + + + static bool IsHResultLikeType(ITypeSymbol type) + { + string typeName = type.Name; + return typeName.Equals("hr", StringComparison.OrdinalIgnoreCase) + || typeName.Equals("hresult", StringComparison.OrdinalIgnoreCase); + } + + // MarshalAs(UnmanagedType.Error) + static SyntaxNode GeneratedMarshalAsUnmanagedTypeErrorAttribute(SyntaxGenerator generator) + => generator.Attribute(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute, + generator.AttributeArgument( + generator.MemberAccessExpression( + generator.DottedName(TypeNames.System_Runtime_InteropServices_UnmanagedType), + generator.IdentifierName(nameof(UnmanagedType.Error))))); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx index 39b19f67fe7b33..ebf0170e328752 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx @@ -883,4 +883,10 @@ The return value in the managed definition will be converted to an additional 'out' parameter at the end of the parameter list when calling the unmanaged COM method. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + This type will be treated as a struct in the native signature, not as a native HRESULT + \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf index e1b1a90cd1d843..7319169fc4e556 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf @@ -437,6 +437,16 @@ Poskytnutý graf obsahuje cykly a nelze ho řadit topologicky. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. Atribut [In] není podporován, pokud není použit také atribut [Out]. Blittable arrays nelze zařadit pouze jako [In]. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf index 5f1a7987ffd947..87834cb361a0b3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf @@ -437,6 +437,16 @@ Das bereitgestellte Diagramm weist Zyklen auf und kann nicht topologisch sortiert werden. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. Das [In]-Attribut wird nur unterstützt, wenn auch das [Out]-Attribut verwendet wird. Blittable-Arrays können nicht nur als "[In]" gemarshallt werden. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf index 147c7bc35eb31d..b64feee1e6d684 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf @@ -437,6 +437,16 @@ El gráfico porporcionado tiene ciclos y no se puede ordenar topológicamente. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. El atributo '[In]' no se admite a menos que también se use el atributo '[Out]'. Las matrices que se pueden transferir en bloque de bits no se pueden serializar solo como '[In]'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf index 7a3a880eee4a79..8a6ae8aa04bf6e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf @@ -437,6 +437,16 @@ Le graphique fourni a des cycles et ne peut pas être trié topologiement. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. L’attribut '[In]' n’est pas pris en charge, sauf si l’attribut '[Out]' est également utilisé. Les tableaux blittables ne peuvent pas être marshalés en tant que « [In] » uniquement. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf index 72ee7c6d199eb4..8d8eee75fb0835 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf @@ -437,6 +437,16 @@ Il grafico specificato contiene cicli e non può essere ordinato in modo topologico. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. L'attributo '[In]' non è supportato a meno che non venga usato anche l'attributo '[Out]'. Le matrici copiabili da BLT non possono essere sottoposte a marshalling solo come '[In]'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf index d9369c066ada6e..f49c439b7809d6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf @@ -437,6 +437,16 @@ 指定されたグラフにはサイクルがあるため、位相的に並べ替えることはできません。 + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. '[In]'属性は、'[Out]'属性も使用しない限りサポートされません。Blittable 配列は、'[In]'としてのみマーシャリングできません。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf index e1bbee48c2b463..b6de95a6c9eab9 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf @@ -437,6 +437,16 @@ 제공된 그래프에는 주기가 있으며 토폴로지로 정렬할 수 없습니다. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. '[Out]' 특성도 사용되지 않는 한 '[In]' 특성은 지원되지 않습니다. Blittable 배열은 '[In]'으로만 마샬링할 수 없습니다. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf index 091f4f56a3762e..843a756f4bce83 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf @@ -437,6 +437,16 @@ Podany wykres zawiera cykle i nie można go posortować topologicznie. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. Atrybut „[In]” nie jest obsługiwany, chyba że używany jest również atrybut „[Out]”. Tablice kopiowalne nie mogą być kierowane tylko jako „[In]”. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf index c551c0a7b047bc..454ee8d412fca3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf @@ -437,6 +437,16 @@ O grafo fornecido tem ciclos e não pode ser classificado topologicamente. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. O atributo '[In]' não é suportado, a menos que o atributo '[Out]' também seja usado. Matrizes Blittable não podem ser empacotadas apenas como '[In]'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf index 0b08eabfaa0c6b..00c77449ac302b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf @@ -437,6 +437,16 @@ Указанный граф содержит циклы, и его невозможно топологически отсортировать. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. Атрибут "[In]" не поддерживается, если также не используется атрибут "[Out]". Преобразуемые массивы нельзя сортировать только как "[In]". diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf index 4a89f731448105..a025fed02419b9 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf @@ -437,6 +437,16 @@ Sağlanan grafikte döngüler var ve bu grafik topolojik olarak sıralanamıyor. + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. '[Out]' özniteliği de kullanılmadığı sürece '[In]' özniteliği desteklenmez. Blittable dizileri yalnızca '[In]' olarak hazırlanamaz. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf index 60cac07d312d81..db934d7871a510 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf @@ -437,6 +437,16 @@ 提供的图形具有循环,并且无法按拓扑顺序排序。 + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. 不支持“[In]”属性,除非也使用“[Out]”属性。不能仅将 Blittable 数组封送为“[In]”。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf index 53c820a8e0a77c..fb2454e1a79930 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf @@ -437,6 +437,16 @@ 提供的圖表有週期,無法以拓撲排序。 + + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + The type '{0}' will be treated as a struct in the native signature, not as a native HRESULT. To treat this as an HRESULT, add '[return:MarshalAs(UnmanagedType.Error)]' to the method. + + + + This type will be treated as a struct in the native signature, not as a native HRESULT + This type will be treated as a struct in the native signature, not as a native HRESULT + + The '[In]' attribute is not supported unless the '[Out]' attribute is also used. Blittable arrays cannot be marshalled as '[In]' only. 除非也使用 '[Out]' 屬性,否則不支援 '[In]' 屬性。無法只將 Blittable 陣列整理為 '[In]'。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratorDiagnosticsBag.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratorDiagnosticsBag.cs index 2bc682244e259d..ba4f35d5e5fe91 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratorDiagnosticsBag.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratorDiagnosticsBag.cs @@ -128,5 +128,11 @@ public class GeneratorDiagnosticProperties /// The diagnostic can be resolved by adding the System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute to the assembly /// public const string AddDisableRuntimeMarshallingAttribute = nameof(AddDisableRuntimeMarshallingAttribute); + + /// + /// The diagnostic can be resolved by adding the System.Runtime.InteropServices.MarshalAsAttribute to the element with one of the UnmanagedType values + /// specified in the comma-separated list of values specified in the value of this property. + /// + public const string AddMarshalAsAttribute = nameof(AddMarshalAsAttribute); } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorFactory.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorFactory.cs index 4fc077035fa994..1aa0d96779f9c6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorFactory.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshalAsMarshallingGeneratorFactory.cs @@ -45,8 +45,8 @@ public ResolvedGenerator Create( or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Byte }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U1, _) } or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int16 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I2, _) } or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt16 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U2, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I4, _) } - or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U4, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I4 or UnmanagedType.Error, _) } + or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt32 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U4 or UnmanagedType.Error, _) } or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Int64 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.I8, _) } or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_UInt64 }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.U8, _) } or { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_IntPtr }, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.SysInt, _) } @@ -74,6 +74,7 @@ public ResolvedGenerator Create( case { ManagedType: PointerTypeInfo(_, _, IsFunctionPointer: true), MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: return ResolvedGenerator.Resolved(s_blittable); + // Bool with marshalling info case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.U1, _) }: return ResolvedGenerator.Resolved(s_byteBool); case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.I1, _) }: @@ -85,9 +86,11 @@ public ResolvedGenerator Create( case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Boolean }, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.VariantBool, _) }: return ResolvedGenerator.Resolved(s_variantBool); + // Delegate types case { ManagedType: DelegateTypeInfo, MarshallingAttributeInfo: NoMarshallingInfo or MarshalAsInfo(UnmanagedType.FunctionPtr, _) }: return ResolvedGenerator.Resolved(s_delegate); + // SafeHandle types with source-generator-emitted marshalling case { MarshallingAttributeInfo: SafeHandleMarshallingInfo(_, bool isAbstract) }: if (!context.AdditionalTemporaryStateLivesAcrossStages || context.Direction != MarshalDirection.ManagedToUnmanaged) { @@ -102,6 +105,7 @@ public ResolvedGenerator Create( } return ResolvedGenerator.Resolved(s_safeHandle); + // void case { ManagedType: SpecialTypeInfo { SpecialType: SpecialType.System_Void } }: return ResolvedGenerator.Resolved(s_forwarder); diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/PreserveSigTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/PreserveSigTests.cs index ee93d00ded7d20..006f903bc2c21b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/PreserveSigTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Tests/PreserveSigTests.cs @@ -23,7 +23,9 @@ public unsafe void CallRcwFromGeneratedComInterface() var expected = new Point(42, 63); - obj.SetPoint(expected); + var hr = obj.SetPoint(expected); + + Assert.Equal(0, hr.Value); Assert.Equal(expected, obj.GetPoint()); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/AddMarshalAsToElementTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/AddMarshalAsToElementTests.cs new file mode 100644 index 00000000000000..d7858a7fc75feb --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/AddMarshalAsToElementTests.cs @@ -0,0 +1,83 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.Interop; +using Xunit; + +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer, + Microsoft.Interop.Analyzers.AddMarshalAsToElementFixer>; + +namespace ComInterfaceGenerator.Unit.Tests +{ + public class AddMarshalAsToElementTests + { + [Fact] + public async Task ReturnHResultStruct_ReportsDiagnostic() + { + var source = """ + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + [GeneratedComInterface] + [Guid("0DB41042-0255-4CDD-B73A-9C5D5F31303D")] + partial interface I + { + [PreserveSig] + HResult {|#0:Method|}(); + } + + struct HResult + { + public int Value; + } + """; + + var fixedSource = """ + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + [GeneratedComInterface] + [Guid("0DB41042-0255-4CDD-B73A-9C5D5F31303D")] + partial interface I + { + [PreserveSig] + [return: MarshalAs(UnmanagedType.Error)] + HResult Method(); + } + + struct HResult + { + public int Value; + } + """; + + await VerifySourceGeneratorAsync(source, fixedSource, VerifyCS.Diagnostic(GeneratorDiagnostics.HResultTypeWillBeTreatedAsStruct).WithLocation(0).WithArguments("HResult")); + } + + private static Task VerifySourceGeneratorAsync(string source, string fixedSource, params DiagnosticResult[] diagnostics) + { + var test = new Test() + { + TestCode = source, + FixedCode = fixedSource, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck, + CodeFixTestBehaviors = CodeFixTestBehaviors.SkipFixAllCheck, // The Batch fixer doesn't work with the Roslyn SDK when the test is testing fixing diagnostics from a source generator (with no analyzers present) + }; + + test.ExpectedDiagnostics.AddRange(diagnostics); + + return test.RunAsync(); + } + + private sealed class Test : VerifyCS.Test + { + private static readonly ImmutableArray GeneratorTypes = ImmutableArray.Create(typeof(Microsoft.Interop.ComInterfaceGenerator)); + + protected override IEnumerable GetSourceGenerators() => GeneratorTypes; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs index a770ffffb0a401..2e4d25da3837e5 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs @@ -1063,7 +1063,8 @@ public async Task ValidateSizeParameterRefKindDiagnostics(string ID, string sour public static IEnumerable IntAndEnumReturnTypeSnippets() { - var diagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ComMethodManagedReturnWillBeOutVariable).WithLocation(0); + var managedReturnWillBeOutDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ComMethodManagedReturnWillBeOutVariable).WithLocation(0); + var hresultReturnStructWillBeStructDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.HResultTypeWillBeTreatedAsStruct).WithLocation(0); var enumDecl = $$""" internal enum Err { @@ -1090,22 +1091,22 @@ internal struct HResult yield return new object[] { ID(), enumReturn, - new DiagnosticResult[] { diagnostic } + new DiagnosticResult[] { managedReturnWillBeOutDiagnostic } }; yield return new object[] { ID(), intReturn, - new DiagnosticResult[] { diagnostic } + new DiagnosticResult[] { managedReturnWillBeOutDiagnostic } }; yield return new object[] { ID(), structHrReturn, - new DiagnosticResult[] { diagnostic } + new DiagnosticResult[] { managedReturnWillBeOutDiagnostic } }; yield return new object[] { ID(), structHResultReturn, - new DiagnosticResult[] { diagnostic } + new DiagnosticResult[] { managedReturnWillBeOutDiagnostic } }; yield return new object[] { ID(), @@ -1130,12 +1131,19 @@ internal struct HResult yield return new object[] { ID(), structHrPreserveSig, - new DiagnosticResult[] { } + new DiagnosticResult[] { hresultReturnStructWillBeStructDiagnostic.WithArguments("HR") } }; yield return new object[] { ID(), structHResultPreserveSig, - new DiagnosticResult[] { } + new DiagnosticResult[] { hresultReturnStructWillBeStructDiagnostic.WithArguments("HResult") } + }; + + var structHResultPreserveSigWithMarshalAs = Template("HResult", structDeclHResult, "[PreserveSig][return:MarshalAs(UnmanagedType.Error)]"); + yield return new object[] { + ID(), + structHResultPreserveSigWithMarshalAs, + new DiagnosticResult[] { } }; var intReturnMarshalAs = Template("int", "", "[return: MarshalAs(UnmanagedType.I4)]"); @@ -1161,9 +1169,10 @@ partial interface I """; } } + [Theory] [MemberData(nameof(IntAndEnumReturnTypeSnippets))] - async Task ValidateIntReturnTypeShowsInfo(string id, string source, DiagnosticResult[] diagnostics) + public async Task ValidateReturnTypeInfoDiagnostics(string id, string source, DiagnosticResult[] diagnostics) { _ = id; diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs index 29e827ef638bea..308cc66f48b9f8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs @@ -324,5 +324,51 @@ public partial interface J : I await VerifyCS.VerifyCodeFixAsync(source, fixedSource); } + + [Fact] + public async Task HResultLikeType_MarshalsAsError() + { + string source = """ + using System.Runtime.InteropServices; + + [ComImport] + [Guid("5DA39CDF-DCAD-447A-836E-EA80DB34D81B")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface [|I|] + { + [PreserveSig] + HResult Foo(); + } + + [StructLayout(LayoutKind.Sequential)] + public struct HResult + { + public int Value; + } + """; + + string fixedSource = """ + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + + [GeneratedComInterface] + [Guid("5DA39CDF-DCAD-447A-836E-EA80DB34D81B")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public partial interface I + { + [PreserveSig] + [return: MarshalAs(UnmanagedType.Error)] + HResult Foo(); + } + + [StructLayout(LayoutKind.Sequential)] + public struct HResult + { + public int Value; + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetPoint.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetPoint.cs index e9edf19b069687..ee9793d7d18c2e 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetPoint.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/NativeExports/ComInterfaceGenerator/GetPoint.cs @@ -52,7 +52,11 @@ private sealed class ImplementingObject : IPointProvider private Point _point; public Point GetPoint() => _point; - public void SetPoint(Point point) => _point = point; + public SharedTypes.ComInterfaces.HResult SetPoint(Point point) + { + _point = point; + return new SharedTypes.ComInterfaces.HResult { Value = 0 }; + } public static class ABI { diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs index a3d91318e5d146..e29b21b33a54a0 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs @@ -15,6 +15,14 @@ internal partial interface IPointProvider [PreserveSig] Point GetPoint(); - void SetPoint(Point point); + [PreserveSig] + [return:MarshalAs(UnmanagedType.Error)] + HResult SetPoint(Point point); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct HResult + { + public int Value; } }