Add PInvoke/SizeParamIndex tests (#19348)
authorZeng Jiang <v-jiazen@microsoft.com>
Fri, 16 Nov 2018 00:31:38 +0000 (08:31 +0800)
committerJeremy Koritzinsky <jkoritzinsky@gmail.com>
Fri, 16 Nov 2018 00:31:38 +0000 (16:31 -0800)
* Add PInvoke/SizeParamIndex tests

* Fix compile warnings

* First pass cleaning up native code.

* Get ParamSizeIndex tests working xplat (excluding the BSTR array tests)

* Fix incorrect definition of ULONG off-windows.

* Disable reverse-PInvoke throwing test off-Windows.

23 files changed:
tests/src/Common/Platform/platformdefines.h
tests/src/Interop/CMakeLists.txt
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PInvokePassingByOutNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PassingByOutTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PassingByOutTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PInvokePassingByRefNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PassingByRefTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PassingByRefTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/helper.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/helper.h [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/PassingByOutTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/PassingByOutTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/ReversePInvokePassingByOutNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/PassingByRefTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/PassingByRefTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/ReversePInvokePassingByRefNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/helper.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/helper.h [new file with mode: 0644]
tests/src/Interop/common/types.h

index 1cc334c..e1e84b6 100644 (file)
@@ -39,7 +39,7 @@ typedef const WCHAR *LPCWSTR, *PCWSTR;
 typedef int HRESULT;
 #define LONGLONG long long
 #define ULONGLONG unsigned LONGLONG
-typedef unsigned long ULONG, *PULONG;
+typedef unsigned int ULONG, *PULONG;
 #define S_OK                    0x0
 #define SUCCEEDED(_hr)          ((HRESULT)(_hr) >= 0)
 #define FAILED(_hr)             ((HRESULT)(_hr) < 0)
index 9825873..5d9c27b 100644 (file)
@@ -18,6 +18,10 @@ add_subdirectory(PInvoke/BestFitMapping/LPStr)
 add_subdirectory(PInvoke/Delegate/MarshalDelegateAsField)
 add_subdirectory(PInvoke/Delegate/MarshalDelegateAsParam)
 add_subdirectory(PInvoke/Primitives/Int)
+add_subdirectory(PInvoke/SizeParamIndex/PInvoke/PassingByOut)
+add_subdirectory(PInvoke/SizeParamIndex/PInvoke/PassingByRef)
+add_subdirectory(PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut)
+add_subdirectory(PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef)
 add_subdirectory(PInvoke/Array/MarshalArrayAsField/LPArrayNative)
 add_subdirectory(PInvoke/Array/MarshalArrayAsParam/LPArrayNative)
 add_subdirectory(PInvoke/Miscellaneous/HandleRef)
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/CMakeLists.txt b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/CMakeLists.txt
new file mode 100644 (file)
index 0000000..90058da
--- /dev/null
@@ -0,0 +1,13 @@
+cmake_minimum_required (VERSION 2.6) 
+project (PInvokePassingByOutNative) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+include_directories("..")
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    PInvokePassingByOutNative.cpp 
+)
+# add the executable 
+add_library (PInvokePassingByOutNative SHARED ${SOURCES}) 
+target_link_libraries(PInvokePassingByOutNative ${LINK_LIBRARIES_ADDITIONAL}) 
+# add the install targets 
+install (TARGETS PInvokePassingByOutNative DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PInvokePassingByOutNative.cpp b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PInvokePassingByOutNative.cpp
new file mode 100644 (file)
index 0000000..5b3e9b6
--- /dev/null
@@ -0,0 +1,100 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// PInvokePassingByOutNative.cpp : Defines the entry point for the DLL application.
+//
+#include <xplatform.h>
+#include <limits.h>
+#include "platformdefines.h"
+#include "helper.h"
+
+//#####################################################################
+//ByOut Array, ByRef SizeParamIndex
+//#####################################################################
+
+//BYTE 0 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayByte_AsByOut_AsSizeParamIndex(BYTE* arrSize, BYTE** ppActual)
+{
+    return CheckAndChangeArrayByOut(ppActual, arrSize, (BYTE)1);
+}
+
+//CHAR 1 ==> CHAR.Max size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArraySbyte_AsByOut_AsSizeParamIndex(CHAR* arrSize, CHAR** ppActual)
+{
+    return CheckAndChangeArrayByOut(ppActual, arrSize, (CHAR)SCHAR_MAX); 
+}
+
+//SHORT -1 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayShort_AsByOut_AsSizeParamIndex(/*out*/SHORT* arrSize, SHORT** ppActual)
+{
+    short shortArray_Size = 16384;//SHRT_MAX+1/2
+
+    *ppActual = (SHORT*)CoreClrAlloc(sizeof(SHORT)*shortArray_Size);
+
+    *arrSize = shortArray_Size;
+
+    for(SHORT i = 0; i < shortArray_Size; ++i)
+    {
+        (*ppActual)[i] = shortArray_Size - 1 - i;
+    }
+    return TRUE;
+}
+
+//SHORT 10 ==> -1 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayShortReturnNegative_AsByOut_AsSizeParamIndex(SHORT* arrSize, SHORT** ppActual)
+{
+    *ppActual = (SHORT*)CoreClrAlloc(sizeof(SHORT)*CArray_Size);
+    *arrSize = -1;
+
+    for(SHORT i = 0; i < CArray_Size; ++i)
+    {
+        (*ppActual)[i] = CArray_Size - 1 - i;
+    }
+    return TRUE;
+}
+
+//USHORT ? ==> ushort.Max ==>  size Array 
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayUshort_AsByOut_AsSizeParamIndex(USHORT** ppActual, USHORT* arrSize)
+{
+    return CheckAndChangeArrayByOut(ppActual, arrSize, (USHORT)USHRT_MAX);
+}
+
+//Int32 ? ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayInt_AsByOut_AsSizeParamIndex(LONG* arrSize,LONG** ppActual)
+{
+    return CheckAndChangeArrayByOut(ppActual, arrSize, (LONG)0);
+}
+
+//ULONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayUInt_AsByOut_AsSizeParamIndex(ULONG* arrSize, ULONG** ppActual)
+{
+    return CheckAndChangeArrayByOut(ppActual, arrSize, (ULONG)20);
+}
+
+//LONGLONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayLong_AsByOut_AsSizeParamIndex(LONGLONG* arrSize, LONGLONG** ppActual)
+{
+    return CheckAndChangeArrayByOut(ppActual, arrSize, (LONGLONG)20);
+}
+
+//ULONGLONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayUlong_AsByOut_AsSizeParamIndex(ULONGLONG** ppActual,ULONGLONG* arrSize,ULONGLONG _unused)
+{
+    return CheckAndChangeArrayByOut(ppActual, arrSize, (ULONGLONG)1000);
+}
+#ifdef _WIN32
+//String 10 size Array ==> BSTR 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayString_AsByOut_AsSizeParamIndex(BSTR** ppBSTR,short* arrSize)
+{
+    *ppBSTR = (BSTR*)CoreClrAlloc(sizeof(BSTR)*CArray_Size);
+    for(int i = 0;i<CArray_Size;++i)
+    {
+        (*ppBSTR)[i] = ToBSTR(CArray_Size - 1 - i);
+    }
+
+    *arrSize = CArray_Size;
+
+    return TRUE;
+}
+#endif
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PassingByOutTest.cs b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PassingByOutTest.cs
new file mode 100644 (file)
index 0000000..555ed11
--- /dev/null
@@ -0,0 +1,257 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+/// <summary>
+///  Pass Array Size by out keyword using SizeParamIndex Attributes
+/// </summary>
+public class ClientMarshalArrayAsSizeParamIndexByOutTest
+{
+
+    #region ByOut
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayByte_AsByOut_AsSizeParamIndex(
+        out byte arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out byte[] arrByte);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArraySbyte_AsByOut_AsSizeParamIndex(
+        out sbyte arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out sbyte[] arrSbyte);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayShort_AsByOut_AsSizeParamIndex(
+        out short arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out short[] arrShort);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayShortReturnNegative_AsByOut_AsSizeParamIndex(
+        out short arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out short[] arrShort);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayUshort_AsByOut_AsSizeParamIndex(
+        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out ushort[] arrUshort, out ushort arrSize);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayInt_AsByOut_AsSizeParamIndex(
+        out Int32 arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out Int32[] arrInt32);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayUInt_AsByOut_AsSizeParamIndex(
+        out UInt32 arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out UInt32[] arrUInt32);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayLong_AsByOut_AsSizeParamIndex(
+        out long arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out long[] arrLong);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayUlong_AsByOut_AsSizeParamIndex(
+         [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out ulong[] arrUlong, out ulong arrSize, ulong unused);
+
+    [DllImport("PInvokePassingByOutNative")]
+    private static extern bool MarshalCStyleArrayString_AsByOut_AsSizeParamIndex(
+    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.BStr)] out string[] arrInt32, out int arrSize);
+
+    #endregion
+
+    static void SizeParamTypeIsByte()
+    {
+        string strDescription = "Scenario(byte ==> BYTE): Array_Size(N->M) = 1";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        byte byte_Array_Size;
+        byte[] arrByte;
+        Assert.IsTrue(MarshalCStyleArrayByte_AsByOut_AsSizeParamIndex(out byte_Array_Size, out arrByte));
+
+        //Construct Expected array
+        int expected_ByteArray_Size = 1;
+        byte[] expectedArrByte = Helper.GetExpChangeArray<byte>(expected_ByteArray_Size);
+        Assert.IsTrue(Helper.EqualArray<byte>(arrByte, (int)byte_Array_Size, expectedArrByte, (int)expectedArrByte.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsSByte()
+    {
+        string strDescription = "Scenario(sbyte ==> CHAR):Array_Size(N->M) = sbyte.Max";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        sbyte sbyte_Array_Size;
+        sbyte[] arrSbyte;
+        Assert.IsTrue(MarshalCStyleArraySbyte_AsByOut_AsSizeParamIndex(out sbyte_Array_Size, out arrSbyte));
+
+        sbyte[] expectedArrSbyte = Helper.GetExpChangeArray<sbyte>(sbyte.MaxValue);
+        Assert.IsTrue(Helper.EqualArray<sbyte>(arrSbyte, (int)sbyte_Array_Size, expectedArrSbyte, (int)expectedArrSbyte.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsShort1()
+    {
+        string strDescription = "Scenario(short ==> SHORT)1,Array_Size(M->N) = -1, Array_Size(N->M)=(ShortMax+1)/2";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        short shortArray_Size = (short)-1;
+        short[] arrShort = Helper.InitArray<short>(10);
+        Assert.IsTrue(MarshalCStyleArrayShort_AsByOut_AsSizeParamIndex(out shortArray_Size, out arrShort));
+
+        //Construct Expected Array
+        int expected_ShortArray_Size = 16384;//(SHRT_MAX+1)/2
+        short[] expectedArrShort = Helper.GetExpChangeArray<short>(expected_ShortArray_Size);
+        Assert.IsTrue(Helper.EqualArray<short>(arrShort, (int)shortArray_Size, expectedArrShort, (int)expectedArrShort.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsShort2()
+    {
+        string strDescription = "Scenario(short ==> SHORT)2, Array_Size = 10, Array_Size(N->M) = -1";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        short short_Array_Size = (short)10;
+        short[] arrShort = Helper.InitArray<short>(short_Array_Size);
+        Assert.Throws<OverflowException>(() => MarshalCStyleArrayShortReturnNegative_AsByOut_AsSizeParamIndex(out short_Array_Size, out arrShort));
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsUShort()
+    {
+        string strDescription = "Scenario(ushort==>USHORT): Array_Size(N->M) = ushort.MaxValue";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        ushort ushort_Array_Size;
+        ushort[] arrUshort;
+        Assert.IsTrue(MarshalCStyleArrayUshort_AsByOut_AsSizeParamIndex(out arrUshort, out ushort_Array_Size));
+
+        //Expected Array
+        ushort[] expectedArrUshort = Helper.GetExpChangeArray<ushort>(ushort.MaxValue);
+        Assert.IsTrue(Helper.EqualArray<ushort>(arrUshort, (int)ushort_Array_Size, expectedArrUshort, (ushort)expectedArrUshort.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsInt32()
+    {
+        string strDescription = "Scenario(Int32 ==> LONG): Array_Size(N->M) = 0 ";
+
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        Int32 Int32_Array_Size;
+        Int32[] arrInt32;
+        Assert.IsTrue(MarshalCStyleArrayInt_AsByOut_AsSizeParamIndex(out Int32_Array_Size, out arrInt32));
+
+        //Expected Array
+        Int32[] expectedArrInt32 = Helper.GetExpChangeArray<Int32>(0);
+        Assert.IsTrue(Helper.EqualArray<Int32>(arrInt32, Int32_Array_Size, expectedArrInt32, expectedArrInt32.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsUInt32()
+    {
+        string strDescription = "Scenario(UInt32 ==> ULONG): Array_Size(N->M) = 20";
+
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        int expected_UInt32ArraySize = 20;
+
+        UInt32 UInt32_Array_Size = (UInt32)10;
+        UInt32[] arrUInt32 = Helper.InitArray<UInt32>((Int32)UInt32_Array_Size);
+        Assert.IsTrue(MarshalCStyleArrayUInt_AsByOut_AsSizeParamIndex(out UInt32_Array_Size, out arrUInt32));
+
+        //Construct expected
+        UInt32[] expectedArrUInt32 = Helper.GetExpChangeArray<UInt32>(expected_UInt32ArraySize);
+        Assert.IsTrue(Helper.EqualArray<UInt32>(arrUInt32, (Int32)UInt32_Array_Size, expectedArrUInt32, (Int32)expectedArrUInt32.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsLong()
+    {
+        string strDescription = "Scenario(long ==> LONGLONG): Array_Size(N->M) = 20";
+
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        int expected_LongArraySize = 20;
+
+        long long_Array_Size = (long)10;
+        long[] arrLong = Helper.InitArray<long>((Int32)long_Array_Size);
+        Assert.IsTrue(MarshalCStyleArrayLong_AsByOut_AsSizeParamIndex(out long_Array_Size, out arrLong));
+
+        long[] expectedArrLong = Helper.GetExpChangeArray<long>(expected_LongArraySize);
+        Assert.IsTrue(Helper.EqualArray<long>(arrLong, (Int32)long_Array_Size, expectedArrLong, (Int32)expectedArrLong.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsULong()
+    {
+        string strDescription = "Scenario(ulong ==> ULONGLONG): Array_Size(N->M) = 1000";
+
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        int expected_ULongArraySize = 1000;
+
+        ulong ulong_Array_Size = (ulong)10;
+        ulong[] arrUlong = Helper.InitArray<ulong>((Int32)ulong_Array_Size);
+        Assert.IsTrue(MarshalCStyleArrayUlong_AsByOut_AsSizeParamIndex(out arrUlong, out ulong_Array_Size, ulong_Array_Size));
+
+        ulong[] expectedArrUlong = Helper.GetExpChangeArray<ulong>(expected_ULongArraySize);
+        Assert.IsTrue(Helper.EqualArray<ulong>(arrUlong, (Int32)ulong_Array_Size, expectedArrUlong, (Int32)expectedArrUlong.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsString()
+    {
+        string strDescription = "Scenario(String ==> BSTR): Array_Size(N->M) = 20";
+
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        int expected_StringArraySize = 20;
+        int string_Array_Size = 10;
+        String[] arrString = Helper.InitArray<String>(string_Array_Size);
+        Assert.IsTrue(MarshalCStyleArrayString_AsByOut_AsSizeParamIndex(out arrString, out string_Array_Size));
+
+        String[] expArrString = Helper.GetExpChangeArray<String>(expected_StringArraySize);
+        Assert.IsTrue(Helper.EqualArray<String>(arrString, string_Array_Size, expArrString, expArrString.Length));
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static int Main()
+    {
+        try{
+            SizeParamTypeIsByte();
+            SizeParamTypeIsSByte();
+            SizeParamTypeIsShort1();
+            SizeParamTypeIsShort2();
+            SizeParamTypeIsUShort();
+            SizeParamTypeIsInt32();
+            SizeParamTypeIsUInt32();
+            SizeParamTypeIsLong();
+            SizeParamTypeIsULong();
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                SizeParamTypeIsString();
+            }
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+}
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PassingByOutTest.csproj b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByOut/PassingByOutTest.csproj
new file mode 100644 (file)
index 0000000..5344935
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>PassingByOutTest</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PassingByOutTest.cs" />
+    <Compile Include="..\helper.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="../../../../Interop.settings.targets" />
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/CMakeLists.txt b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f3349b1
--- /dev/null
@@ -0,0 +1,13 @@
+cmake_minimum_required (VERSION 2.6) 
+project (PInvokePassingByRefNative) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+include_directories("..")
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    PInvokePassingByRefNative.cpp 
+) 
+# add the executable 
+add_library (PInvokePassingByRefNative SHARED ${SOURCES}) 
+target_link_libraries(PInvokePassingByRefNative ${LINK_LIBRARIES_ADDITIONAL}) 
+# add the install targets 
+install (TARGETS PInvokePassingByRefNative DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PInvokePassingByRefNative.cpp b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PInvokePassingByRefNative.cpp
new file mode 100644 (file)
index 0000000..e0ac16e
--- /dev/null
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// PInvokePassingByRefNative.cpp : Defines the entry point for the DLL application.
+//
+#include <xplatform.h>
+#include <limits.h>
+#include "helper.h"
+
+//#####################################################################
+//ByRef Array, ByRef SizeParamIndex
+//#####################################################################
+
+//BYTE 1 ==> 0 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayByte_AsByRef_AsSizeParamIndex(BYTE* arrSize, BYTE** ppActual)
+{
+    return CheckAndChangeArrayByRef(ppActual, arrSize, (BYTE)1, (BYTE)0);
+}
+
+//CHAR 10 ==> CHAR.Max size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArraySbyte_AsByRef_AsSizeParamIndex(CHAR* arrSize, CHAR** ppActual)
+{
+    return CheckAndChangeArrayByRef(ppActual, arrSize, (CHAR)10, (CHAR)CHAR_MAX); 
+}
+
+//SHORT -1 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayShort_AsByRef_AsSizeParamIndex(SHORT* arrSize, SHORT** ppActual)
+{
+    if(*arrSize != -1)
+    {
+        printf("%s,ManagedtoNative Error!\n",__FUNCTION__);
+        printf("arrSize != -1");
+        return FALSE;
+    }
+
+    SHORT* pExpectedArr = InitArray<SHORT>((SHORT)Array_Size);
+
+    if(!EqualArray(*ppActual, (SHORT)Array_Size, pExpectedArr, (SHORT)Array_Size))
+    {
+        printf("%s,ManagedtoNative Error!\n",__FUNCTION__);
+        return FALSE;
+    }
+
+    CoreClrFree(*ppActual);
+    *ppActual = (SHORT*)CoreClrAlloc(sizeof(SHORT)*CArray_Size);
+
+    *arrSize = CArray_Size;
+
+    for(SHORT i = 0; i < CArray_Size; ++i)
+    {
+        (*ppActual)[i] = CArray_Size - 1 - i;
+    }
+    return TRUE;
+}
+
+//SHORT 10 ==> -1 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayShortReturnNegative_AsByRef_AsSizeParamIndex(SHORT* arrSize, SHORT** ppActual)
+{
+    SHORT* pExpectedArr = InitArray<SHORT>((SHORT)Array_Size);
+
+    if(!EqualArray(*ppActual, (SHORT)Array_Size, pExpectedArr, (SHORT)Array_Size))
+    {
+        printf("%s,ManagedtoNative Error!\n",__FUNCTION__);
+        return FALSE;
+    }
+
+    CoreClrFree(*ppActual);
+    *ppActual = (SHORT*)CoreClrAlloc(sizeof(SHORT)*CArray_Size);
+
+    *arrSize = (SHORT)-1;
+
+    for(SHORT i = 0; i < CArray_Size; ++i)
+    {
+        (*ppActual)[i] = CArray_Size - 1 - i;
+    }
+    return TRUE;
+}
+
+//USHORT 20 ==> ushort.Max  size Array 
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayUshort_AsByRef_AsSizeParamIndex(USHORT** ppActual, USHORT* arrSize)
+{
+    return CheckAndChangeArrayByRef(ppActual, arrSize, (USHORT)20, (USHORT)65535);
+}
+
+//Int32 10 ==> 1 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayInt_AsByRef_AsSizeParamIndex(LONG* arrSize,LONG unused,LONG** ppActual)
+{
+    return CheckAndChangeArrayByRef(ppActual, arrSize, (LONG)10, (LONG)1);
+}
+
+//ULONG 1234 ==> 4321 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayUInt_AsByRef_AsSizeParamIndex(ULONG** ppActual,ULONG unused, ULONG* arrSize)
+{
+    return CheckAndChangeArrayByRef(ppActual, arrSize, (ULONG)1234, (ULONG)4321);
+}
+
+//LONGLONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayLong_AsByRef_AsSizeParamIndex(LONGLONG* arrSize, LONGLONG** ppActual)
+{
+    return CheckAndChangeArrayByRef(ppActual, arrSize, (LONGLONG)10, (LONGLONG)20);
+}
+
+//ULONGLONG 0 ==> 0 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayUlong_AsByRef_AsSizeParamIndex(ULONGLONG* arrSize, ULONGLONG** ppActual)
+{
+    return CheckAndChangeArrayByRef(ppActual, arrSize, (ULONGLONG)0, (ULONGLONG)0);
+}
+#ifdef _WIN32
+//String size Array 20 ==> BSTR 10 size Array
+extern "C" DLL_EXPORT BOOL __stdcall MarshalCStyleArrayString_AsByRef_AsSizeParamIndex(short* arrSize, BSTR** ppBSTR,char *** pppStr)
+{
+    BSTR* pExpectedArr = InitArrayBSTR(20);
+
+    if(!EqualArrayBSTR(*ppBSTR,*arrSize,pExpectedArr,20))
+    {
+        printf("%s,ManagedtoNative Error!\n",__FUNCTION__);
+        return FALSE;
+    }
+
+    CoreClrFree(*ppBSTR);
+
+    *ppBSTR = (BSTR*)CoreClrAlloc(sizeof(BSTR)*10);
+    for(int i = 0;i<10;++i)
+    {
+        (*ppBSTR)[i] = ToBSTR(10 - 1 - i);
+    }
+
+    *arrSize = 10;
+
+    return TRUE;
+}
+#endif
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PassingByRefTest.cs b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PassingByRefTest.cs
new file mode 100644 (file)
index 0000000..c562e13
--- /dev/null
@@ -0,0 +1,265 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+/// <summary>
+///  Pass LPArray Size by ref keyword using SizeParamIndex Attributes
+/// </summary>
+
+public class ClientMarshalArrayAsSizeParamIndexByRefTest
+{
+
+    #region ByRef
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayByte_AsByRef_AsSizeParamIndex(
+        ref byte arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref byte[] arrByte);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArraySbyte_AsByRef_AsSizeParamIndex(
+        ref sbyte arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref sbyte[] arrSbyte);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayShort_AsByRef_AsSizeParamIndex(
+        ref short arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref short[] arrShort);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayShortReturnNegative_AsByRef_AsSizeParamIndex(
+        ref short arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref short[] arrShort);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayUshort_AsByRef_AsSizeParamIndex(
+        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref ushort[] arrUshort, ref ushort arrSize);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayInt_AsByRef_AsSizeParamIndex(
+        ref Int32 arrSize, Int32 unused, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref Int32[] arrInt32);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayUInt_AsByRef_AsSizeParamIndex(
+         [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] ref UInt32[] arrUInt32, UInt32 unused, ref UInt32 arrSize);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayLong_AsByRef_AsSizeParamIndex(
+        ref long arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref long[] arrLong);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayUlong_AsByRef_AsSizeParamIndex(
+        ref ulong arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref ulong[] arrUlong);
+
+    [DllImport("PInvokePassingByRefNative")]
+    private static extern bool MarshalCStyleArrayString_AsByRef_AsSizeParamIndex(
+        ref int arrSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.BStr)] ref string[] arrStr,
+        [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.LPStr)] ref string[] arrStr2);
+
+    #endregion
+
+    static void SizeParamTypeIsByte()
+    {
+        string strDescription = "Scenario(byte==>BYTE):Array_Size(M->N)=1,Array_Size(N->M)= byte.MinValue";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        byte byte_Array_Size = 1;
+        byte[] arrByte = Helper.InitArray<byte>(byte_Array_Size);
+        Assert.IsTrue(MarshalCStyleArrayByte_AsByRef_AsSizeParamIndex(ref byte_Array_Size, ref arrByte));
+
+        //Construct Expected array
+        int expected_ByteArray_Size = Byte.MinValue;
+        byte[] expectedArrByte = Helper.GetExpChangeArray<byte>(expected_ByteArray_Size);
+        Assert.IsTrue(Helper.EqualArray<byte>(arrByte, (int)byte_Array_Size, expectedArrByte, (int)expectedArrByte.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsSByte()
+    {
+        string strDescription = "Scenario(sbyte==>CHAR): Array_Size(M->N) = 10, Array_Size(N->M) = sbyte.Max";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        sbyte sbyte_Array_Size = (sbyte)10;
+        sbyte[] arrSbyte = Helper.InitArray<sbyte>(sbyte_Array_Size);
+
+        Assert.IsTrue(MarshalCStyleArraySbyte_AsByRef_AsSizeParamIndex(ref sbyte_Array_Size, ref arrSbyte));
+
+        //Construct Expected
+        sbyte[] expectedArrSbyte = Helper.GetExpChangeArray<sbyte>(sbyte.MaxValue);
+        Assert.IsTrue(Helper.EqualArray<sbyte>(arrSbyte, (int)sbyte_Array_Size, expectedArrSbyte, (int)sbyte.MaxValue));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsShort1()
+    {
+        string strDescription = "Scenario(short==>SHORT)1: Array_Size(M->N) = -1, Array_Size(N->M) = 20";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        short short_Array_Size = (short)-1;
+        short[] arrShort = Helper.InitArray<short>(10);
+        int expected_ByteArraySize = 20;
+
+        Assert.IsTrue(MarshalCStyleArrayShort_AsByRef_AsSizeParamIndex(ref short_Array_Size, ref arrShort));
+
+        //Construct Expected
+        short[] expectedArrShort = Helper.GetExpChangeArray<short>(expected_ByteArraySize);
+        Assert.IsTrue(Helper.EqualArray<short>(arrShort, (int)short_Array_Size, expectedArrShort, expectedArrShort.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsShort2()
+    {
+        string strDescription = "Scenario(short==>SHORT)2: Array_Size(M->N) = 10, Array_Size(N->M) = -1";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        short short_Array_Size = (short)10;
+        short[] arrShort = Helper.InitArray<short>(10);
+        Assert.Throws<OverflowException>(() => MarshalCStyleArrayShortReturnNegative_AsByRef_AsSizeParamIndex(ref short_Array_Size, ref arrShort));
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsUShort()
+    {
+        string strDescription = "Scenario(ushort==>USHORT): Array_Size(M->N) = 0, Array_Size(N->M) = ushort.MaxValue";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        ushort ushort_Array_Size = 20;
+        ushort[] arrUshort = Helper.InitArray<ushort>(ushort_Array_Size);
+
+        int expected_UshortArraySize = ushort.MaxValue;
+        Assert.IsTrue(MarshalCStyleArrayUshort_AsByRef_AsSizeParamIndex(ref arrUshort, ref ushort_Array_Size));
+
+        //Construct Expected
+        ushort[] expectedArrShort = Helper.GetExpChangeArray<ushort>(expected_UshortArraySize);
+        Assert.IsTrue(Helper.EqualArray<ushort>(arrUshort, (int)ushort_Array_Size, expectedArrShort, expectedArrShort.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsInt32()
+    {
+        string strDescription = "Scenario(Int32==>LONG):Array_Size(M->N)=10, Array_Size(N->M)=1";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        Int32 Int32_Array_Size = (Int32)10;
+        Int32[] arrInt32 = Helper.InitArray<Int32>(Int32_Array_Size);
+
+        Assert.IsTrue(MarshalCStyleArrayInt_AsByRef_AsSizeParamIndex(ref Int32_Array_Size, Int32.MaxValue, ref arrInt32));
+
+        //Construct Expected
+        int expected_UshortArraySize = 1;
+        Int32[] expectedArrInt32 = Helper.GetExpChangeArray<Int32>(expected_UshortArraySize);
+        Assert.IsTrue(Helper.EqualArray<Int32>(arrInt32, Int32_Array_Size, expectedArrInt32, expectedArrInt32.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsUInt32()
+    {
+        string strDescription = "Scenario(UInt32==>ULONG):Array_Size(M->N)=1234,Array_Size(N->M)=4321";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        UInt32 UInt32_Array_Size = (UInt32)1234;
+        UInt32[] arrUInt32 = Helper.InitArray<UInt32>((Int32)UInt32_Array_Size);
+        Assert.IsTrue(MarshalCStyleArrayUInt_AsByRef_AsSizeParamIndex(ref arrUInt32, 1234, ref UInt32_Array_Size));
+
+        //Construct Expected
+        int expected_UInt32ArraySize = 4321;
+        UInt32[] expectedArrUInt32 = Helper.GetExpChangeArray<UInt32>(expected_UInt32ArraySize);
+        Assert.IsTrue(Helper.EqualArray<UInt32>(arrUInt32, (Int32)UInt32_Array_Size, expectedArrUInt32, (Int32)expectedArrUInt32.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsLong()
+    {
+        string strDescription = "Scenario(long==>LONGLONG):Array_Size(M->N)=10,Array_Size(N->M)=20";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        long long_Array_Size = (long)10;
+        long[] arrLong = Helper.InitArray<long>((Int32)long_Array_Size);
+        Assert.IsTrue(MarshalCStyleArrayLong_AsByRef_AsSizeParamIndex(ref long_Array_Size, ref arrLong));
+
+        //Construct Expected Array
+        int expected_LongArraySize = 20;
+        long[] expectedArrLong = Helper.GetExpChangeArray<long>(expected_LongArraySize);
+        Assert.IsTrue(Helper.EqualArray<long>(arrLong, (Int32)long_Array_Size, expectedArrLong, (Int32)expectedArrLong.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsULong()
+    {
+        string strDescription = "Scenario(ulong==>ULONGLONG):Array_Size(M->N)=0, Array_Size(N->M)=0";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        ulong ulong_Array_Size = (ulong)0;
+        ulong[] arrUlong = Helper.InitArray<ulong>((Int32)ulong_Array_Size);
+
+        Assert.IsTrue(MarshalCStyleArrayUlong_AsByRef_AsSizeParamIndex(ref ulong_Array_Size, ref arrUlong));
+
+        //Construct Expected
+        int expected_ULongArraySize = 0;
+        ulong[] expectedArrUlong = Helper.GetExpChangeArray<ulong>(expected_ULongArraySize);
+        Assert.IsTrue(Helper.EqualArray<ulong>(arrUlong, (Int32)ulong_Array_Size, expectedArrUlong, (Int32)expectedArrUlong.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static void SizeParamTypeIsString()
+    {
+        string strDescription = "Scenario(String==>BSTR):Array_Size(M->N)= 20, Array_Size(N->M)=10";
+        Console.WriteLine();
+        Console.WriteLine(strDescription + " Starts!");
+
+        int array_Size = 20;
+        String[] arrString = Helper.InitArray<String>(array_Size);
+        String[] arrString2 = Helper.InitArray<String>(array_Size);
+
+        Assert.IsTrue(MarshalCStyleArrayString_AsByRef_AsSizeParamIndex(ref array_Size, ref arrString, ref arrString2));
+
+        //Construct Expected
+        int expected_StringArraySize = 10;
+        String[] expArrString = Helper.GetExpChangeArray<String>(expected_StringArraySize);
+        Assert.IsTrue(Helper.EqualArray<String>(arrString, array_Size, expArrString, expArrString.Length));
+
+        Console.WriteLine(strDescription + " Ends!");
+    }
+
+    static int Main()
+    {
+        try{
+            SizeParamTypeIsByte();
+            SizeParamTypeIsSByte();
+            SizeParamTypeIsShort1();
+            SizeParamTypeIsShort2();
+            SizeParamTypeIsUShort();
+            SizeParamTypeIsInt32();
+            SizeParamTypeIsUInt32();
+            SizeParamTypeIsLong();
+            SizeParamTypeIsULong();
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                SizeParamTypeIsString();
+            }
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+}
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PassingByRefTest.csproj b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/PassingByRef/PassingByRefTest.csproj
new file mode 100644 (file)
index 0000000..349b063
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>PassingByRefTest</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PassingByRefTest.cs" />
+    <Compile Include="..\helper.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="../../../../Interop.settings.targets" />
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/helper.cs b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/helper.cs
new file mode 100644 (file)
index 0000000..7074c9e
--- /dev/null
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+public class Helper
+{
+
+    #region General method
+
+    public static T[] InitArray<T>(int arrSize)
+    {
+        T[] array = new T[arrSize];
+        for (int i = 0; i < array.Length; i++)
+        {
+            array[i] = (T)Convert.ChangeType(i, typeof(T));
+        }
+        return array;
+    }
+
+    public static bool EqualArray<T>(T[] actualArray, int actualSize, T[] expectedArray, int expectedSize)
+    {
+        int failures = 0;
+        if (actualArray == null && expectedArray == null)
+        {
+            Console.WriteLine("\tTwo array are equal.Both of them null");
+            return true;
+        }
+        else if (actualArray == null && expectedArray != null)
+        {
+            Console.WriteLine("\tTwo array are not equal.The sourcArr is null,but the expectedArray is not null");
+            return false;
+        }
+        else if (actualArray != null && expectedArray == null)
+        {
+            Console.WriteLine("\tTwo array are not equal.The sourcArr is not null but the expectedArray is null");
+            return false;
+        }
+        else if (!actualSize.Equals(expectedSize))
+        {
+            Console.WriteLine("\tTwo array are not equal.The sizes are not equal:Expected:{0},Actaul:{1}", expectedSize, actualSize);
+            return false;
+        }
+        for (int i = 0; i < expectedSize; ++i)
+        {
+            if (!actualArray[i].Equals(expectedArray[i]))
+            {
+                Console.WriteLine("\tTwo array are not equal.The values of index {0} are not equal!", i);
+                Console.WriteLine("\t\tThe actualArray is {0},the expectedArray is {1}", actualArray[i].ToString(), expectedArray[i].ToString());
+                failures++;
+            }
+        }
+        if (failures > 0)
+            return false;
+        return true;
+    }
+
+    public static T[] GetExpChangeArray<T>(int cSize)
+    {
+        T[] array = new T[cSize];
+
+        for (int i = array.Length - 1; i >= 0; --i)
+            array[i] = (T)Convert.ChangeType(array.Length - 1 - i, typeof(T));
+
+        return array;
+    }
+
+    public static bool CheckAndChangeArray<T>(ref T[] arrArg, ref T arrSize, int actualArrSize, int expectedArrSize)
+    {
+        T[] actualArr = InitArray<T>(actualArrSize);
+        if (!EqualArray<T>(arrArg, actualArrSize, actualArr, actualArrSize))
+        {
+            return false;
+        }
+
+        arrSize = (T)Convert.ChangeType(expectedArrSize, typeof(T));
+        arrArg = GetExpChangeArray<T>(expectedArrSize);
+        return true;
+    }
+
+    #endregion
+
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/helper.h b/tests/src/Interop/PInvoke/SizeParamIndex/PInvoke/helper.h
new file mode 100644 (file)
index 0000000..ca93822
--- /dev/null
@@ -0,0 +1,225 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// helper.h : Defines helper functions
+#include <xplatform.h>
+#include "platformdefines.h"
+
+
+const LONG Array_Size = 10;
+const LONG CArray_Size = 20;
+
+//////////////////////////////////////////////////////////////////////////////
+// Verify helper methods
+//////////////////////////////////////////////////////////////////////////////
+template<typename T> BOOL IsObjectEquals(T o1, T o2)
+{
+    // T::operator== required.
+    return o1 == o2;
+}
+
+//Int32 helper
+template<typename T>
+T* InitArray(SIZE_T arrSize)
+{
+    T* pExpectArr = (T*)CoreClrAlloc(sizeof(T)*arrSize);
+    for(SIZE_T i = 0;i<arrSize;++i)
+    {
+        pExpectArr[i] = (T)i;
+    }
+    return pExpectArr;
+}
+
+template<typename T>
+T* InitExpectedArray(SIZE_T arrSize)
+{
+    T* pExpectArr = (T*)CoreClrAlloc(sizeof(T)*arrSize);
+    for(SIZE_T i = 0;i<arrSize;++i)
+    {
+        pExpectArr[i] = arrSize - 1 - i;
+    }
+    return pExpectArr;
+}
+
+template<typename T>
+BOOL EqualArray(T* actualArray, SIZE_T actualSize, T* expectedArray, SIZE_T expectedSize)
+{
+    int failures = 0;
+
+    if(actualArray == NULL && expectedArray == NULL)
+    {
+        printf("Two arrays are equal. Both of them are NULL\n");
+        return TRUE;
+    }
+    else if(actualArray == NULL && expectedArray != NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is NULL,but the Compared is not NULL!\n");
+        return FALSE;
+    }
+    else if(actualArray != NULL && expectedArray == NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is not NULL,but the Compared is NULL!\n");
+        return FALSE;
+    }
+    else if(actualSize != expectedSize)
+    {
+        printf("Two arrays aren't equal. The arrays size are not equal. Expected:%d, Actual:%d!\n",(int)expectedSize,(int)actualSize);
+        return FALSE;
+    }
+    for(SIZE_T i = 0;i<actualSize;++i)
+    {
+        if(actualArray[i] != expectedArray[i])
+        {
+            printf("Two arrays aren't equal.The value of index of %d isn't equal!\n",(int)i);
+            printf("\tThe value in array from managed to native is %d\n",(int)actualArray[i]);
+            printf("\tThe value in expected array is %d\n",(int)expectedArray[i]);
+            failures++;
+        }
+    }
+    if(failures>0)
+        return FALSE;
+    return TRUE;
+}
+
+template<typename T>
+BOOL CheckAndChangeArrayByRef(T ** ppActual, T* Actual_Array_Size, SIZE_T Expected_Array_Size, SIZE_T Return_Array_Size)
+{
+    T* pExpectedArr = InitArray<T>(Expected_Array_Size);
+    if(!EqualArray(*ppActual, (SIZE_T)*Actual_Array_Size, pExpectedArr, Expected_Array_Size))
+    {
+        printf("ManagedtoNative Error in Method: %s!\n",__FUNCTION__);
+        return FALSE;
+    }
+
+    CoreClrFree(pExpectedArr);
+    CoreClrFree(*ppActual);
+    *ppActual = (T*)CoreClrAlloc(sizeof(T)*Return_Array_Size);
+
+    *Actual_Array_Size = ((T)Return_Array_Size);
+    for(SIZE_T i = 0; i < Return_Array_Size; ++i)
+    {
+        (*ppActual)[i] = (T)(Return_Array_Size - 1 - i);
+    }
+    return TRUE;
+}
+
+template<typename T>
+BOOL CheckAndChangeArrayByOut(T ** ppActual, T* Actual_Array_Size, SIZE_T Array_Size)
+{
+    *ppActual = (T*)CoreClrAlloc(sizeof(T)*Array_Size);
+    *Actual_Array_Size = ((T)Array_Size);
+
+    for(SIZE_T i = 0; i < Array_Size; ++i)
+    {
+        (*ppActual)[i] = (T)(Array_Size - 1 - i);
+    }
+    return TRUE;
+}
+
+//template<typename T>
+//BOOL CheckReturnArray(T* pReturnArr, T Actual_Array_Size, T Expected_Array_Size)
+//{
+//    T* pExpectedArr = InitExpectedArray(Expected_Array_Size);
+//
+//    if(!EqualArray(pReturnArr, Actual_Array_Size, pExpectedArr, Expected_Array_Size))
+//    {
+//        printf("ManagedtoNative Error in Method: %s!\n",__FUNCTION__);
+//        CoreClrFree(pExpectedArr);
+//        return FALSE;
+//    }
+//    else
+//    {
+//        //printf("Managed to Native:Passed!\n");
+//        CoreClrFree(pExpectedArr);
+//        return TRUE;
+//    }
+//}
+
+//BSTR helper
+#ifdef _WIN32
+template<> BOOL IsObjectEquals(BSTR o1, BSTR o2)
+{
+    if ( o1 == NULL && o2 == NULL )
+        return TRUE;
+    else if ( o1 == NULL && o2 != NULL )
+        return FALSE;
+    else if ( o1 != NULL && o2 == NULL )
+        return FALSE;
+
+    UINT uLen1 = SysStringLen(o1);
+    UINT uLen2 = SysStringLen(o2);
+
+    if (uLen1 != uLen2 )
+        return FALSE;
+
+    return memcmp(o1, o2, uLen1) == 0;
+}
+
+BSTR ToBSTR(int i)
+{
+    BSTR bstrRet = NULL;
+    VarBstrFromI4(i, 0, 0, &bstrRet);
+
+    return bstrRet;
+}
+
+BOOL CmpBSTR(BSTR bstr1, BSTR bstr2)
+{
+    UINT uLen1 = SysStringLen(bstr1);
+    UINT uLen2 = SysStringLen(bstr2);
+
+    if (uLen1 != uLen2 )
+        return FALSE;
+    return memcmp(bstr1, bstr2, uLen1) == 0;
+}
+
+BSTR* InitArrayBSTR(LONG arrSize)
+{
+    BSTR* pExpectArr = (BSTR*)CoreClrAlloc(sizeof(BSTR)*arrSize);
+    for(LONG i = 0;i<arrSize;++i)
+    {
+        pExpectArr[i] = ToBSTR(i);
+    }
+    return pExpectArr;
+}
+
+BOOL EqualArrayBSTR(BSTR* ArrBSTR, LONG arrSize1, BSTR* CArrBSTR, LONG arrSize2)
+{
+    int failures = 0;
+
+    if(ArrBSTR == NULL && CArrBSTR == NULL)
+    {
+        printf("Two arrays are equal. Both of them NULL\n");
+        return TRUE;
+    }
+    else if(ArrBSTR == NULL && CArrBSTR != NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is NULL,but the Compared is not NULL!\n");
+        return FALSE;
+    }
+    else if(ArrBSTR != NULL && CArrBSTR == NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is not NULL,but the Compared is NULL!\n");
+        return FALSE;
+    }
+    else if(arrSize1 != arrSize2)
+    {
+        printf("Two arrays aren't equal. The arrays size are not equal!\n");
+        return FALSE;
+    }
+    for(int i = 0;i<arrSize1;++i)
+    {
+        if(!CmpBSTR(ArrBSTR[i],CArrBSTR[i]))
+        {
+            printf("Two arrays aren't equal.The value of index of %d isn't equal!\n",i);
+            printf("\tThe value in array from managed to native is %S\n",ArrBSTR[i]);
+            printf("\tThe value in expected array is %S\n",CArrBSTR[i]);
+            failures++;
+        }
+    }
+    if(failures>0)
+        return FALSE;
+    return TRUE;
+}
+#endif
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/CMakeLists.txt b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4dbfc7c
--- /dev/null
@@ -0,0 +1,14 @@
+cmake_minimum_required (VERSION 2.6) 
+project (ReversePInvokePassingByOutNative) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+include_directories("..")
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    ReversePInvokePassingByOutNative.cpp 
+) 
+# Additional files to reference: 
+# add the executable 
+add_library (ReversePInvokePassingByOutNative SHARED ${SOURCES}) 
+target_link_libraries(ReversePInvokePassingByOutNative ${LINK_LIBRARIES_ADDITIONAL}) 
+# add the install targets 
+install (TARGETS ReversePInvokePassingByOutNative DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/PassingByOutTest.cs b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/PassingByOutTest.cs
new file mode 100644 (file)
index 0000000..ab0f37a
--- /dev/null
@@ -0,0 +1,208 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+public class ReversePInvoke_MashalArrayByOut_AsManagedTest
+{
+    public static int arrSize = 10;
+
+    public static int failures = 0;
+
+    #region Func Sig
+
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalByteArray_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelByteArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalSbyteArray_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelSbyteArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalShortArray_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelShortArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelShortArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalUshortArray_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelUshortArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalInt32Array_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelInt32ArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalUint32Array_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelUint32ArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalLongArray_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelLongArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalUlongArray_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelUlongArrByOutAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByOutNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalStringArray_AsParam_AsByOut([MarshalAs(UnmanagedType.FunctionPtr)]DelStringArrByOutAsCdeclCaller caller);
+
+    #endregion
+
+    #region Delegate Method
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelByteArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out byte[] arrArg, out byte arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelSbyteArrByOutAsCdeclCaller(out sbyte arraySize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out sbyte[] arrArg);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelShortArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out short[] arrArg, out short arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelUshortArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out ushort[] arrArg, out ushort arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelInt32ArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out Int32[] arrArg, out Int32 arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelUint32ArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out UInt32[] arrArg, out UInt32 arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelLongArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out long[] arrArg, out long arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelUlongArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out ulong[] arrArg, out ulong arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelStringArrByOutAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.BStr)] out string[] arrArg, out Int32 arraySize);
+    
+    #endregion
+
+    #region Test Method
+
+    //Type: byte ==> BYTE    Array Size: byte.MinValue ==> 20
+    public static bool TestMethodForByteArray_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out byte[] arrArg, out byte arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<byte>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    //Type: sbyte ==> CHAR  Array Size: 1 ==> sbyte.MaxValue
+    public static bool TestMethodForSbyteArray_AsReversePInvokeByOut_AsCdecl(out sbyte arraySize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] out sbyte[] arrArg)
+    {
+        arrArg = Helper.GetExpChangeArray<sbyte>(sbyte.MaxValue);
+        arraySize = sbyte.MaxValue;
+        return true;
+    }
+
+    //Type: short ==> SHORT  Array Size: -1 ==> 20(Actual 10 ==> 20)
+    public static bool TestMethodForShortArray_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out short[] arrArg, out short arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<short>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    //Type: short ==> SHORT  Array Size: 10 ==> -1(Actual 10 ==> 20)
+    public static bool TestMethodForShortArrayReturnNegativeSize_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out short[] arrArg, out short arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<short>(20);
+        arraySize = -1;
+        return true;
+    }
+
+    //Type: ushort ==> USHORT  Array Size: ushort.MaxValue ==> 20
+    public static bool TestMethodForUshortArray_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out ushort[] arrArg, out ushort arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<ushort>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    //Type: Int32 ==> LONG    Array Size: 10 ==> 20
+    public static bool TestMethodForInt32Array_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out Int32[] arrArg, out Int32 arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<Int32>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    //Type: UInt32 ==> ULONG    Array Size: 10 ==> 20
+    public static bool TestMethodForUint32Array_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out UInt32[] arrArg, out UInt32 arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<UInt32>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    //Type: long ==> LONGLONG    Array Size: 10 ==> 20
+    public static bool TestMethodForLongArray_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out long[] arrArg, out long arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<long>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    //Type: ulong ==> ULONGLONG    Array Size: 10 ==> 20
+    public static bool TestMethodForUlongArray_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out ulong[] arrArg, out ulong arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<ulong>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    //Type: string ==> BSTR    Array Size: 10 ==> 20
+    public static bool TestMethodForStringArray_AsReversePInvokeByOut_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.BStr)] out string[] arrArg, out Int32 arraySize)
+    {
+        arrArg = Helper.GetExpChangeArray<string>(20);
+        arraySize = 20;
+        return true;
+    }
+
+    #endregion
+
+    public static void RunTestByOut()
+    {
+        Console.WriteLine("ReversePInvoke C-Style Array marshaled by out with SizeParamIndex attribute(by out Array size).");
+
+        //Common value type
+        Console.WriteLine("\tScenario 1 : byte ==> BYTE, Array_Size = byte.MinValue, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalByteArray_AsParam_AsByOut(new DelByteArrByOutAsCdeclCaller(TestMethodForByteArray_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalByteArray_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 2 : sbyte ==> CHAR, Array_Size = 1, Return_Array_Size = sbyte.Max");
+        Assert.IsTrue(DoCallBack_MarshalSbyteArray_AsParam_AsByOut(new DelSbyteArrByOutAsCdeclCaller(TestMethodForSbyteArray_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalSbyteArray_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 3 : short ==> SHORT, Array_Size = -1, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalShortArray_AsParam_AsByOut(new DelShortArrByOutAsCdeclCaller(TestMethodForShortArray_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalShortArray_AsReversePInvokeByOut_AsCdecl Failed!");
+
+        Console.WriteLine("\tScenario 4 : short ==> SHORT, Array_Size = 10, Return_Array_Size = -1");
+        Assert.IsTrue(DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByOut(new DelShortArrByOutAsCdeclCaller(TestMethodForShortArrayReturnNegativeSize_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalShortArray_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 5 : ushort ==> USHORT, Array_Size = ushort.MaxValue, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalUshortArray_AsParam_AsByOut(new DelUshortArrByOutAsCdeclCaller(TestMethodForUshortArray_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalUshortArray_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 6 : Int32 ==> LONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalInt32Array_AsParam_AsByOut(new DelInt32ArrByOutAsCdeclCaller(TestMethodForInt32Array_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalInt32Array_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 7 : UInt32 ==> ULONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalUint32Array_AsParam_AsByOut(new DelUint32ArrByOutAsCdeclCaller(TestMethodForUint32Array_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalUint32Array_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 8 : long ==> LONGLONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalLongArray_AsParam_AsByOut(new DelLongArrByOutAsCdeclCaller(TestMethodForLongArray_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalLongArray_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 9 : ulong ==> ULONGLONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalUlongArray_AsParam_AsByOut(new DelUlongArrByOutAsCdeclCaller(TestMethodForUlongArray_AsReversePInvokeByOut_AsCdecl)));
+        Console.WriteLine("\t\tMarshalUlongArray_AsReversePInvokeByOut_AsCdecl Passed!");
+
+        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+        {
+            Console.WriteLine("\tScenario 10 : string ==> BSTR, Array_Size = 10, Return_Array_Size = 20");
+            Assert.IsTrue(DoCallBack_MarshalStringArray_AsParam_AsByOut(new DelStringArrByOutAsCdeclCaller(TestMethodForStringArray_AsReversePInvokeByOut_AsCdecl)));
+            Console.WriteLine("\t\tMarshalStringArray_AsReversePInvokeByOut_AsCdecl Passed!");
+        }
+    }
+
+    public static int Main()
+    {
+        try{
+            RunTestByOut();
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+}
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/PassingByOutTest.csproj b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/PassingByOutTest.csproj
new file mode 100644 (file)
index 0000000..5344935
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>PassingByOutTest</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PassingByOutTest.cs" />
+    <Compile Include="..\helper.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="../../../../Interop.settings.targets" />
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/ReversePInvokePassingByOutNative.cpp b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut/ReversePInvokePassingByOutNative.cpp
new file mode 100644 (file)
index 0000000..f95d2a6
--- /dev/null
@@ -0,0 +1,205 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ReversePInvokePassingByOutNative.cpp : Defines the entry point for the DLL application.
+//
+#include <xplatform.h>
+#include <limits.h>
+#include "platformdefines.h"
+#include "helper.h"
+
+//Func Pointer
+typedef BOOL (__cdecl *DelByteArrByOutAsCdeclCaller)(BYTE** arrByte, BYTE* arraySize);
+typedef BOOL (__cdecl *DelSbyteArrByOutAsCdeclCaller)(CHAR* arraySize, CHAR** arrSbyte);
+typedef BOOL (__cdecl *DelShortArrByOutAsCdeclCaller)(SHORT** arrShort, SHORT* arraySize);
+typedef BOOL (__cdecl *DelUshortArrByOutAsCdeclCaller)(USHORT** arrUshort, USHORT* arraySize);
+typedef BOOL (__cdecl *DelInt32ArrByOutAsCdeclCaller)(LONG** arrInt32, LONG* arraySize);
+typedef BOOL (__cdecl *DelUint32ArrByOutAsCdeclCaller)(ULONG** arrUint32, ULONG* arraySize);
+typedef BOOL (__cdecl *DelLongArrByOutAsCdeclCaller)(LONGLONG** arrLong, LONGLONG* arraySize);
+typedef BOOL (__cdecl *DelUlongArrByOutAsCdeclCaller)(ULONGLONG** arrUlong, ULONGLONG* arraySize);
+typedef BOOL (__cdecl *DelStringArrByOutAsCdeclCaller)(BSTR** arrString, LONG* arraySize);
+
+//#######################################################
+//Test Method
+//#######################################################
+
+//BYTE 0 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalByteArray_AsParam_AsByOut(DelByteArrByOutAsCdeclCaller caller)
+{
+    BYTE arrSize = 0;
+    BYTE* arrByte = InitArray<BYTE>(arrSize);
+
+    if(!caller(&arrByte, &arrSize))
+    {
+        printf("DoCallBack_MarshalByteArray_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrByte);
+        return FALSE;
+    }
+
+    return CheckArray(arrByte, arrSize, (BYTE)20);
+}
+
+//CHAR 1 ==> CHAR.Max size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalSbyteArray_AsParam_AsByOut(DelSbyteArrByOutAsCdeclCaller caller)
+{
+    CHAR arrSize = 1;
+    CHAR* arrSbyte = InitArray<CHAR>((size_t)arrSize);
+
+    if(!caller(&arrSize, &arrSbyte))
+    {
+        printf("DoCallBack_MarshalSbyteArray_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrSbyte);
+        return FALSE;
+    }
+
+    return CheckArray(arrSbyte, (size_t)arrSize, (CHAR)127);
+}
+
+//SHORT -1 ==> 20 size Array(Actual: 10 ==> 20)
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalShortArray_AsParam_AsByOut(DelShortArrByOutAsCdeclCaller caller)
+{
+    SHORT arrSize = -1;
+    SHORT* arrShort = InitArray<SHORT>(SHORT(10));
+
+    if(!caller(&arrShort, &arrSize))
+    {
+        printf("DoCallBack_MarshalShortArray_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrShort);
+        return FALSE;
+    }
+
+    return CheckArray(arrShort, (size_t)arrSize, (SHORT)20);
+}
+
+//SHORT 10 ==> -1 size Array(Actual: 10 ==> 20)
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByOut(DelShortArrByOutAsCdeclCaller caller)
+{
+    SHORT arrSize = 10;
+    SHORT* arrShort = InitArray<SHORT>((size_t)arrSize);
+
+    if(!caller(&arrShort, &arrSize))
+    {
+        printf("DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrShort);
+        return FALSE;
+    }
+
+    if(arrSize == -1)
+        return CheckArray(arrShort, (SHORT)20, (SHORT)20);
+    else
+        return FALSE;
+}
+
+//USHORT ushort.Max ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalUshortArray_AsParam_AsByOut(DelUshortArrByOutAsCdeclCaller caller)
+{
+    USHORT arrSize = 65535;
+    USHORT* arrUshort = InitArray<USHORT>(arrSize);
+
+    if(!caller(&arrUshort, &arrSize))
+    {
+        printf("DoCallBack_MarshalUshortArray_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrUshort);
+        return FALSE;
+    }
+
+    return CheckArray(arrUshort, arrSize, (USHORT)20);
+}
+
+//Int32 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalInt32Array_AsParam_AsByOut(DelInt32ArrByOutAsCdeclCaller caller)
+{
+    LONG arrSize = 10;
+    LONG* arrInt32 = InitArray<LONG>((size_t)arrSize);
+
+    if(!caller(&arrInt32, &arrSize))
+    {
+        printf("DoCallBack_MarshalInt32Array_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrInt32);
+        return FALSE;
+    }
+
+    return CheckArray(arrInt32, (size_t)arrSize, (LONG)20);
+}
+
+//UInt32 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalUint32Array_AsParam_AsByOut(DelUint32ArrByOutAsCdeclCaller caller)
+{
+    ULONG arrSize = 10;
+    ULONG* arrUint32 = InitArray<ULONG>(arrSize);
+
+    if(!caller(&arrUint32, &arrSize))
+    {
+        printf("DoCallBack_MarshalUint32Array_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrUint32);
+        return FALSE;
+    }
+
+    return CheckArray(arrUint32, arrSize, (ULONG)20);
+}
+
+//LONGLONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalLongArray_AsParam_AsByOut(DelLongArrByOutAsCdeclCaller caller)
+{
+    LONGLONG arrSize = 10;
+    LONGLONG* arrLong = InitArray<LONGLONG>((SIZE_T)arrSize);
+
+    if(!caller(&arrLong, &arrSize))
+    {
+        printf("DoCallBack_MarshalLongArray_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrLong);
+        return FALSE;
+    }
+
+    return CheckArray(arrLong, (SIZE_T)arrSize, 20);
+}
+
+//ULONGLONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalUlongArray_AsParam_AsByOut(DelUlongArrByOutAsCdeclCaller caller)
+{
+    ULONGLONG arrSize = 10;
+    ULONGLONG* arrUlong = InitArray<ULONGLONG>((SIZE_T)arrSize);
+
+    if(!caller(&arrUlong, &arrSize))
+    {
+        printf("DoCallBack_MarshalUlongArray_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrUlong);
+        return FALSE;
+    }
+
+    return CheckArray(arrUlong, (SIZE_T)arrSize, 20);
+}
+
+#ifdef _WIN32
+//BSTR 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalStringArray_AsParam_AsByOut(DelStringArrByOutAsCdeclCaller caller)
+{
+    LONG arrSize = 10;
+    BSTR* arrString = InitArrayBSTR(arrSize);
+
+    if(!caller(&arrString, &arrSize))
+    {
+        printf("DoCallBack_MarshalStringArray_AsParam_AsByOut:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrString);
+        return FALSE;
+    }
+
+    LONG ExpectedArraySize = 20;
+    BSTR* pExpectedArr = (BSTR*)CoreClrAlloc(sizeof(BSTR)*ExpectedArraySize);
+    for(LONG i = 0; i < ExpectedArraySize; ++i)
+    {
+        pExpectedArr[i] = ToBSTR(ExpectedArraySize - 1 - i);
+    }
+
+    if(!EqualArrayBSTR(arrString, arrSize, pExpectedArr, ExpectedArraySize))
+    {
+        printf("ManagedtoNative Error in Method: %s!\n",__FUNCTION__);
+        return FALSE;
+    }
+
+    CoreClrFree(arrString);
+    CoreClrFree(pExpectedArr);
+    return TRUE;
+}
+#endif
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/CMakeLists.txt b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/CMakeLists.txt
new file mode 100644 (file)
index 0000000..34e702b
--- /dev/null
@@ -0,0 +1,14 @@
+cmake_minimum_required (VERSION 2.6) 
+project (ReversePInvokePassingByRefNative) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+include_directories("..")
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    ReversePInvokePassingByRefNative.cpp 
+) 
+# Additional files to reference: 
+# add the executable 
+add_library (ReversePInvokePassingByRefNative SHARED ${SOURCES}) 
+target_link_libraries(ReversePInvokePassingByRefNative ${LINK_LIBRARIES_ADDITIONAL}) 
+# add the install targets 
+install (TARGETS ReversePInvokePassingByRefNative DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/PassingByRefTest.cs b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/PassingByRefTest.cs
new file mode 100644 (file)
index 0000000..e6864c2
--- /dev/null
@@ -0,0 +1,223 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+public class ReversePInvoke_MashalArrayByRef_AsManagedTest
+{
+    public static int arrSize = 10;
+
+    public static int failures = 0;
+
+    #region Func Sig
+
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalByteArray_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelByteArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalSbyteArray_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelSbyteArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalShortArray_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelShortArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelShortArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalUshortArray_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelUshortArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalInt32Array_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelInt32ArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalUint32Array_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelUint32ArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalLongArray_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelLongArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalUlongArray_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelUlongArrByRefAsCdeclCaller caller);
+    [DllImport("ReversePInvokePassingByRefNative", CallingConvention = CallingConvention.Cdecl)]
+    private static extern bool DoCallBack_MarshalStringArray_AsParam_AsByRef([MarshalAs(UnmanagedType.FunctionPtr)]DelStringArrByRefAsCdeclCaller caller);
+
+    #endregion
+
+    #region Delegate Method
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelByteArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref byte[] arrArg, ref byte arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelSbyteArrByRefAsCdeclCaller(ref sbyte arraySize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref sbyte[] arrArg);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelShortArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref short[] arrArg, ref short arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelUshortArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref ushort[] arrArg, ref ushort arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelInt32ArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref Int32[] arrArg, ref Int32 arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelUint32ArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref UInt32[] arrArg, ref UInt32 arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelLongArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref long[] arrArg, ref long arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelUlongArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref ulong[] arrArg, ref ulong arraySize);
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate bool DelStringArrByRefAsCdeclCaller([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.BStr)] ref string[] arrArg, ref Int32 arraySize);
+    
+    #endregion
+
+    #region Test Method
+
+    //Type: byte ==> BYTE    Array Size: byte.MinValue ==> 20
+    public static bool TestMethodForByteArray_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref byte[] arrArg, ref byte arraySize)
+    {
+        if (arraySize == byte.MinValue)
+            return Helper.CheckAndChangeArray<byte>(ref arrArg, ref arraySize, (Int32)byte.MinValue, 20);
+        return false;
+    }
+
+    //Type: sbyte ==> CHAR  Array Size: 1 ==> sbyte.MaxValue
+    public static bool TestMethodForSbyteArray_AsReversePInvokeByRef_AsCdecl(ref sbyte arraySize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ref sbyte[] arrArg)
+    {
+        if (arraySize == 1)
+            return Helper.CheckAndChangeArray<sbyte>(ref arrArg, ref arraySize, 1, (Int32)sbyte.MaxValue);
+        return false;
+    }
+
+    //Type: short ==> SHORT  Array Size: -1 ==> 20(Actual 10 ==> 20)
+    public static bool TestMethodForShortArray_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref short[] arrArg, ref short arraySize)
+    {
+        if (arraySize == -1)
+            return Helper.CheckAndChangeArray<short>(ref arrArg, ref arraySize, 10, 20);
+        return false;
+    }
+
+    //Type: short ==> SHORT  Array Size: 10 ==> -1(Actual 10 ==> 20)
+    public static bool TestMethodForShortArrayReturnNegativeSize_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref short[] arrArg, ref short arraySize)
+    {
+        if (arraySize == 10)
+        {
+            Helper.CheckAndChangeArray<short>(ref arrArg, ref arraySize, 10, 20);
+            arraySize = -1;
+            return true;
+        }
+        return false;
+    }
+
+    //Type: ushort ==> USHORT  Array Size: ushort.MaxValue ==> 20
+    public static bool TestMethodForUshortArray_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref ushort[] arrArg, ref ushort arraySize)
+    {
+        if (arraySize == ushort.MaxValue)
+            return Helper.CheckAndChangeArray<ushort>(ref arrArg, ref arraySize, (Int32)ushort.MaxValue, 20);
+        return false;
+    }
+
+    //Type: Int32 ==> LONG    Array Size: 10 ==> 20
+    public static bool TestMethodForInt32Array_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref Int32[] arrArg, ref Int32 arraySize)
+    {
+        if (arraySize == 10)
+            return Helper.CheckAndChangeArray<Int32>(ref arrArg, ref arraySize, 10, 20);
+        return false;
+    }
+
+    //Type: UInt32 ==> ULONG    Array Size: 10 ==> 20
+    public static bool TestMethodForUint32Array_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref UInt32[] arrArg, ref UInt32 arraySize)
+    {
+        if (arraySize == 10)
+            return Helper.CheckAndChangeArray<UInt32>(ref arrArg, ref arraySize, 10, 20);
+        return false;
+    }
+
+    //Type: long ==> LONGLONG    Array Size: 10 ==> 20
+    public static bool TestMethodForLongArray_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref long[] arrArg, ref long arraySize)
+    {
+        if (arraySize == 10)
+            return Helper.CheckAndChangeArray<long>(ref arrArg, ref arraySize, 10, 20);
+        return false;
+    }
+
+    //Type: ulong ==> ULONGLONG    Array Size: 10 ==> 20
+    public static bool TestMethodForUlongArray_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] ref ulong[] arrArg, ref ulong arraySize)
+    {
+        if (arraySize == 10)
+            return Helper.CheckAndChangeArray<ulong>(ref arrArg, ref arraySize, 10, 20);
+        return false;
+    }
+
+    //Type: string ==> BSTR    Array Size: 10 ==> 20
+    public static bool TestMethodForStringArray_AsReversePInvokeByRef_AsCdecl([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.BStr)] ref string[] arrArg, ref Int32 arraySize)
+    {
+        string[] actualArr = Helper.InitArray<string>(10);
+        if (!Helper.EqualArray<string>(arrArg, arraySize, actualArr, 10))
+        {
+            return false;
+        }
+
+        arraySize = 20;
+        arrArg = Helper.GetExpChangeArray<string>(20);
+        return true;
+    }
+
+    #endregion
+
+    public static void RunTestByRef()
+    {
+        Console.WriteLine("ReversePInvoke C-Style Array marshaled by ref with SizeParamIndex attribute(by ref Array size).");
+
+        //Common value type
+        Console.WriteLine("\tScenario 1 : byte ==> BYTE, Array_Size = byte.MinValue, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalByteArray_AsParam_AsByRef(new DelByteArrByRefAsCdeclCaller(TestMethodForByteArray_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalByteArray_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 2 : sbyte ==> CHAR, Array_Size = 1, Return_Array_Size = sbyte.Max");
+        Assert.IsTrue(DoCallBack_MarshalSbyteArray_AsParam_AsByRef(new DelSbyteArrByRefAsCdeclCaller(TestMethodForSbyteArray_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalSbyteArray_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        // We don't support exception interop in .NET Core off-Windows.
+        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+        {
+            Console.WriteLine("\tScenario 3 : short ==> SHORT, Array_Size = -1, Return_Array_Size = 20");
+            Assert.Throws<OverflowException>(() => DoCallBack_MarshalShortArray_AsParam_AsByRef(new DelShortArrByRefAsCdeclCaller(TestMethodForShortArray_AsReversePInvokeByRef_AsCdecl)));
+            Console.WriteLine("\t\tMarshalShortArray_AsReversePInvokeByRef_AsCdecl Passed!");
+        }
+
+
+        Console.WriteLine("\tScenario 4 : short ==> SHORT, Array_Size = 10, Return_Array_Size = -1");
+        Assert.IsTrue(DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByRef(new DelShortArrByRefAsCdeclCaller(TestMethodForShortArrayReturnNegativeSize_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalShortArray_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 5 : ushort ==> USHORT, Array_Size = ushort.MaxValue, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalUshortArray_AsParam_AsByRef(new DelUshortArrByRefAsCdeclCaller(TestMethodForUshortArray_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalUshortArray_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 6 : Int32 ==> LONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalInt32Array_AsParam_AsByRef(new DelInt32ArrByRefAsCdeclCaller(TestMethodForInt32Array_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalInt32Array_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 7 : UInt32 ==> ULONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalUint32Array_AsParam_AsByRef(new DelUint32ArrByRefAsCdeclCaller(TestMethodForUint32Array_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalUint32Array_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 8 : long ==> LONGLONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalLongArray_AsParam_AsByRef(new DelLongArrByRefAsCdeclCaller(TestMethodForLongArray_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalLongArray_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        Console.WriteLine("\tScenario 9 : ulong ==> ULONGLONG, Array_Size = 10, Return_Array_Size = 20");
+        Assert.IsTrue(DoCallBack_MarshalUlongArray_AsParam_AsByRef(new DelUlongArrByRefAsCdeclCaller(TestMethodForUlongArray_AsReversePInvokeByRef_AsCdecl)));
+        Console.WriteLine("\t\tMarshalUlongArray_AsReversePInvokeByRef_AsCdecl Passed!");
+
+        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+        {
+            Console.WriteLine("\tScenario 10 : string ==> BSTR, Array_Size = 10, Return_Array_Size = 20");
+            Assert.IsTrue(DoCallBack_MarshalStringArray_AsParam_AsByRef(new DelStringArrByRefAsCdeclCaller(TestMethodForStringArray_AsReversePInvokeByRef_AsCdecl)));
+            Console.WriteLine("\t\tMarshalStringArray_AsReversePInvokeByRef_AsCdecl Passed!");
+        }
+    }
+
+    public static int Main()
+    {
+        try{
+            RunTestByRef();
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+}
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/PassingByRefTest.csproj b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/PassingByRefTest.csproj
new file mode 100644 (file)
index 0000000..349b063
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>PassingByRefTest</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\..\</SolutionDir>
+    <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PassingByRefTest.cs" />
+    <Compile Include="..\helper.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="../../../../Interop.settings.targets" />
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/ReversePInvokePassingByRefNative.cpp b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef/ReversePInvokePassingByRefNative.cpp
new file mode 100644 (file)
index 0000000..a51ecef
--- /dev/null
@@ -0,0 +1,203 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ReversePInvokePassingByRefNative.cpp : Defines the entry point for the DLL application.
+#include <xplatform.h>
+#include <limits.h>
+#include "platformdefines.h"
+#include "helper.h"
+
+//Func Pointer
+typedef BOOL (__cdecl *DelByteArrByRefAsCdeclCaller)(BYTE** arrByte, BYTE* arraySize);
+typedef BOOL (__cdecl *DelSbyteArrByRefAsCdeclCaller)(CHAR* arraySize, CHAR** arrSbyte);
+typedef BOOL (__cdecl *DelShortArrByRefAsCdeclCaller)(SHORT** arrShort, SHORT* arraySize);
+typedef BOOL (__cdecl *DelUshortArrByRefAsCdeclCaller)(USHORT** arrUshort, USHORT* arraySize);
+typedef BOOL (__cdecl *DelInt32ArrByRefAsCdeclCaller)(LONG** arrInt32, LONG* arraySize);
+typedef BOOL (__cdecl *DelUint32ArrByRefAsCdeclCaller)(ULONG** arrUint32, ULONG* arraySize);
+typedef BOOL (__cdecl *DelLongArrByRefAsCdeclCaller)(LONGLONG** arrLong, LONGLONG* arraySize);
+typedef BOOL (__cdecl *DelUlongArrByRefAsCdeclCaller)(ULONGLONG** arrUlong, ULONGLONG* arraySize);
+typedef BOOL (__cdecl *DelStringArrByRefAsCdeclCaller)(BSTR** arrString, LONG* arraySize);
+
+//#######################################################
+//Test Method
+//#######################################################
+
+//BYTE 0 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalByteArray_AsParam_AsByRef(DelByteArrByRefAsCdeclCaller caller)
+{
+    BYTE arrSize = 0;
+    BYTE* arrByte = InitArray<BYTE>(arrSize);
+
+    if(!caller(&arrByte, &arrSize))
+    {
+        printf("DoCallBack_MarshalByteArray_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrByte);
+        return FALSE;
+    }
+
+    return CheckArray(arrByte, arrSize, (BYTE)20);
+}
+
+//CHAR 1 ==> CHAR.Max size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalSbyteArray_AsParam_AsByRef(DelSbyteArrByRefAsCdeclCaller caller)
+{
+    CHAR arrSize = 1;
+    CHAR* arrSbyte = InitArray<CHAR>((size_t)arrSize);
+
+    if(!caller(&arrSize, &arrSbyte))
+    {
+        printf("DoCallBack_MarshalSbyteArray_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrSbyte);
+        return FALSE;
+    }
+
+    return CheckArray(arrSbyte, (size_t)arrSize, (CHAR)127);
+}
+
+//SHORT -1 ==> 20 size Array(Actual: 10 ==> 20)
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalShortArray_AsParam_AsByRef(DelShortArrByRefAsCdeclCaller caller)
+{
+    SHORT arrSize = -1;
+    SHORT* arrShort = InitArray<SHORT>(SHORT(10));
+
+    if(!caller(&arrShort, &arrSize))
+    {
+        printf("DoCallBack_MarshalShortArray_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrShort);
+        return FALSE;
+    }
+
+    return CheckArray(arrShort, (size_t)arrSize, (SHORT)20);
+}
+
+//SHORT 10 ==> -1 size Array(Actual: 10 ==> 20)
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByRef(DelShortArrByRefAsCdeclCaller caller)
+{
+    SHORT arrSize = 10;
+    SHORT* arrShort = InitArray<SHORT>((size_t)arrSize);
+
+    if(!caller(&arrShort, &arrSize))
+    {
+        printf("DoCallBack_MarshalShortArrayReturnNegativeSize_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrShort);
+        return FALSE;
+    }
+
+    if(arrSize == -1)
+        return CheckArray(arrShort, (SHORT)20, (SHORT)20);
+    else
+        return FALSE;
+}
+
+//USHORT ushort.Max ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalUshortArray_AsParam_AsByRef(DelUshortArrByRefAsCdeclCaller caller)
+{
+    USHORT arrSize = 65535;
+    USHORT* arrUshort = InitArray<USHORT>(arrSize);
+
+    if(!caller(&arrUshort, &arrSize))
+    {
+        printf("DoCallBack_MarshalUshortArray_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrUshort);
+        return FALSE;
+    }
+
+    return CheckArray(arrUshort, arrSize, (USHORT)20);
+}
+
+//Int32 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalInt32Array_AsParam_AsByRef(DelInt32ArrByRefAsCdeclCaller caller)
+{
+    LONG arrSize = 10;
+    LONG* arrInt32 = InitArray<LONG>((size_t)arrSize);
+
+    if(!caller(&arrInt32, &arrSize))
+    {
+        printf("DoCallBack_MarshalInt32Array_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrInt32);
+        return FALSE;
+    }
+
+    return CheckArray(arrInt32, (size_t)arrSize, (LONG)20);
+}
+
+//UInt32 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalUint32Array_AsParam_AsByRef(DelUint32ArrByRefAsCdeclCaller caller)
+{
+    ULONG arrSize = 10;
+    ULONG* arrUint32 = InitArray<ULONG>(arrSize);
+
+    if(!caller(&arrUint32, &arrSize))
+    {
+        printf("DoCallBack_MarshalUint32Array_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrUint32);
+        return FALSE;
+    }
+
+    return CheckArray(arrUint32, arrSize, (ULONG)20);
+}
+
+//LONGLONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalLongArray_AsParam_AsByRef(DelLongArrByRefAsCdeclCaller caller)
+{
+    LONGLONG arrSize = 10;
+    LONGLONG* arrLong = InitArray<LONGLONG>(SIZE_T(arrSize));
+
+    if(!caller(&arrLong, &arrSize))
+    {
+        printf("DoCallBack_MarshalLongArray_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrLong);
+        return FALSE;
+    }
+
+    return CheckArray(arrLong, (SIZE_T)arrSize, 20);
+}
+
+//ULONGLONG 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalUlongArray_AsParam_AsByRef(DelUlongArrByRefAsCdeclCaller caller)
+{
+    ULONGLONG arrSize = 10;
+    ULONGLONG* arrUlong = InitArray<ULONGLONG>(SIZE_T(arrSize));
+
+    if(!caller(&arrUlong, &arrSize))
+    {
+        printf("DoCallBack_MarshalUlongArray_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrUlong);
+        return FALSE;
+    }
+
+    return CheckArray(arrUlong, (SIZE_T)arrSize, 20);
+}
+#ifdef _WIN32
+//BSTR 10 ==> 20 size Array
+extern "C" DLL_EXPORT BOOL __cdecl DoCallBack_MarshalStringArray_AsParam_AsByRef(DelStringArrByRefAsCdeclCaller caller)
+{
+    LONG arrSize = 10;
+    BSTR* arrString = InitArrayBSTR(arrSize);
+
+    if(!caller(&arrString, &arrSize))
+    {
+        printf("DoCallBack_MarshalStringArray_AsParam_AsByRef:\n\tThe Caller returns wrong value\n");
+        CoreClrFree(arrString);
+        return FALSE;
+    }
+
+    LONG ExpectedArraySize = 20;
+    BSTR* pExpectedArr = (BSTR*)CoreClrAlloc(sizeof(BSTR)*ExpectedArraySize);
+    for(LONG i = 0; i < ExpectedArraySize; ++i)
+    {
+        pExpectedArr[i] = ToBSTR(ExpectedArraySize - 1 - i);
+    }
+
+    if(!EqualArrayBSTR(arrString, arrSize, pExpectedArr, ExpectedArraySize))
+    {
+        printf("ManagedtoNative Error in Method: %s!\n",__FUNCTION__);
+        return FALSE;
+    }
+
+    CoreClrFree(arrString);
+    CoreClrFree(pExpectedArr);
+    return TRUE;
+}
+#endif
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/helper.cs b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/helper.cs
new file mode 100644 (file)
index 0000000..a02e219
--- /dev/null
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+public class Helper
+{
+    #region General method
+
+    public static T[] InitArray<T>(int arrSize)
+    {
+        T[] array = new T[arrSize];
+        for (int i = 0; i < array.Length; i++)
+        {
+            array[i] = (T)Convert.ChangeType(i, typeof(T));
+        }
+        return array;
+    }
+
+    public static bool EqualArray<T>(T[] actualArray, int actualSize, T[] expArr, int arrSize2)
+    {
+        int failures = 0;
+        if (actualArray == null && expArr == null)
+        {
+            Console.WriteLine("\tTwo array are equal.Both of them null");
+            return true;
+        }
+        else if (actualArray == null && expArr != null)
+        {
+            Console.WriteLine("\tTwo array are not equal.The sourcArr is null,but the expArr is not null");
+            return false;
+        }
+        else if (actualArray != null && expArr == null)
+        {
+            Console.WriteLine("\tTwo array are not equal.The sourcArr is not null but the expArr is null");
+            return false;
+        }
+        else if (!actualSize.Equals(arrSize2))
+        {
+            Console.WriteLine("\tTwo array are not equal.The sizes are not equal");
+            return false;
+        }
+        for (int i = 0; i < arrSize2; ++i)
+        {
+            if (!actualArray[i].Equals(expArr[i]))
+            {
+                Console.WriteLine("\tTwo array are not equal.The values of index {0} are not equal!", i);
+                Console.WriteLine("\t\tThe actualArray is {0},the expArr is {1}", actualArray[i].ToString(), expArr[i].ToString());
+                failures++;
+            }
+        }
+        if (failures > 0)
+            return false;
+        return true;
+    }
+
+    public static T[] GetExpChangeArray<T>(int cSize)
+    {
+        T[] array = new T[cSize];
+
+        for (int i = array.Length - 1; i >= 0; --i)
+            array[i] = (T)Convert.ChangeType(array.Length - 1 - i, typeof(T));
+
+        return array;
+    }
+
+    public static bool CheckAndChangeArray<T>(ref T[] arrArg, ref T arrSize, int actualArrSize, int expectedArrSize)
+    {
+        T[] actualArr = InitArray<T>(actualArrSize);
+        if (!EqualArray<T>(arrArg, actualArrSize, actualArr, actualArrSize))
+        {
+            return false;
+        }
+
+        arrSize = (T)Convert.ChangeType(expectedArrSize, typeof(T));
+        arrArg = GetExpChangeArray<T>(expectedArrSize);
+        return true;
+    }
+
+    #endregion
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/helper.h b/tests/src/Interop/PInvoke/SizeParamIndex/ReversePInvoke/helper.h
new file mode 100644 (file)
index 0000000..9b0a6b0
--- /dev/null
@@ -0,0 +1,229 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// helper.h : Defines helper functions
+#include <xplatform.h>
+
+const LONG Array_Size = 10;
+const LONG CArray_Size = 20;
+
+//////////////////////////////////////////////////////////////////////////////
+// Verify helper methods
+//////////////////////////////////////////////////////////////////////////////
+template<typename T> 
+BOOL IsObjectEquals(T o1, T o2)
+{
+    // T::operator== required.
+    return o1 == o2;
+}
+
+//Int32 helper
+template<typename T>
+T* InitArray(SIZE_T arrSize)
+{
+    T* pExpectArr = (T*)CoreClrAlloc(sizeof(T)*arrSize);
+    for(SIZE_T i = 0;i<arrSize;++i)
+    {
+        pExpectArr[i] = (T)i;
+    }
+    return pExpectArr;
+}
+
+template<typename T>
+T* InitExpectedArray(SIZE_T arrSize)
+{
+    T* pExpectArr = (T*)CoreClrAlloc(sizeof(T)*arrSize);
+    for(SIZE_T i = 0;i<arrSize;++i)
+    {
+        pExpectArr[i] = (T)(arrSize - 1 - i);
+    }
+    return pExpectArr;
+}
+
+template<typename T>
+BOOL EqualArray(T* actualArray, SIZE_T actualSize, T* expectedArray, SIZE_T expectedSize)
+{
+    int failures = 0;
+
+    if(actualArray == NULL && expectedArray == NULL)
+    {
+        printf("Two arrays are equal. Both of them NULL\n");
+        return TRUE;
+    }
+    else if(actualArray == NULL && expectedArray != NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is NULL,but the Compared is not NULL!\n");
+        return FALSE;
+    }
+    else if(actualArray != NULL && expectedArray == NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is not NULL,but the Compared is NULL!\n");
+        return FALSE;
+    }
+    else if(actualSize != expectedSize)
+    {
+        printf("Two arrays aren't equal. The arrays size are not equal!\n");
+        return FALSE;
+    }
+    for(SIZE_T i = 0;i < actualSize; ++i)
+    {
+        if(actualArray[i] != expectedArray[i])
+        {
+            printf("Two arrays aren't equal.The value of index of %d isn't equal!\n",(int)i);
+            printf("\tThe value in actual   rray is %d\n",(int)actualArray[i]);
+            printf("\tThe value in expected array is %d\n",(int)expectedArray[i]);
+            failures++;
+        }
+    }
+    if( failures > 0 )
+        return FALSE;
+    return TRUE;
+}
+
+template<typename T>
+BOOL CheckAndChangeArrayByRef(T ** ppActual, T* Actual_Array_Size, SIZE_T Expected_Array_Size, SIZE_T Return_Array_Size)
+{
+    T* pExpectedArr = InitArray<T>(Expected_Array_Size);
+    if(!EqualArray(*ppActual, *Actual_Array_Size, pExpectedArr, Expected_Array_Size))
+    {
+        printf("ManagedtoNative Error in Method: %s!\n",__FUNCTION__);
+        return FALSE;
+    }
+
+    CoreClrFree(pExpectedArr);
+    CoreClrFree(*ppActual);
+    *ppActual = (T*)CoreClrAlloc(sizeof(T)*Return_Array_Size);
+
+    *Actual_Array_Size = ((T)Return_Array_Size);
+    for(SIZE_T i = 0; i < Return_Array_Size; ++i)
+    {
+        (*ppActual)[i] = Return_Array_Size - 1 - i;
+    }
+    return TRUE;
+}
+
+template<typename T>
+BOOL CheckAndChangeArrayByOut(T ** ppActual, T* Actual_Array_Size, SIZE_T Return_Array_Size)
+{
+    if(*ppActual != NULL )
+    {
+        printf("ManagedtoNative Error in Method: %s!\n",__FUNCTION__);
+        printf("Array is not NULL");
+        return FALSE;
+    }
+
+    CoreClrFree(*ppActual);
+    *ppActual = (T*)CoreClrAlloc(sizeof(T)*Return_Array_Size);
+
+    *Actual_Array_Size = ((T)Return_Array_Size);
+    for(SIZE_T i = 0; i < Return_Array_Size; ++i)
+    {
+        (*ppActual)[i] = Return_Array_Size - 1 - i;
+    }
+    return TRUE;
+}
+
+template<typename T>
+BOOL CheckArray(T* pReturnArr, SIZE_T actualArraySize, SIZE_T expectedArraySize)
+{
+    T* pExpectedArr = InitExpectedArray<T>(expectedArraySize);
+
+    if(!EqualArray(pReturnArr, actualArraySize, pExpectedArr, expectedArraySize))
+    {
+        printf("ManagedtoNative Error in Method: %s!\n",__FUNCTION__);
+        CoreClrFree(pExpectedArr);
+        return FALSE;
+    }
+    else
+    {
+        CoreClrFree(pExpectedArr);
+        return TRUE;
+    }
+}
+
+//BSTR helper
+#ifdef _WIN32
+template<> 
+BOOL IsObjectEquals(BSTR o1, BSTR o2)
+{
+    if ( o1 == NULL && o2 == NULL )
+        return TRUE;
+    else if ( o1 == NULL && o2 != NULL )
+        return FALSE;
+    else if ( o1 != NULL && o2 == NULL )
+        return FALSE;
+
+    UINT uLen1 = SysStringLen(o1);
+    UINT uLen2 = SysStringLen(o2);
+
+    if (uLen1 != uLen2 )
+        return FALSE;
+
+    return memcmp(o1, o2, uLen1) == 0;
+}
+
+BSTR ToBSTR(int i)
+{
+    BSTR bstrRet = NULL;
+    VarBstrFromI4(i, 0, 0, &bstrRet);
+
+    return bstrRet;
+}
+BOOL CmpBSTR(BSTR bstr1, BSTR bstr2)
+{
+    UINT uLen1 = SysStringLen(bstr1);
+    UINT uLen2 = SysStringLen(bstr2);
+
+    if (uLen1 != uLen2 )
+        return FALSE;
+    return memcmp(bstr1, bstr2, uLen1) == 0;
+}
+BSTR* InitArrayBSTR(LONG arrSize)
+{
+    BSTR* pExpectArr = (BSTR*)CoreClrAlloc(sizeof(BSTR)*arrSize);
+    for(LONG i = 0;i<arrSize;++i)
+    {
+        pExpectArr[i] = ToBSTR(i);
+    }
+    return pExpectArr;
+}
+BOOL EqualArrayBSTR(BSTR* actualBSTRArray, LONG actualSize, BSTR* expectedBSTRArray, LONG expectedSize)
+{
+    int failures = 0;
+
+    if(actualBSTRArray == NULL && expectedBSTRArray == NULL)
+    {
+        printf("Two arrays are equal. Both of them NULL\n");
+        return TRUE;
+    }
+    else if(actualBSTRArray == NULL && expectedBSTRArray != NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is NULL,but the Compared is not NULL!\n");
+        return FALSE;
+    }
+    else if(actualBSTRArray != NULL && expectedBSTRArray == NULL)
+    {
+        printf("Two arrays aren't equal. Array from Managed to Native is not NULL,but the Compared is NULL!\n");
+        return FALSE;
+    }
+    else if(actualSize != expectedSize)
+    {
+        printf("Two arrays aren't equal. The arrays size are not equal!\n");
+        return FALSE;
+    }
+    for(int i = 0;i < actualSize; ++i)
+    {
+        if(!CmpBSTR(actualBSTRArray[i],expectedBSTRArray[i]))
+        {
+            printf("Two arrays aren't equal.The value of index of %d isn't equal!\n",i);
+            printf("\tThe value in array from managed to native is %S\n",actualBSTRArray[i]);
+            printf("\tThe value in expected array is %S\n",expectedBSTRArray[i]);
+            failures++;
+        }
+    }
+    if(failures>0)
+        return FALSE;
+    return TRUE;
+}
+#endif
index 26b49ac..140150a 100755 (executable)
@@ -36,6 +36,9 @@ typedef char CHAR, *PCHAR;
 typedef unsigned short USHORT;
 typedef signed short SHORT;
 typedef unsigned short WORD, *PWORD, *LPWORD;
+typedef int LONG;
+
+typedef size_t SIZE_T;
 
 #ifndef TRUE
 #define TRUE 1