Implement HResult swapping for generated COM methods. (#83348)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Wed, 15 Mar 2023 02:24:48 +0000 (19:24 -0700)
committerGitHub <noreply@github.com>
Wed, 15 Mar 2023 02:24:48 +0000 (19:24 -0700)
17 files changed:
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGeneratorHelpers.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Marshallers/ManagedHResultExceptionMarshallerFactory.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/UnmanagedToManagedStubGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/GeneratedStatements.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManagedToNativeStubCodeContext.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BlittableMarshaller.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/CharMarshaller.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/StaticPinnableManagedValueMarshaller.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/NativeToManagedStubCodeContext.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SignatureContext.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/StubCodeContext.cs
src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/VariableDeclarations.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs

index 195be3d..f773acb 100644 (file)
@@ -393,6 +393,47 @@ namespace Microsoft.Interop
             // Create the stub.
             var signatureContext = SignatureContext.Create(symbol, DefaultMarshallingInfoParser.Create(environment, generatorDiagnostics, symbol, new InteropAttributeCompilationData(), generatedComAttribute), environment, typeof(VtableIndexStubGenerator).Assembly);
 
+            // Search for the element information for the managed return value.
+            // We need to transform it such that any return type is converted to an out parameter at the end of the parameter list.
+            ImmutableArray<TypePositionInfo> returnSwappedSignatureElements = signatureContext.ElementTypeInformation;
+            for (int i = 0; i < returnSwappedSignatureElements.Length; ++i)
+            {
+                if (returnSwappedSignatureElements[i].IsManagedReturnPosition)
+                {
+                    if (returnSwappedSignatureElements[i].ManagedType == SpecialTypeInfo.Void)
+                    {
+                        // Return type is void, just remove the element from the signature list.
+                        // We don't introduce an out parameter.
+                        returnSwappedSignatureElements = returnSwappedSignatureElements.RemoveAt(i);
+                    }
+                    else
+                    {
+                        // Convert the current element into an out parameter on the native signature
+                        // while keeping it at the return position in the managed signature.
+                        var managedSignatureAsNativeOut = returnSwappedSignatureElements[i] with
+                        {
+                            RefKind = RefKind.Out,
+                            RefKindSyntax = SyntaxKind.OutKeyword,
+                            ManagedIndex = TypePositionInfo.ReturnIndex,
+                            NativeIndex = symbol.Parameters.Length
+                        };
+                        returnSwappedSignatureElements = returnSwappedSignatureElements.SetItem(i, managedSignatureAsNativeOut);
+                    }
+                    break;
+                }
+            }
+
+            signatureContext = signatureContext with
+            {
+                // Add the HRESULT return value in the native signature.
+                // This element does not have any influence on the managed signature, so don't assign a managed index.
+                ElementTypeInformation = returnSwappedSignatureElements.Add(
+                    new TypePositionInfo(SpecialTypeInfo.Int32, new ManagedHResultExceptionMarshallingInfo())
+                    {
+                        NativeIndex = TypePositionInfo.ReturnIndex
+                    })
+            };
+
             var containingSyntaxContext = new ContainingSyntaxContext(syntax);
 
             var methodSyntaxTemplate = new ContainingSyntax(syntax.Modifiers.StripTriviaFromTokens(), SyntaxKind.MethodDeclaration, syntax.Identifier, syntax.TypeParameterList);
index b560017..be826c0 100644 (file)
@@ -56,6 +56,8 @@ namespace Microsoft.Interop
                         ? MarshalMode.ManagedToUnmanagedOut
                         : MarshalMode.UnmanagedToManagedIn));
 
+            generatorFactory = new ManagedHResultExceptionMarshallerFactory(generatorFactory, direction);
+
             generatorFactory = new NativeToManagedThisMarshallerFactory(generatorFactory);
 
             generatorFactory = new ByValueContentsMarshalKindValidator(generatorFactory);
diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Marshallers/ManagedHResultExceptionMarshallerFactory.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Marshallers/ManagedHResultExceptionMarshallerFactory.cs
new file mode 100644 (file)
index 0000000..0ab4ede
--- /dev/null
@@ -0,0 +1,115 @@
+// 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;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Microsoft.Interop
+{
+    internal sealed record ManagedHResultExceptionMarshallingInfo : MarshallingInfo;
+
+    internal sealed class ManagedHResultExceptionMarshallerFactory : IMarshallingGeneratorFactory
+    {
+        private readonly IMarshallingGeneratorFactory _inner;
+        private readonly MarshalDirection _direction;
+
+        public ManagedHResultExceptionMarshallerFactory(IMarshallingGeneratorFactory inner, MarshalDirection direction)
+        {
+            if (direction is not (MarshalDirection.ManagedToUnmanaged or MarshalDirection.UnmanagedToManaged))
+            {
+                throw new ArgumentOutOfRangeException(nameof(direction));
+            }
+            _inner = inner;
+            _direction = direction;
+        }
+
+        public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext context)
+        {
+            if (info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo)
+            {
+                return _direction switch
+                {
+                    MarshalDirection.UnmanagedToManaged => new UnmanagedToManagedMarshaller(),
+                    MarshalDirection.ManagedToUnmanaged => new ManagedToUnmanagedMarshaller(),
+                    _ => throw new UnreachableException()
+                };
+            }
+            else
+            {
+                return _inner.Create(info, context);
+            }
+        }
+
+        private sealed class ManagedToUnmanagedMarshaller : IMarshallingGenerator
+        {
+            public ManagedTypeInfo AsNativeType(TypePositionInfo info) => info.ManagedType;
+            public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
+            {
+                Debug.Assert(info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo);
+
+                if (context.CurrentStage != StubCodeContext.Stage.Unmarshal)
+                {
+                    yield break;
+                }
+
+                (string managedIdentifier, _) = context.GetIdentifiers(info);
+
+                // Marshal.ThrowExceptionForHR(<managed>);
+                yield return ExpressionStatement(
+                    InvocationExpression(
+                        MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+                            ParseName(TypeNames.System_Runtime_InteropServices_Marshal),
+                            IdentifierName("ThrowExceptionForHR")),
+                        ArgumentList(
+                            SingletonSeparatedList(Argument(IdentifierName(managedIdentifier))))));
+            }
+
+            public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => SignatureBehavior.NativeType;
+            public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) => ValueBoundaryBehavior.ManagedIdentifier;
+            public bool IsSupported(TargetFramework target, Version version) => true;
+            public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
+            public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => false;
+        }
+
+        private sealed class UnmanagedToManagedMarshaller : IMarshallingGenerator
+        {
+            public ManagedTypeInfo AsNativeType(TypePositionInfo info) => info.ManagedType;
+            public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
+            {
+                Debug.Assert(info.MarshallingAttributeInfo is ManagedHResultExceptionMarshallingInfo);
+
+                if (context.CurrentStage != StubCodeContext.Stage.Unmarshal)
+                {
+                    yield break;
+                }
+
+                (string managedIdentifier, _) = context.GetIdentifiers(info);
+
+                //<managed> = 0; // S_OK
+                yield return ExpressionStatement(
+                    AssignmentExpression(
+                        SyntaxKind.SimpleAssignmentExpression,
+                        IdentifierName(managedIdentifier),
+                        LiteralExpression(
+                            SyntaxKind.NumericLiteralExpression,
+                            Literal(0))))
+                .WithSemicolonToken(
+                    Token(
+                        TriviaList(),
+                        SyntaxKind.SemicolonToken,
+                        TriviaList(
+                            Comment("// S_OK"))));
+            }
+
+            public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) => SignatureBehavior.NativeType;
+            public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) => ValueBoundaryBehavior.ManagedIdentifier;
+            public bool IsSupported(TargetFramework target, Version version) => true;
+            public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
+            public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => false;
+        }
+    }
+}
index d2ce04b..4d62b5d 100644 (file)
@@ -37,7 +37,7 @@ namespace Microsoft.Interop
                 marshallingNotSupportedCallback(failure.Info, failure.Exception);
             }
 
-            if (_marshallers.ManagedReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.ManagedReturnMarshaller.TypeInfo, _context))
+            if (_marshallers.NativeReturnMarshaller.Generator.UsesNativeIdentifier(_marshallers.NativeReturnMarshaller.TypeInfo, _context))
             {
                 // If we need a different native return identifier, then recreate the context with the correct identifier before we generate any code.
                 _context = new NativeToManagedStubCodeContext(targetFramework, targetFrameworkVersion, ReturnIdentifier, $"{ReturnIdentifier}{StubCodeContext.GeneratedNativeIdentifierSuffix}");
index e399f13..3bc1ecc 100644 (file)
@@ -108,10 +108,16 @@ namespace Microsoft.Interop
                 return ExpressionStatement(invoke);
             }
 
+            var (managed, native) = context.GetIdentifiers(marshallers.NativeReturnMarshaller.TypeInfo);
+
+            string targetIdentifier = marshallers.NativeReturnMarshaller.Generator.UsesNativeIdentifier(marshallers.NativeReturnMarshaller.TypeInfo, context)
+                ? native
+                : managed;
+
             return ExpressionStatement(
                     AssignmentExpression(
                         SyntaxKind.SimpleAssignmentExpression,
-                        IdentifierName(context.GetIdentifiers(marshallers.NativeReturnMarshaller.TypeInfo).native),
+                        IdentifierName(targetIdentifier),
                         invoke));
         }
 
index c953e24..1b5b01d 100644 (file)
@@ -15,6 +15,7 @@ namespace Microsoft.Interop
         private readonly Version _frameworkVersion;
 
         private const string InvokeReturnIdentifier = "__invokeRetVal";
+        private const string InvokeReturnIdentifierNative = "__invokeRetValUnmanaged";
         private readonly string _returnIdentifier;
         private readonly string _nativeReturnIdentifier;
 
@@ -47,12 +48,14 @@ namespace Microsoft.Interop
             // We can't use ReturnIdentifier or ReturnNativeIdentifier since that will be used by the managed return value.
             // Additionally, since all use cases today of a TypePositionInfo in the native position but not the managed
             // are for infos that aren't in the managed signature at all (PreserveSig scenario), we don't have a name
-            // that we can use from source. As a result, we generate another name for the native return value
+            // that we can use from source.
+            // If this changes, the assert below will trigger and we will need to decide what to do.
+            // As a result, we generate another name for the native return value
             // and use the same name for native and managed.
             else if (info.IsNativeReturnPosition)
             {
                 Debug.Assert(info.ManagedIndex == TypePositionInfo.UnsetIndex);
-                return (InvokeReturnIdentifier, InvokeReturnIdentifier);
+                return (InvokeReturnIdentifier, InvokeReturnIdentifierNative);
             }
             else
             {
index af93fb4..b06fcd8 100644 (file)
@@ -274,7 +274,7 @@ namespace Microsoft.Interop
                 new LinearCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, string.Empty, context));
 
             ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0));
-            if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
+            if (MarshallerHelpers.GetMarshalDirection(info, context) != MarshalDirection.ManagedToUnmanaged)
             {
                 // In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here.
                 numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, marshalInfo.ElementCountInfo, context);
index 78fb271..bb3b35b 100644 (file)
@@ -31,7 +31,7 @@ namespace Microsoft.Interop
             {
                 return ValueBoundaryBehavior.ManagedIdentifier;
             }
-            else if (context.SingleFrameSpansNativeContext && !info.IsManagedReturnPosition)
+            else if (context.SingleFrameSpansNativeContext && !context.IsInStubReturnPosition(info))
             {
                 return ValueBoundaryBehavior.NativeIdentifier;
             }
@@ -40,7 +40,7 @@ namespace Microsoft.Interop
 
         public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
         {
-            if (!info.IsByRef || info.IsManagedReturnPosition)
+            if (!info.IsByRef || context.IsInStubReturnPosition(info))
                 yield break;
 
             (string managedIdentifier, string nativeIdentifier) = context.GetIdentifiers(info);
@@ -100,7 +100,7 @@ namespace Microsoft.Interop
 
         public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
         {
-            return info.IsByRef && !info.IsManagedReturnPosition && !context.SingleFrameSpansNativeContext;
+            return info.IsByRef && !context.IsInStubReturnPosition(info) && !context.SingleFrameSpansNativeContext;
         }
 
         public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
index 63be014..970a11c 100644 (file)
@@ -125,7 +125,7 @@ namespace Microsoft.Interop
 
         public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context)
         {
-            return info.IsManagedReturnPosition || (info.IsByRef && !context.SingleFrameSpansNativeContext);
+            return context.IsInStubReturnPosition(info) || (info.IsByRef && !context.SingleFrameSpansNativeContext);
         }
 
         public bool SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, StubCodeContext context) => false;
@@ -133,7 +133,7 @@ namespace Microsoft.Interop
         private static bool IsPinningPathSupported(TypePositionInfo info, StubCodeContext context)
         {
             return context.SingleFrameSpansNativeContext
-                && !info.IsManagedReturnPosition
+                && !context.IsInStubReturnPosition(info)
                 && info.IsByRef;
         }
 
index 9fc17cf..2c11351 100644 (file)
@@ -50,16 +50,17 @@ namespace Microsoft.Interop
         private const string ParameterIdentifierSuffix = "param";
 
         /// <summary>
-        /// Gets a parameter for the unmanaged signature that represents the provided <paramref name="info"/>.
+        /// Gets a parameter for the unmanaged signature that represents the provided <paramref name="info"/> in the given <paramref name="context"/>.
         /// </summary>
         /// <param name="generator">The marshalling generator for this <paramref name="info"/></param>
         /// <param name="info">Object to marshal</param>
+        /// <param name="context">The stub marshalling context</param>
         public static ParameterSyntax AsParameter(this IMarshallingGenerator generator, TypePositionInfo info, StubCodeContext context)
         {
             SignatureBehavior behavior = generator.GetNativeSignatureBehavior(info);
             if (behavior == SignatureBehavior.ManagedTypeAndAttributes)
             {
-                return GenerateForwardingParameter(info);
+                return GenerateForwardingParameter(info, context.GetIdentifiers(info).managed);
             }
             string identifierName;
             if (context.Direction == MarshalDirection.ManagedToUnmanaged)
@@ -99,9 +100,9 @@ namespace Microsoft.Interop
                 });
         }
 
-        private static ParameterSyntax GenerateForwardingParameter(TypePositionInfo info)
+        private static ParameterSyntax GenerateForwardingParameter(TypePositionInfo info, string identifier)
         {
-            ParameterSyntax param = Parameter(Identifier(info.InstanceIdentifier))
+            ParameterSyntax param = Parameter(Identifier(identifier))
                 .WithModifiers(TokenList(Token(info.RefKindSyntax)))
                 .WithType(info.ManagedType.Syntax);
 
index 91d43dc..3708fd9 100644 (file)
@@ -78,7 +78,7 @@ namespace Microsoft.Interop
         }
         private static bool IsPinningPathSupported(TypePositionInfo info, StubCodeContext context)
         {
-            return context.SingleFrameSpansNativeContext && !info.IsByRef && !info.IsManagedReturnPosition;
+            return context.SingleFrameSpansNativeContext && !info.IsByRef && !context.IsInStubReturnPosition(info);
         }
 
         private IEnumerable<StatementSyntax> GeneratePinningPath(TypePositionInfo info, StubCodeContext context)
index 64b16d9..3dbd182 100644 (file)
@@ -55,13 +55,12 @@ namespace Microsoft.Interop
             }
             // If the info is in the managed return position but is not in the native return position,
             // then that means that the stub is introducing an additional info for the return position.
-            // This means that there is no name in source for this info, so we must provide one here.
+            // This element can be in any position in the native signature,
+            // but since it isn't in the managed signature, there is no name in source for this info, so we must provide one here.
             // We can't use ReturnIdentifier or ReturnNativeIdentifier since that will be used by the return value of the stub itself.
-            // Additionally, we don't have a name that we can use from source.
             // As a result, we generate another name for the native return value.
             if (info.IsManagedReturnPosition)
             {
-                Debug.Assert(info.NativeIndex == TypePositionInfo.UnsetIndex);
                 return (InvokeReturnIdentifier, InvokeReturnIdentifierNative);
             }
 
index ce4cab5..d5d7526 100644 (file)
@@ -123,7 +123,7 @@ namespace Microsoft.Interop
             retTypeInfo = retTypeInfo with
             {
                 ManagedIndex = TypePositionInfo.ReturnIndex,
-                NativeIndex = TypePositionInfo.ReturnIndex
+                NativeIndex = TypePositionInfo.ReturnIndex,
             };
 
             typeInfos.Add(retTypeInfo);
index fe10c97..35e07f1 100644 (file)
@@ -138,5 +138,24 @@ namespace Microsoft.Interop
         {
             return $"{GetIdentifiers(info).native}__{name}";
         }
+
+        /// <summary>
+        /// Compute if the provided element is the return element for the stub that is being generated (not any inner call).
+        /// </summary>
+        /// <param name="info">The element information</param>
+        /// <returns><c>true</c> if the element is in the return position for this stub; otherwise, false.</returns>
+        public bool IsInStubReturnPosition(TypePositionInfo info)
+        {
+            if (Direction == MarshalDirection.ManagedToUnmanaged)
+            {
+                return info.IsManagedReturnPosition;
+            }
+            else if (Direction == MarshalDirection.UnmanagedToManaged)
+            {
+                return info.IsNativeReturnPosition;
+            }
+
+            throw new InvalidOperationException("Stub contexts should not be bidirectional");
+        }
     }
 }
index aa080c3..513784d 100644 (file)
@@ -37,13 +37,13 @@ namespace Microsoft.Interop
             }
 
             // Stub return is not the same as invoke return
-            if (!marshallers.IsManagedVoidReturn && !marshallers.ManagedNativeSameReturn)
+            if (!marshallers.IsManagedVoidReturn)
             {
                 // Declare variables for stub return value
                 AppendVariableDeclarations(variables, marshallers.ManagedReturnMarshaller, context, initializeToDefault: initializeDeclarations);
             }
 
-            if (!marshallers.IsManagedVoidReturn)
+            if (!marshallers.IsUnmanagedVoidReturn && !marshallers.ManagedNativeSameReturn)
             {
                 // Declare variables for invoke return value
                 AppendVariableDeclarations(variables, marshallers.NativeReturnMarshaller, context, initializeToDefault: initializeDeclarations);
@@ -87,21 +87,20 @@ namespace Microsoft.Interop
             foreach (BoundGenerator marshaller in marshallers.NativeParameterMarshallers)
             {
                 TypePositionInfo info = marshaller.TypeInfo;
-                if (info.IsNativeReturnPosition)
+                if (info.IsNativeReturnPosition || info.IsManagedReturnPosition)
                     continue;
 
                 // Declare variables for parameters
                 AppendVariableDeclarations(variables, marshaller, context, initializeToDefault: initializeDeclarations);
             }
 
-            // Stub return is not the same as invoke return
-            if (!marshallers.IsManagedVoidReturn && !marshallers.ManagedNativeSameReturn)
+            if (!marshallers.IsManagedVoidReturn)
             {
                 // Declare variables for stub return value
                 AppendVariableDeclarations(variables, marshallers.ManagedReturnMarshaller, context, initializeToDefault: initializeDeclarations);
             }
 
-            if (!marshallers.IsManagedVoidReturn)
+            if (!marshallers.IsUnmanagedVoidReturn && !marshallers.ManagedNativeSameReturn)
             {
                 // Declare variables for invoke return value
                 AppendVariableDeclarations(variables, marshallers.NativeReturnMarshaller, context, initializeToDefault: initializeDeclarations);
@@ -136,21 +135,6 @@ namespace Microsoft.Interop
                             initializeToDefault: true));
                     }
                 }
-                else if (marshaller.TypeInfo.IsManagedReturnPosition)
-                {
-                    statementsToUpdate.Add(MarshallerHelpers.Declare(
-                        marshaller.TypeInfo.ManagedType.Syntax,
-                        managed,
-                        initializeToDefault));
-
-                    if (marshaller.Generator.UsesNativeIdentifier(marshaller.TypeInfo, context))
-                    {
-                        statementsToUpdate.Add(MarshallerHelpers.Declare(
-                            marshaller.Generator.AsNativeType(marshaller.TypeInfo).Syntax,
-                            native,
-                            initializeToDefault));
-                    }
-                }
                 else
                 {
                     ValueBoundaryBehavior boundaryBehavior = marshaller.Generator.GetValueBoundaryBehavior(marshaller.TypeInfo, context);
index 0553592..be35404 100644 (file)
@@ -184,8 +184,10 @@ partial interface INativeAPI
     {VirtualMethodIndex(0)}
     [return:MarshalUsing(ConstantElementCount=10)]
     {collectionType} Method(
-        {collectionType} p,
-        in {collectionType} pIn,
+        [MarshalUsing(CountElementName = ""pSize"")] {collectionType} p,
+        int pSize,
+        [MarshalUsing(CountElementName = ""pInSize"")] in {collectionType} pIn,
+        in int pInSize,
         int pRefSize,
         [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
         [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
index 97b73a1..ac3d5b0 100644 (file)
@@ -125,8 +125,10 @@ partial interface INativeAPI
     {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
     [return:MarshalUsing(ConstantElementCount=10)]
     {collectionType} Method(
-        {collectionType} p,
-        in {collectionType} pIn,
+        [MarshalUsing(CountElementName = ""pSize"")] {collectionType} p,
+        int pSize,
+        [MarshalUsing(CountElementName = ""pInSize"")] in {collectionType} pIn,
+        in int pInSize,
         int pRefSize,
         [MarshalUsing(CountElementName = ""pRefSize"")] ref {collectionType} pRef,
         [MarshalUsing(CountElementName = ""pOutSize"")] out {collectionType} pOut,
@@ -146,8 +148,10 @@ partial interface INativeAPI
     {AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}
     [return:MarshalUsing(typeof({marshallerType}), ConstantElementCount=10)]
     {collectionType} Method(
-        [MarshalUsing(typeof({marshallerType}))] {collectionType} p,
-        [MarshalUsing(typeof({marshallerType}))] in {collectionType} pIn,
+        [MarshalUsing(typeof({marshallerType}), CountElementName = ""pSize"")] {collectionType} p,
+        [MarshalUsing(typeof({marshallerType}), CountElementName = ""pInSize"")] in {collectionType} pIn,
+        int pSize,
+        in int pInSize,
         int pRefSize,
         [MarshalUsing(typeof({marshallerType}), CountElementName = ""pRefSize"")] ref {collectionType} pRef,
         [MarshalUsing(typeof({marshallerType}), CountElementName = ""pOutSize"")] out {collectionType} pOut,
@@ -222,8 +226,10 @@ partial interface INativeAPI
     [return:MarshalUsing(ConstantElementCount=10)]
     [return:MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)]
     TestCollection<int> Method(
-        [MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] {collectionType} p,
-        [MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] in {collectionType} pIn,
+        [MarshalUsing(CountElementName = ""pSize""), MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] {collectionType} p,
+        [MarshalUsing(CountElementName = ""pInSize""), MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] in {collectionType} pIn,
+        int pSize,
+        in int pInSize,
         int pRefSize,
         [MarshalUsing(CountElementName = ""pRefSize""), MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] ref {collectionType} pRef,
         [MarshalUsing(CountElementName = ""pOutSize"")][MarshalUsing(typeof({elementMarshaller}), ElementIndirectionDepth = 1)] out {collectionType} pOut,