Add PInvoke/SafeHandles tests (#19330)
authorZeng Jiang <v-jiazen@microsoft.com>
Fri, 16 Nov 2018 01:49:41 +0000 (09:49 +0800)
committerJeremy Koritzinsky <jkoritzinsky@gmail.com>
Fri, 16 Nov 2018 01:49:41 +0000 (17:49 -0800)
* Add PInvoke/SafeHandles tests

* Fix compile warnings - include header file with relative path

* Run SafeHandle tests only on Windows. The tests are very Windows-centric, so we'd have to do quite a bit of a re-write to get them working xplat.

* Fix CMake project references.

* Fix interface CMakeLists project reference.

* Add casts for string literals.

* Use LPOLESTR and the W macro instead of OLECHAR* cast and L prefix.

29 files changed:
tests/src/Interop/CMakeLists.txt
tests/src/Interop/PInvoke/SafeHandles/AbstractDerived/AbstractDerivedTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/AbstractDerived/AbstractDerivedTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/CleanUpCheck/CleanUpCheckTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/CleanUpCheck/CleanUpCheckTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Default/DefaultTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Default/DefaultTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Helper.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Interface/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/InvalidMarshalAs/InvalidMarshalAsTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/InvalidMarshalAs/InvalidMarshalAsTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Misc.Unsupported/MiscUnsupportedTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Misc.Unsupported/MiscUnsupportedTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Misc/MiscTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/Misc/MiscTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/CMakeLists.txt [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/SafeFileHandle.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/SafeHandleNative.cpp [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/SigDiff/SigDiffTest.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/SigDiff/SigDiffTest.csproj [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/StructDefs.cs [new file with mode: 0644]
tests/src/Interop/PInvoke/SafeHandles/StructDefs.h [new file with mode: 0644]

index 27d3049..1ecb281 100644 (file)
@@ -59,6 +59,9 @@ add_subdirectory(ExecInDefAppDom)
 if(WIN32)
     add_subdirectory(PInvoke/Variant)
     add_subdirectory(PInvoke/Varargs)
+    add_subdirectory(PInvoke/SafeHandles)
+    add_subdirectory(PInvoke/SafeHandles/ReleaseHandle)
+    add_subdirectory(PInvoke/SafeHandles/Interface)
     add_subdirectory(PInvoke/NativeCallManagedComVisible)
     # This test doesn't necessarily need to be Windows-only, but the implementation is very tied to Windows APIs
     add_subdirectory(PInvoke/DateTime)
diff --git a/tests/src/Interop/PInvoke/SafeHandles/AbstractDerived/AbstractDerivedTest.cs b/tests/src/Interop/PInvoke/SafeHandles/AbstractDerived/AbstractDerivedTest.cs
new file mode 100644 (file)
index 0000000..fdbb931
--- /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.IO;
+using System.Runtime.InteropServices;
+using SafeHandlesTests;
+using TestLibrary;
+
+public abstract class AllMySafeHandles : SafeHandle
+{
+    private static readonly IntPtr _invalidHandleValue = new IntPtr(-1);
+
+    //0 or -1 considered invalid
+    public override bool IsInvalid
+    {
+        get { return handle == IntPtr.Zero || handle == _invalidHandleValue; }
+    }
+
+    protected AllMySafeHandles(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) { }
+}
+
+public sealed class MySafeEventHandle : AllMySafeHandles
+{
+    private MySafeEventHandle() : base(true) { }
+
+    [DllImport("api-ms-win-core-synch-l1-2-0", SetLastError = true, EntryPoint = "CreateEventW")]
+    internal static extern MySafeEventHandle CreateEvent(IntPtr mustBeZero, bool isManualReset, bool initialState, String name);
+
+    [DllImport("api-ms-win-core-synch-l1-2-0", SetLastError = true)]
+    internal static extern bool SetEvent(MySafeEventHandle handle);
+
+    [DllImport("api-ms-win-core-handle-l1-1-0")]
+    private static extern bool CloseHandle(IntPtr handle);
+
+    override protected bool ReleaseHandle()
+    {
+        return CloseHandle(handle);
+    }
+}
+
+public class AbstractDerivedSHTester
+{
+    private static void ClassHierarchyTest()
+    {
+        Console.WriteLine("Class hierarchy test...");
+
+        //create an event
+        Console.WriteLine("\tCreate new event");
+        MySafeEventHandle sh = MySafeEventHandle.CreateEvent(IntPtr.Zero, true, false, null);
+        Assert.IsFalse(sh.IsInvalid, "CreateEvent returned an invalid SafeHandle!");
+        Assert.IsTrue(MySafeEventHandle.SetEvent(sh), "SetEvent failed on a SafeHandle!");
+
+        Console.WriteLine("\tFirst test: Call dispose on sh");
+        sh.Dispose();
+        Console.WriteLine("\tCall succeeded.\n");
+
+        // Now create another event and force the critical finalizer to run.
+        Console.WriteLine("\tCreate new event");
+        sh = MySafeEventHandle.CreateEvent(IntPtr.Zero, false, true, null);
+        Assert.IsFalse(sh.IsInvalid, "CreateEvent returned an invalid SafeHandle!");
+
+        Console.WriteLine("\tSecond test: Force critical finalizer to run");
+        sh = null;
+        GC.Collect();
+        GC.WaitForPendingFinalizers();
+        Console.WriteLine("\tSucceeded.\n");
+    }
+
+    private static int Main()
+    {
+        try
+        {
+            ClassHierarchyTest();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SafeHandles/AbstractDerived/AbstractDerivedTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/AbstractDerived/AbstractDerivedTest.csproj
new file mode 100644 (file)
index 0000000..f3080e2
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>AbstractDerivedTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="AbstractDerivedTest.cs" />
+    <Compile Include="..\*.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/SafeHandles/CMakeLists.txt b/tests/src/Interop/PInvoke/SafeHandles/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6c1321e
--- /dev/null
@@ -0,0 +1,15 @@
+#VCXPROJ 
+cmake_minimum_required (VERSION 2.6) 
+project (PInvoke_SafeHandle) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") 
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    SafeHandleNative.cpp 
+) 
+# Additional files to reference: 
+#    StructDefs.h 
+#    SafeHandleNative.def 
+# add the executable 
+add_library (PInvoke_SafeHandle SHARED ${SOURCES}) 
+# add the install targets 
+install (TARGETS PInvoke_SafeHandle DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/SafeHandles/CleanUpCheck/CleanUpCheckTest.cs b/tests/src/Interop/PInvoke/SafeHandles/CleanUpCheck/CleanUpCheckTest.cs
new file mode 100644 (file)
index 0000000..d7f61eb
--- /dev/null
@@ -0,0 +1,161 @@
+// 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 SafeHandlesTests;
+using TestLibrary;
+
+public class SHTester
+{
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_In([In]SafeFileHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Out(out SFH_NoCloseHandle sh1);
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern SFH_NoCloseHandle SHParam_OutRetVal();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Ref(ref SFH_NoCloseHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Multiple([In]SafeFileHandle sh1, out SFH_NoCloseHandle sh2, ref SFH_NoCloseHandle sh3, Int32 sh1Value, Int32 sh3Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_In([In]StructWithSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Out(out StructWithSHFld s);
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern StructWithSHFld SHStructParam_OutRetVal();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref1(ref StructWithSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref2(ref StructWithSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple1([In]StructWithSHFld sh1, out StructWithSHFld sh2, ref StructWithSHFld sh3, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple2([In]StructWithSHFld sh1, ref StructWithSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple3([In]StructWithSHFld sh1, ref StructWithSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    public static int Main()
+    {
+        try
+        {
+            RunSHParamTests();
+            RunSHStructParamTests();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+
+    /// <summary>
+    ///passing SH subclass parameters to unmanaged code in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it 
+    ///1- passing SH subclass parameters individually in separate methods (In, out, ref)
+    ///2- passing SH subclass parameters in combination in the same method
+    /// </summary>
+    public static void RunSHParamTests()
+    {
+        Console.WriteLine("\nRunSHParamTests():");
+        ///1- passing SH subclass parameters individually in separate methods (In, out, ref)
+        //get a new SH
+        SafeFileHandle hnd = Helper.NewSFH();
+        Int32 hndInt32 = Helper.SHInt32(hnd); //get the 32-bit value associated with hnd
+
+        Console.WriteLine("Testing SHParam_In...");
+        SHParam_In(hnd, hndInt32);
+        CheckCleanUp(hnd);
+
+        SFH_NoCloseHandle hndout = Helper.NewSFH_NoCloseHandle(); //get a new value
+        SafeHandle hnd_ref_backup = hndout;
+        hndInt32 = Helper.SHInt32(hndout);
+        Console.WriteLine("Testing SHParam_Ref...");
+        SHParam_Ref(ref hndout, hndInt32);
+        CheckCleanUp(hnd_ref_backup);
+
+        ///2- passing SH subclass parameters in combination in the same method
+        //initialize parameters
+        SafeFileHandle hnd1 = Helper.NewSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(hnd1); //get the 32-bit value associated with hnd1
+
+        SFH_NoCloseHandle hnd2 = null; //out parameter
+
+        SFH_NoCloseHandle hnd3 = Helper.NewSFH_NoCloseHandle();
+        SafeHandle hnd3_ref_backup = hnd3;
+        Int32 hnd3Int32 = Helper.SHInt32(hnd3); //get the 32-bit value associated with hnd3
+
+        Console.WriteLine("Testing SHParam_Multiple...");
+        SHParam_Multiple(hnd1, out hnd2, ref hnd3, hnd1Int32, hnd3Int32);
+        CheckCleanUp(hnd1);
+        CheckCleanUp(hnd2);
+        CheckCleanUp(hnd3_ref_backup);
+    }
+
+    /// <summary>
+    ///passing structures (with SH subclass fields) as parameters in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it
+    ///1- passing structures (In, out, ref) (with SH subclass fields) individually in separate methods
+    ///2- passing structures (In, out, ref) (with SH subclass fields) in combination in the same method
+    /// </summary>
+    public static void RunSHStructParamTests()
+    {
+        Console.WriteLine("\nRunSHStructParamTests():");
+        ///1- passing structures (In, out, ref) (with SH subclass fields) individually in separate methods
+        ///
+        ///
+
+        //initialize a new StructWithSHFld
+        StructWithSHFld s = new StructWithSHFld();
+        s.hnd = Helper.NewSFH(); //get a new SH
+        Int32 hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+
+        Console.WriteLine("Testing SHStructParam_In...");
+        SHStructParam_In(s, hndInt32);
+        CheckCleanUp(s.hnd);
+
+        s.hnd = Helper.NewSFH(); //get a new SH
+        SafeHandle hnd_ref_backup = s.hnd;
+        hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+        Console.WriteLine("Testing SHStructParam_Ref1 (does not change value of handle field)...");
+        SHStructParam_Ref1(ref s, hndInt32);
+        CheckCleanUp(hnd_ref_backup);
+
+        ///2- passing structures (In, out, ref) (with SH subclass fields) in combination in the same method
+        ///
+        ///
+        //initialize parameters
+        StructWithSHFld s1 = new StructWithSHFld();
+        s1.hnd = Helper.NewSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(s1.hnd); //get the 32-bit value associated with s1.hnd
+
+        StructWithSHFld s3 = new StructWithSHFld();
+        s3.hnd = Helper.NewSFH();
+        Int32 hnd3Int32 = Helper.SHInt32(s3.hnd); //get the 32-bit value associated with s3.hnd
+        SafeHandle hnd3_ref_backup = s3.hnd;
+        Console.WriteLine("Testing SHStructParam_Multiple2 (takes a ref struct as one of the params)...");
+        SHStructParam_Multiple2(s1, ref s3, hnd1Int32, hnd3Int32);
+        CheckCleanUp(s1.hnd);
+        CheckCleanUp(hnd3_ref_backup);
+    }
+
+    static void CheckCleanUp(SafeHandle hnd)
+    {
+        Assert.IsFalse(hnd.IsClosed, "Failed: SafeFileHandle Closed before calling close");
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SafeHandles/CleanUpCheck/CleanUpCheckTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/CleanUpCheck/CleanUpCheckTest.csproj
new file mode 100644 (file)
index 0000000..ae60938
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>CleanUpCheckTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="CleanUpCheckTest.cs" />
+    <Compile Include="..\*.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/SafeHandles/Default/DefaultTest.cs b/tests/src/Interop/PInvoke/SafeHandles/Default/DefaultTest.cs
new file mode 100644 (file)
index 0000000..9e864fd
--- /dev/null
@@ -0,0 +1,171 @@
+// 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 SafeHandlesTests;
+using TestLibrary;
+
+public class SHTester
+{
+    public static int Main()
+    {
+        try
+        {
+            RunSHParamTests();
+            RunSHStructParamTests();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_In([In]SafeFileHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Out(out SFH_NoCloseHandle sh1);
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern SFH_NoCloseHandle SHParam_OutRetVal();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Ref(ref SFH_NoCloseHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Multiple([In]SafeFileHandle sh1, out SFH_NoCloseHandle sh2, ref SFH_NoCloseHandle sh3, Int32 sh1Value, Int32 sh3Value);
+
+    /// <summary>
+    ///passing SH subclass parameters to unmanaged code in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it 
+    ///1-      passing SH subclass parameters individually in separate methods (In, out, ref)
+    ///2-      passing SH subclass parameters in combination in the same method
+    /// </summary>
+    public static void RunSHParamTests()
+    {
+        Console.WriteLine("SHParam_In");
+        SafeFileHandle hnd = Helper.NewSFH();    //get a new SH
+        Int32 hndInt32 = Helper.SHInt32(hnd); //get the 32-bit value associated with hnd
+        Assert.IsTrue(SHParam_In(hnd, hndInt32), "SHParam_In did not receive hnd as expected");
+        Assert.IsTrue(!Helper.IsChanged(hnd), "SHParam_In did not return hnd as expected.");
+
+        Console.WriteLine("SHParam_Out");
+        SFH_NoCloseHandle hndout;
+        SHParam_Out(out hndout);
+        Assert.IsTrue(Helper.IsChanged(hndout), "SHParam_Out did not return hndout as expected. hndout = " + Helper.SHInt32(hndout));
+
+        Console.WriteLine("SHParam_OutRetVal");
+        hndout = null;
+        hndout = SHParam_OutRetVal();
+        Assert.IsTrue(Helper.IsChanged(hndout), "SHParam_OutRetVal did not return hndout as expected.");
+
+        Console.WriteLine("SHParam_Ref");
+        hndout = Helper.NewSFH_NoCloseHandle(); //get a new value
+        hndInt32 = Helper.SHInt32(hndout);
+        Assert.IsTrue(SHParam_Ref(ref hndout, hndInt32), "SHParam_Ref did not receive hndout as expected");
+        Assert.IsTrue(Helper.IsChanged(hndout), "SHParam_Ref did not return hndout as expected.");
+
+        Console.WriteLine("SHParam_Multiple");
+        SafeFileHandle hnd1 = Helper.NewSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(hnd1); //get the 32-bit value associated with hnd1
+        SFH_NoCloseHandle hnd2 = null; //out parameter
+        SFH_NoCloseHandle hnd3 = Helper.NewSFH_NoCloseHandle();
+        Int32 hnd3Int32 = Helper.SHInt32(hnd3); //get the 32-bit value associated with hnd3
+
+        Assert.IsTrue(SHParam_Multiple(hnd1, out hnd2, ref hnd3, hnd1Int32, hnd3Int32), "SHParam_Multiple did not receive parameter(s) as expected.");
+
+        Assert.IsTrue(Helper.IsChanged(hnd2), "HParam_Multiple did not return hnd2 as expected.");
+        Assert.IsTrue(Helper.IsChanged(hnd3), "HParam_Multiple did not return hnd3 as expected.");
+    }
+
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_In([In]StructWithSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Out(out StructWithSHFld s);
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern StructWithSHFld SHStructParam_OutRetVal();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref1(ref StructWithSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref2(ref StructWithSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple1([In]StructWithSHFld sh1, out StructWithSHFld sh2, ref StructWithSHFld sh3, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple2([In]StructWithSHFld sh1, ref StructWithSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple3([In]StructWithSHFld sh1, ref StructWithSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    /// <summary>
+    ///passing structures (with SH subclass fields) as parameters in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it
+    ///1-      passing structures (In, out, ref) (with SH subclass fields) individually in separate methods
+    ///2-      passing structures (In, out, ref) (with SH subclass fields) in combination in the same method
+    /// </summary>
+    public static void RunSHStructParamTests()
+    {
+        Console.WriteLine("\nRunSHStructParamTests():");
+
+        Console.WriteLine("SHStructParam_In");
+        StructWithSHFld s = new StructWithSHFld();//initialize a new StructWithSHFld
+        s.hnd = Helper.NewSFH(); //get a new SH
+        Int32 hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+        Assert.IsTrue(SHStructParam_In(s, hndInt32), "SHStructParam_In did not receive param as expected.");
+        Assert.IsTrue(!Helper.IsChanged(s.hnd), "SHStructParam_In did not return param as expected.");//check that the value of the HANDLE field did not change
+
+        Console.WriteLine("SHStructParam_Out");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Out(out s), "SHStructParam_Out");
+
+        Console.WriteLine("SHStructParam_OutRetVal");
+        //mangling the unmanaged signature and returning the structure should cause this exception since PInvoke does not support returning
+        //non-blittable value classes
+        Assert.Throws<MarshalDirectiveException>(() => SHStructParam_OutRetVal(), "SHStructParam_OutRetVal");
+
+        Console.WriteLine("SHStructParam_Ref1"); //Testing SHStructParam_Ref1 (does not change value of handle field)
+        s.hnd = Helper.NewSFH(); //get a new SH
+        hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+        Assert.IsTrue(SHStructParam_Ref1(ref s, hndInt32), "SHStructParam_Ref1 did not receive param as expected.");
+        Assert.IsTrue(!Helper.IsChanged(s.hnd), "SHStructParam_Ref1 did not return param as expected."); //check that the value of the HANDLE field is not changed
+
+        Console.WriteLine("SHStructParam_Ref2");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Ref2(ref s, hndInt32), "SHStructParam_Ref2");
+        ///
+        ///2-   passing structures (In, out, ref) (with SH subclass fields) in combination in the same method
+        ///
+
+        StructWithSHFld s1 = new StructWithSHFld();//initialize parameters
+        s1.hnd = Helper.NewSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(s1.hnd); //get the 32-bit value associated with s1.hnd
+
+        StructWithSHFld s2; //out parameter
+
+        StructWithSHFld s3 = new StructWithSHFld();
+        s3.hnd = Helper.NewSFH();
+        Int32 hnd3Int32 = Helper.SHInt32(s3.hnd); //get the 32-bit value associated with s3.hnd
+
+        Console.WriteLine("SHStructParam_Multiple1: takes an out struct as one of the params and so is expected to result in an exception");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Multiple1(s1, out s2, ref s3, hnd1Int32, hnd3Int32), "SHStructParam_Multiple1");
+
+        Console.WriteLine("SHStructParam_Multiple2:takes a ref struct as one of the params");
+        s3.hnd = Helper.NewSFH();
+        hnd3Int32 = Helper.SHInt32(s3.hnd); //get the 32-bit value associated with s3.hnd
+        Assert.IsTrue(SHStructParam_Multiple2(s1, ref s3, hnd1Int32, hnd3Int32), "SHStructParam_Multiple2 did not receive parameter(s) as expected.");
+        Assert.IsTrue(!Helper.IsChanged(s1.hnd), "SHStructParam_Multiple2 did not return s1.hnd as expected.");
+        Assert.IsTrue(!Helper.IsChanged(s3.hnd), "SHStructParam_Multiple2 did not return s3.hnd as expected.");
+
+        Console.WriteLine("SHStructParam_Multiple3:takes a ref struct as one of the params aand changes its handle field and so is expected to result in an exception");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Multiple3(s1, ref s3, hnd1Int32, hnd3Int32), "SHStructParam_Multiple3");
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SafeHandles/Default/DefaultTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/Default/DefaultTest.csproj
new file mode 100644 (file)
index 0000000..067e960
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>DefaultTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="DefaultTest.cs" />
+    <Compile Include="..\*.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/SafeHandles/Helper.cs b/tests/src/Interop/PInvoke/SafeHandles/Helper.cs
new file mode 100644 (file)
index 0000000..cc22959
--- /dev/null
@@ -0,0 +1,271 @@
+// 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 SafeHandlesTests;
+
+namespace SafeHandlesTests{
+    public class Helper
+    {
+        public const int N = 3;
+        public const int ReturnValue = 123;
+
+        /// <summary>
+        /// Creates and returns a new SFH_NoCloseHandle
+        /// </summary>
+        /// <returns></returns>
+        public static SFH_NoCloseHandle NewSFH_NoCloseHandle()
+        {
+            String lpFileName = "C.txt";
+            DesiredAccess dwDesiredAccess = DesiredAccess.GENERIC_WRITE;
+            ShareMode dwShareMode = ShareMode.FILE_SHARE_WRITE;
+            IntPtr lpSecurityAttributes = IntPtr.Zero;
+            CreationDisposition dwCreationDisposition = CreationDisposition.CREATE_ALWAYS;
+            FlagsAndAttributes dwFlagsAndAttributes = FlagsAndAttributes.None;
+            IntPtr hTemplateFile = IntPtr.Zero;
+
+            //create the handle
+            SFH_NoCloseHandle hnd = SFH_NoCloseHandle.CreateFile(lpFileName, dwDesiredAccess, dwShareMode,
+                lpSecurityAttributes, dwCreationDisposition,
+                dwFlagsAndAttributes, hTemplateFile);
+
+            return hnd;
+        }
+
+        /// <summary>
+        /// Creates and returns a new ChildSFH_NoCloseHandle
+        /// </summary>
+        /// <returns></returns>
+        public static ChildSFH_NoCloseHandle NewChildSFH_NoCloseHandle()
+        {
+            String lpFileName = "D.txt";
+            DesiredAccess dwDesiredAccess = DesiredAccess.GENERIC_WRITE;
+            ShareMode dwShareMode = ShareMode.FILE_SHARE_WRITE;
+            IntPtr lpSecurityAttributes = IntPtr.Zero;
+            CreationDisposition dwCreationDisposition = CreationDisposition.CREATE_ALWAYS;
+            FlagsAndAttributes dwFlagsAndAttributes = FlagsAndAttributes.None;
+            IntPtr hTemplateFile = IntPtr.Zero;
+
+            //create the handle
+            ChildSFH_NoCloseHandle hnd = ChildSFH_NoCloseHandle.CreateChildSafeFileHandle(lpFileName, dwDesiredAccess, dwShareMode,
+                lpSecurityAttributes, dwCreationDisposition,
+                dwFlagsAndAttributes, hTemplateFile);
+
+            return hnd;
+        }
+
+        /// <summary>
+        /// Creates and returns a new ChildSafeFileHandle
+        /// </summary>
+        /// <returns></returns>
+        public static ChildSafeFileHandle NewChildSFH()
+        {
+            String lpFileName = "B.txt";
+            DesiredAccess dwDesiredAccess = DesiredAccess.GENERIC_WRITE;
+            ShareMode dwShareMode = ShareMode.FILE_SHARE_WRITE;
+            IntPtr lpSecurityAttributes = IntPtr.Zero;
+            CreationDisposition dwCreationDisposition = CreationDisposition.CREATE_ALWAYS;
+            FlagsAndAttributes dwFlagsAndAttributes = FlagsAndAttributes.None;
+            IntPtr hTemplateFile = IntPtr.Zero;
+
+            //create the handle
+            ChildSafeFileHandle hnd = ChildSafeFileHandle.CreateChildSafeFileHandle(lpFileName, dwDesiredAccess, dwShareMode,
+                lpSecurityAttributes, dwCreationDisposition,
+                dwFlagsAndAttributes, hTemplateFile);
+
+            return hnd;
+        }
+
+        /// <summary>
+        /// Creates and returns a new SafeFileHandle
+        /// </summary>
+        /// <returns></returns>
+        public static SafeFileHandle NewSFH()
+        {
+            String lpFileName = "A.txt";
+            DesiredAccess dwDesiredAccess = DesiredAccess.GENERIC_WRITE;
+            ShareMode dwShareMode = ShareMode.FILE_SHARE_WRITE;
+            IntPtr lpSecurityAttributes = IntPtr.Zero;
+            CreationDisposition dwCreationDisposition = CreationDisposition.CREATE_ALWAYS;
+            FlagsAndAttributes dwFlagsAndAttributes = FlagsAndAttributes.None;
+            IntPtr hTemplateFile = IntPtr.Zero;
+
+            //create the handle
+            SafeFileHandle hnd = SafeFileHandle.CreateFile(lpFileName, dwDesiredAccess, dwShareMode,
+                lpSecurityAttributes, dwCreationDisposition,
+                dwFlagsAndAttributes, hTemplateFile);
+
+            return hnd;
+        }
+
+        /// <summary>
+        /// Returns the Int32 value associated with a SFH_NoCloseHandle
+        /// </summary>
+        /// <returns></returns>
+        public static Int32 SHInt32(SFH_NoCloseHandle hnd)
+        {
+            IntPtr hndIntPtr = hnd.DangerousGetHandle(); //get the IntPtr associated with hnd
+            return hndIntPtr.ToInt32(); //return the 32-bit value associated with hnd
+        }
+
+        /// <summary>
+        /// Returns the Int32 value associated with a ChildSFH_NoCloseHandle
+        /// </summary>
+        /// <returns></returns>
+        public static Int32 SHInt32(ChildSFH_NoCloseHandle hnd)
+        {
+            IntPtr hndIntPtr = hnd.DangerousGetHandle(); //get the IntPtr associated with hnd
+            return hndIntPtr.ToInt32(); //return the 32-bit value associated with hnd
+        }
+
+        /// <summary>
+        /// Returns the Int32 value associated with a ChildSafeFileHandle
+        /// </summary>
+        /// <returns></returns>
+        public static Int32 SHInt32(ChildSafeFileHandle hnd)
+        {
+            IntPtr hndIntPtr = hnd.DangerousGetHandle(); //get the IntPtr associated with hnd
+            return hndIntPtr.ToInt32(); //return the 32-bit value associated with hnd
+        }
+
+        /// <summary>
+        /// Returns the Int32 value associated with a SafeFileHandle
+        /// </summary>
+        /// <returns></returns>
+        public static Int32 SHInt32(SafeFileHandle hnd)
+        {
+            IntPtr hndIntPtr = hnd.DangerousGetHandle(); //get the IntPtr associated with hnd
+            return hndIntPtr.ToInt32(); //return the 32-bit value associated with hnd
+        }
+
+        /// <summary>
+        /// Returns the Int32 value associated with a SafeHandle
+        /// </summary>
+        /// <returns></returns>
+        public static Int32 SHInt32(SafeHandle hnd)
+        {
+            IntPtr hndIntPtr = hnd.DangerousGetHandle(); //get the IntPtr associated with hnd
+            return hndIntPtr.ToInt32(); //return the 32-bit value associated with hnd
+        }
+
+        /// <summary>
+        /// Returns true if SH subclass (SFH_NoCloseHandle) value has changed else returns false
+        /// </summary>
+        /// <returns></returns>
+        public static bool IsChanged(SFH_NoCloseHandle hnd)
+        {
+            Int32 hndInt32 = SHInt32(hnd); //get the 32-bit value associated with hnd
+            if (hndInt32 == ReturnValue)
+                return true;
+            return false;
+        }
+
+        /// <summary>
+        /// Returns true if ChildSFH_NoCloseHandle value has changed else returns false
+        /// </summary>
+        /// <returns></returns>
+        public static bool IsChanged(ChildSFH_NoCloseHandle hnd)
+        {
+            Int32 hndInt32 = SHInt32(hnd); //get the 32-bit value associated with hnd
+            if (hndInt32 == ReturnValue)
+                return true;
+            return false;
+        }
+
+        /// <summary>
+        /// Returns true if SafeFileHandle subclass value has changed else returns false
+        /// </summary>
+        /// <returns></returns>
+        public static bool IsChanged(ChildSafeFileHandle hnd)
+        {
+            Int32 hndInt32 = SHInt32(hnd); //get the 32-bit value associated with hnd
+            if (hndInt32 == ReturnValue)
+                return true;
+            return false;
+        }
+
+        /// <summary>
+        /// Returns true if SH subclass value has changed else returns false
+        /// </summary>
+        /// <returns></returns>
+        public static bool IsChanged(SafeFileHandle hnd)
+        {
+            Int32 hndInt32 = SHInt32(hnd); //get the 32-bit value associated with hnd
+            if (hndInt32 == ReturnValue)
+                return true;
+            return false;
+        }
+
+        /// <summary>
+        /// Returns true if SH value has changed else returns false
+        /// </summary>
+        /// <returns></returns>
+        public static bool IsChanged(SafeHandle hnd)
+        {
+            Int32 hndInt32 = SHInt32(hnd); //get the 32-bit value associated with hnd
+            if (hndInt32 == ReturnValue)
+                return true;
+            return false;
+        }
+
+        /// <summary>
+        /// Creates a new StructWithManySHFlds; Fills in arrInt32s with the 32-bit values
+        /// of the handle flds of the struct
+        /// </summary>
+        /// <returns>StructWithManySHFlds</returns>
+        public static StructWithManySHFlds NewStructWithManySHFlds(ref Int32[] arrInt32s)
+        {
+            arrInt32s = new Int32[15]; //size corresponds to the number of flds
+            StructWithManySHFlds s = new StructWithManySHFlds();
+            s.hnd1 = NewSFH(); //get a new SH
+            arrInt32s[0] = SHInt32(s.hnd1);
+            s.hnd2 = NewSFH(); //get a new SH
+            arrInt32s[1] = SHInt32(s.hnd2);
+            s.hnd3 = NewChildSFH(); //get a new SH
+            arrInt32s[2] = SHInt32(s.hnd3);
+            s.hnd4 = NewSFH(); //get a new SH
+            arrInt32s[3] = SHInt32(s.hnd4);
+            s.hnd5 = NewSFH(); //get a new SH
+            arrInt32s[4] = SHInt32(s.hnd5);
+            s.hnd6 = NewChildSFH(); //get a new SH
+            arrInt32s[5] = SHInt32(s.hnd6);
+            s.hnd7 = NewSFH(); //get a new SH
+            arrInt32s[6] = SHInt32(s.hnd7);
+            s.hnd8 = NewSFH(); //get a new SH
+            arrInt32s[7] = SHInt32(s.hnd8);
+            s.hnd9 = NewChildSFH(); //get a new SH
+            arrInt32s[8] = SHInt32(s.hnd9);
+            s.hnd10 = NewSFH(); //get a new SH
+            arrInt32s[9] = SHInt32(s.hnd10);
+            s.hnd11 = NewSFH(); //get a new SH
+            arrInt32s[10] = SHInt32(s.hnd11);
+            s.hnd12 = NewChildSFH(); //get a new SH
+            arrInt32s[11] = SHInt32(s.hnd12);
+            s.hnd13 = NewSFH(); //get a new SH
+            arrInt32s[12] = SHInt32(s.hnd13);
+            s.hnd14 = NewSFH(); //get a new SH
+            arrInt32s[13] = SHInt32(s.hnd14);
+            s.hnd15 = NewChildSFH(); //get a new SH
+            arrInt32s[14] = SHInt32(s.hnd15);
+
+            return s;
+        }
+
+        /// <summary>
+        /// Returns true if any of the handle flds has changed else returns false
+        /// </summary>
+        public static bool IsChangedStructWithManySHFlds(StructWithManySHFlds s, Int32[] arrInt32s)
+        {
+            if (SHInt32(s.hnd1) != arrInt32s[0] || SHInt32(s.hnd2) != arrInt32s[1] || SHInt32(s.hnd3) != arrInt32s[2] ||
+                SHInt32(s.hnd4) != arrInt32s[3] || SHInt32(s.hnd5) != arrInt32s[4] || SHInt32(s.hnd6) != arrInt32s[5] ||
+                SHInt32(s.hnd7) != arrInt32s[6] || SHInt32(s.hnd8) != arrInt32s[7] || SHInt32(s.hnd9) != arrInt32s[8] ||
+                SHInt32(s.hnd10) != arrInt32s[9] || SHInt32(s.hnd11) != arrInt32s[10] || SHInt32(s.hnd12) != arrInt32s[11] ||
+                SHInt32(s.hnd13) != arrInt32s[12] || SHInt32(s.hnd14) != arrInt32s[13] || SHInt32(s.hnd15) != arrInt32s[14])
+                return true;
+            return false;
+        }
+
+    } //end of class Helper
+}
diff --git a/tests/src/Interop/PInvoke/SafeHandles/Interface/CMakeLists.txt b/tests/src/Interop/PInvoke/SafeHandles/Interface/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8fd1a0f
--- /dev/null
@@ -0,0 +1,15 @@
+#VCXPROJ 
+cmake_minimum_required (VERSION 2.6) 
+project (PInvoke_SafeHandle_MarshalAs_Interface) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+include_directories("..")
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    InterfaceNative.cpp 
+) 
+# Additional files to reference: 
+#    InterfaceNative.def 
+# add the executable 
+add_library (PInvoke_SafeHandle_MarshalAs_Interface SHARED ${SOURCES}) 
+# add the install targets 
+install (TARGETS PInvoke_SafeHandle_MarshalAs_Interface DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceNative.cpp b/tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceNative.cpp
new file mode 100644 (file)
index 0000000..eaf5c8f
--- /dev/null
@@ -0,0 +1,616 @@
+// 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.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <oleauto.h>
+#include <xplatform.h>
+#include "StructDefs.h"
+
+/////////////////////////////////Internal Helper Methods
+/////////////////////////////////
+BOOL static CheckGetIDsOfNamesHR(HRESULT hr)
+{
+    if( FAILED(hr) )
+    {
+        printf("\t\t\tGetIDsOfNames Call FAILED!\n");
+        if( hr == E_OUTOFMEMORY )
+            printf("\t\t\thr == E_OUTOFMEMORY\n");
+        else if( hr == DISP_E_UNKNOWNNAME )
+            printf("\t\t\thr == DISP_E_UNKNOWNNAME\n");
+        else 
+            printf("\t\t\thr == DISP_E_UNKNOWNLCID\n");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+BOOL static CheckInvokeHR(HRESULT hr)
+{
+    if( FAILED(hr) )
+    {
+        printf("\t\t\tInvoke FAILED!\n");
+        if( hr == DISP_E_BADPARAMCOUNT )
+            printf("\t\t\thr == DISP_E_BADPARAMCOUNT\n");
+        else if( hr == DISP_E_EXCEPTION )
+            printf("\t\t\thr == DISP_E_EXCEPTION\n");
+        else if( hr == DISP_E_MEMBERNOTFOUND )
+            printf("\t\t\thr == DISP_E_MEMBERNOTFOUND\n");
+        else if( hr == DISP_E_UNKNOWNINTERFACE )
+            printf("\t\t\thr == DISP_E_UNKNOWNINTERFACE\n");
+        else if( hr == DISP_E_BADVARTYPE )
+            printf("\t\t\thr == DISP_E_BADVARTYPE\n");
+        else if( hr == DISP_E_NONAMEDARGS )
+            printf("\t\t\thr == DISP_E_NONAMEDARGS\n");
+        else if( hr == DISP_E_OVERFLOW )
+            printf("\t\t\thr == DISP_E_OVERFLOW\n");
+        else if( hr == DISP_E_PARAMNOTFOUND )
+            printf("\t\t\thr == DISP_E_PARAMNOTFOUND\n");
+        else if( hr == DISP_E_TYPEMISMATCH )
+            printf("\t\t\thr == DISP_E_TYPEMISMATCH\n");
+        else if( hr == DISP_E_UNKNOWNLCID )
+            printf("\t\t\thr == DISP_E_UNKNOWNLCID\n");
+        else 
+            printf("\t\t\thr == DISP_E_PARAMNOTOPTIONAL\n");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+BOOL static InvokeAllFldsAndProps(IDispatch* ptoIntf, long shfld1Val, long shfld2Val)
+{
+    HRESULT hr;
+    DISPID dispid_DangerousGetHandle, dispid_IsInvalid, dispid_shfld1, dispid_shfld2_prop;
+    OLECHAR FAR* szMember;
+    VARIANTARG varargs;
+    DISPPARAMS DispParams = {&varargs, NULL, 0, 0};
+    VARIANT  VarResult, VarResultBool, VarResultDangHnd;
+
+    printf("\t\t\tCalling GetIDsOfNames() for shfld1...\n");
+    szMember = (LPOLESTR)W("shfld1");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_shfld1);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    printf("\t\t\tCalling GetIDsOfNames() for shfld2_prop...\n");
+    szMember = (LPOLESTR)W("shfld2_prop");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_shfld2_prop);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    printf("\n");
+
+    //////////////////////////////////////////////////////////////////
+    //NOTE: The SH fld is being returned as type VT_DISPATCH
+    /* printf("\t\t\tInvoking shfld1...\n");
+    hr = ptoIntf->Invoke(dispid_shfld1, 
+    IID_NULL, 
+    LOCALE_SYSTEM_DEFAULT, 
+    DISPATCH_PROPERTYGET, 
+    &DispParams, 
+    &VarResult, 
+    NULL, 
+    NULL);
+    if( !CheckInvokeHR(hr) )
+    return FALSE;
+    else
+    printf("\t\t\tCall completed successfully.\n");
+    if( VarResult.vt != VT_INT || VarResult.intVal != shfld1Val )
+    {
+    printf("\t\t\t\tThe return Variant is incorrect!\n");
+    return FALSE;
+    }
+    */
+
+    //Get the property
+    printf("\t\t\tInvoking shfld2_prop (Getting)...\n");
+    hr = ptoIntf->Invoke(dispid_shfld2_prop, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_PROPERTYGET, 
+        &DispParams, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+    if( VarResult.vt != VT_DISPATCH )
+    {
+        printf("\t\t\t\tThe return Variant is incorrect!\n");
+        return FALSE;
+    }
+    else //invoke DangerousGetHandle on this SH property using VarResult.pdispVal
+    {
+        printf("\t\t\t\tCalling GetIDsOfNames() for IsInvalid (shfld2_prop)...\n");
+        szMember = (LPOLESTR)W("IsInvalid");
+        hr = (VarResult.pdispVal)->GetIDsOfNames(IID_NULL, 
+            &szMember, 
+            1, 
+            LOCALE_SYSTEM_DEFAULT, 
+            &dispid_IsInvalid);
+        if( !CheckGetIDsOfNamesHR(hr) )
+            return FALSE;
+        else
+            printf("\t\t\t\tCall completed successfully.\n");
+
+        printf("\t\t\t\tInvoking IsInValid (shfld2_prop)...\n");
+        hr = (VarResult.pdispVal)->Invoke(dispid_IsInvalid, 
+            IID_NULL, 
+            LOCALE_SYSTEM_DEFAULT, 
+            DISPATCH_PROPERTYGET, 
+            &DispParams, 
+            &VarResultBool, 
+            NULL, 
+            NULL);
+        if( !CheckInvokeHR(hr) )
+            return FALSE;
+        else
+            printf("\t\t\t\tCall completed successfully.\n");
+        if( VarResultBool.vt != VT_BOOL || VarResultBool.bVal != 0 ) //should be valid
+        {
+            printf("\t\t\t\t\tThe return Variant is incorrect!\n");
+            return FALSE;
+        }
+
+        printf("\t\t\t\tCalling GetIDsOfNames() for DangerousGetHandle (shfld2_prop)...\n");
+        szMember = (LPOLESTR)W("DangerousGetHandle");
+        hr = (VarResult.pdispVal)->GetIDsOfNames(IID_NULL, 
+            &szMember, 
+            1, 
+            LOCALE_SYSTEM_DEFAULT, 
+            &dispid_DangerousGetHandle);
+        if( !CheckGetIDsOfNamesHR(hr) )
+            return FALSE;
+        else
+            printf("\t\t\t\tCall completed successfully.\n");
+
+        printf("\t\t\t\tInvoking DangerousGetHandle (shfld2_prop)...\n");
+        hr = (VarResult.pdispVal)->Invoke(dispid_DangerousGetHandle, 
+            IID_NULL, 
+            LOCALE_SYSTEM_DEFAULT, 
+            DISPATCH_METHOD, 
+            &DispParams, 
+            &VarResultDangHnd, 
+            NULL, 
+            NULL);
+        if( !CheckInvokeHR(hr) )
+            return FALSE;
+        else
+            printf("\t\t\t\tCall completed successfully.\n");
+        if( VarResultDangHnd.vt != VT_INT || VarResultDangHnd.intVal != shfld2Val ) 
+        {
+            printf("\t\t\t\t\tThe return Variant is incorrect!\n");
+            return FALSE;
+        }
+
+    } //end of else
+
+    //Set the property to a SFH interface
+    DISPPARAMS DispParams_prop = {&varargs,NULL, 1, 0};
+    DispParams_prop.rgvarg[0].vt = VT_DISPATCH;
+    DispParams_prop.rgvarg[0].pdispVal = ptoIntf; //Set the value of the property to its parent pointer
+    printf("\t\t\tInvoking shfld2_prop (Setting)...\n");
+    hr = ptoIntf->Invoke(dispid_shfld2_prop, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_PROPERTYPUT, 
+        &DispParams_prop, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+
+    return TRUE;
+} //end of InvokeAllFldsAndProps
+
+BOOL static InvokeAllInSHIntf(IDispatch* ptoIntf, long shVal, long shfld1Val, long shfld2Val)
+{
+    HRESULT hr;
+    DISPID dispid_DangerousGetHandle, dispid_IsClosed, dispid_IsInvalid, dispid_Close, dispid_Dispose, dispid_SetHandleAsInvalid;
+    OLECHAR FAR* szMember;
+    VARIANTARG varargs;
+    DISPPARAMS DispParams = {&varargs, NULL, 0, 0};
+    VARIANT  VarResult;
+
+    ///////////////////////////////////////////////////////////////////
+    printf("\t\t\tCalling GetIDsOfNames() for DangerousGetHandle...\n");
+    szMember = (LPOLESTR)W("DangerousGetHandle");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_DangerousGetHandle);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    printf("\t\t\tCalling GetIDsOfNames() for IsClosed...\n");
+    szMember = (LPOLESTR)W("IsClosed");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_IsClosed);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    printf("\t\t\tCalling GetIDsOfNames() for IsInvalid...\n");
+    szMember = (LPOLESTR)W("IsInvalid");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_IsInvalid);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    printf("\t\t\tCalling GetIDsOfNames() for Close...\n");
+    szMember = (LPOLESTR)W("Close");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_Close);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    printf("\t\t\tCalling GetIDsOfNames() for Dispose...\n");
+    szMember = (LPOLESTR)W("Dispose");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_Dispose);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    printf("\t\t\tCalling GetIDsOfNames() for SetHandleAsInvalid...\n");
+    szMember = (LPOLESTR)W("SetHandleAsInvalid");
+    hr = ptoIntf->GetIDsOfNames(IID_NULL, 
+        &szMember, 
+        1, 
+        LOCALE_SYSTEM_DEFAULT, 
+        &dispid_SetHandleAsInvalid);
+    if( !CheckGetIDsOfNamesHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+
+    printf("\n");
+    //////////////////////////////////////////////////////////////////
+    printf("\t\t\tInvoking IsInvalid...\n");
+    hr = ptoIntf->Invoke(dispid_IsInvalid, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_PROPERTYGET, 
+        &DispParams, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+    if( VarResult.vt != VT_BOOL || VarResult.boolVal != 0 ) 
+    {
+        printf("\t\t\t\tThe return Variant is incorrect!\n");
+        return FALSE;
+    }
+
+    //////////////////////////////////////////////////////////////////
+    printf("\t\t\tInvoking IsClosed...\n");
+    hr = ptoIntf->Invoke(dispid_IsClosed, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_PROPERTYGET, 
+        &DispParams, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+    if( VarResult.vt != VT_BOOL || VarResult.boolVal != 0 ) 
+    {
+        printf("\t\t\t\tThe return Variant is incorrect!\n");
+        return FALSE;
+    }
+
+    //////////////////////////////////////////////////////////////////
+    printf("\t\t\tInvoking DangerousGetHandle...\n");
+    hr = ptoIntf->Invoke(dispid_DangerousGetHandle, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_METHOD, 
+        &DispParams, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+    if( VarResult.vt != VT_INT || VarResult.intVal != shVal ) 
+    {
+        printf("\t\t\t\tThe return Variant is incorrect!\n");
+        return FALSE;
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    //invoke close repetitively to make sure nothing out of the ordinary happens
+    for(int i = 0; i < 3; i++) 
+    {
+        printf("\t\t\tInvoking Close...\n");
+        hr = ptoIntf->Invoke(dispid_Close, 
+            IID_NULL, 
+            LOCALE_SYSTEM_DEFAULT, 
+            DISPATCH_METHOD, 
+            &DispParams, 
+            &VarResult, 
+            NULL, 
+            NULL);
+        if( !CheckInvokeHR(hr) )
+            return FALSE;
+        else
+            printf("\t\t\tCall completed successfully.\n");
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    printf("\t\t\tInvoking Dispose...\n");
+    hr = ptoIntf->Invoke(dispid_Dispose, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_METHOD, 
+        &DispParams, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    /////////////////////////////////////////////////////////////////////
+    /* NOTE: This method should really be named SetHandleAsClosed since it
+    only closes the handle and does not set it to its invalid values; this 
+    decision was taken because the runtime would have had to waste storage 
+    on remembering what the invalid values for this handle were */
+    printf("\t\t\tInvoking SetHandleAsInvalid...\n");
+    hr = ptoIntf->Invoke(dispid_SetHandleAsInvalid, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_METHOD, 
+        &DispParams, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+
+    ////////////////////////////////////////////////////////////////////
+    /* 01/16/04 comment: This check is not needed (see note in previous
+    call); removing because I am also removing the IsClosed check in the 
+    IsInvalid property of the handle; since we no longer check IsClosed, 
+    calling IsInvalid after SetHandleAsInvalid returns false since 
+    SetHandleAsInvalid only closes the handle 
+
+    printf("\t\t\tInvoking IsInvalid...\n");
+    hr = ptoIntf->Invoke(dispid_IsInvalid, 
+    IID_NULL, 
+    LOCALE_SYSTEM_DEFAULT, 
+    DISPATCH_PROPERTYGET, 
+    &DispParams, 
+    &VarResult, 
+    NULL, 
+    NULL);
+    if( !CheckInvokeHR(hr) )
+    return FALSE;
+    else
+    printf("\t\t\tCall completed successfully.\n");
+    if( VarResult.vt != VT_BOOL || VarResult.boolVal != -1 ) //should be invalid since we closed the handle 
+    {
+    printf("\t\t\t\tThe return Variant is incorrect!\n");
+    return FALSE;
+    }
+    */
+    //////////////////////////////////////////////////////////////////
+    printf("\t\t\tInvoking IsClosed...\n");
+    hr = ptoIntf->Invoke(dispid_IsClosed, 
+        IID_NULL, 
+        LOCALE_SYSTEM_DEFAULT, 
+        DISPATCH_PROPERTYGET, 
+        &DispParams, 
+        &VarResult, 
+        NULL, 
+        NULL);
+    if( !CheckInvokeHR(hr) )
+        return FALSE;
+    else
+        printf("\t\t\tCall completed successfully.\n");
+    if( VarResult.vt != VT_BOOL || VarResult.boolVal != -1 ) //should be invalid since we closed the handle
+    {
+        printf("\t\t\t\tThe return Variant is incorrect!\n");
+        return FALSE;
+    }
+
+    printf("\n");
+    /////////////////////////////////////////////////////////////
+    ///Invoke SH fields and properties that are defined in SFH class
+    ///////////////////////////////////////////////////////////////////
+    if( !InvokeAllFldsAndProps(ptoIntf, shfld1Val, shfld2Val) )
+        return FALSE;
+
+
+    return TRUE;
+} //end of InvokeAllInSHIntf
+
+
+/////////////////////////////////Exported Methods
+/////////////////////////////////
+
+extern "C" DLL_EXPORT BOOL __stdcall SH_MAIntf(IDispatch* ptoIntf, long shIntfVal, long shfld1Val, long shfld2Val)
+{
+    printf("\t\tIN SH_MAIntf!\n");
+
+    return InvokeAllInSHIntf(ptoIntf, shIntfVal, shfld1Val, shfld2Val);
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SH_MAIntf_Ref(IDispatch** ptoIntf, long shIntfVal, long shfld1Val, long shfld2Val)
+{
+    printf("\t\tIN SH_MAIntf_Ref!\n");
+
+    return InvokeAllInSHIntf(*ptoIntf, shIntfVal, shfld1Val, shfld2Val);
+}
+
+
+extern "C" DLL_EXPORT BOOL __stdcall SHFld_MAIntf(StructMAIntf s, long shndVal, long shfld1Val, long shfld2Val)
+{
+    printf("\t\tIN SHFld_MAIntf!\n");
+
+    return InvokeAllInSHIntf(s.ptoIntf, shndVal, shfld1Val, shfld2Val);
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHObjectParam(VARIANT v, int shVal, int shfld1Val, int shfld2Val, LPSTR objtype)
+{
+    if( strcmp(objtype, "DispatchWrapper") == 0 )
+    {
+        if( v.vt != VT_DISPATCH )
+        {
+            printf("\tSHObjectParam: v.vt != VT_DISPATCH\n");
+            return FALSE;
+        }
+        else //use the IDispatch pointer to invoke some methods
+            return InvokeAllInSHIntf(v.pdispVal, shVal, shfld1Val, shfld2Val);
+    }
+    else if( strcmp(objtype, "UnknownWrapper") == 0 )
+    {
+        if( v.vt != VT_UNKNOWN )
+        {
+            printf("\tSHObjectParam: v.vt != VT_UNKNOWN\n");
+            return FALSE;
+        }
+        else //use the IDispatch pointer to invoke some methods
+            return InvokeAllInSHIntf(v.pdispVal, shVal, shfld1Val, shfld2Val);
+    }
+    else if( strcmp(objtype, "SafeFileHandle") == 0 )
+    {
+        if( v.vt != VT_DISPATCH ) //if the Object implements IDISPATCH, then it is marshaled to VT_DISPATCH
+        {
+            printf("\tSHObjectParam: v.vt != VT_DISPATCH\n");
+            return FALSE;
+        }
+        else //use the IUnknown pointer to QI for IDispatch
+        {
+            HRESULT hr;
+            IDispatch* pIDisp;
+
+            //QI for IDispatch pointer to SH
+            printf("\tSHObjectParam: QI for IDispatch using IUnknown pointer (v.punkVal)\n");
+            hr = v.punkVal->QueryInterface(IID_IDispatch, (void**)&pIDisp);
+            if( FAILED(hr) )
+            {
+                printf("\tSHObjectParam: hr = E_NOINTERFACE\n");
+                return FALSE;
+            }
+            printf("\tSHObjectParam: QI Done\n");
+
+            //use IDispatch pointer obtained from QI
+            return InvokeAllInSHIntf(pIDisp, shVal, shfld1Val, shfld2Val);
+        }
+    }
+    else
+    {
+        printf("\tSHObjectParam: ERROR! String param is not as expected!\n");
+        printf("\tSHObjectParam: objtype = %s\n",objtype);
+        return FALSE;
+    }
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHStructWithObjectFldParam(StructWithVARIANTFld s, int shVal, int shfld1Val, int shfld2Val, LPSTR objtype)
+{
+    if( strcmp(objtype, "DispatchWrapper") == 0 )
+    {
+        if( s.v.vt != VT_DISPATCH )
+        {
+            printf("\tSHStructWithObjectFldParam: s.v.vt != VT_DISPATCH\n");
+            return FALSE;
+        }
+        else //use the IDispatch pointer to invoke some methods
+            return InvokeAllInSHIntf(s.v.pdispVal, shVal, shfld1Val, shfld2Val);
+    }
+    else if( strcmp(objtype, "UnknownWrapper") == 0 )
+    {
+        if( s.v.vt != VT_UNKNOWN )
+        {
+            printf("\tSHStructWithObjectFldParam: s.v.vt != VT_UNKNOWN\n");
+            return FALSE;
+        }
+        else //use the IUnknown pointer to QI for IDispatch
+        {
+            HRESULT hr;
+            IDispatch* pIDisp;
+
+            //QI for IDispatch pointer to SH
+            printf("\tSHStructWithObjectFldParam: QI for IDispatch using IUnknown pointer (s.v.punkVal)\n");
+            hr = s.v.punkVal->QueryInterface(IID_IDispatch, (void**)&pIDisp);
+            if( FAILED(hr) )
+            {
+                printf("\tSHStructWithObjectFldParam: hr = E_NOINTERFACE\n");
+                return FALSE;
+            }
+            printf("\tSHStructWithObjectFldParam: QI Done\n");
+
+            //use IDispatch pointer obtained from QI
+            return InvokeAllInSHIntf(pIDisp, shVal, shfld1Val, shfld2Val);
+        }
+    }
+    else if( strcmp(objtype, "SafeFileHandle") == 0 )
+    {
+        if( s.v.vt != VT_DISPATCH )
+        {
+            printf("\tSHStructWithObjectFldParam: s.v.vt != VT_DISPATCH\n");
+            return FALSE;
+        }
+        else //use the IDispatch pointer to invoke some methods
+            return InvokeAllInSHIntf(s.v.pdispVal, shVal, shfld1Val, shfld2Val);
+    }
+    else
+    {
+        printf("\tSHStructWithObjectFldParam: ERROR! String param is not as expected!\n");
+        printf("\tSHStructWithObjectFldParam: objtype = %s\n",objtype);
+        return FALSE;
+    }
+}
diff --git a/tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceTest.cs b/tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceTest.cs
new file mode 100644 (file)
index 0000000..3dac428
--- /dev/null
@@ -0,0 +1,96 @@
+// 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 SafeHandlesTests;
+using System.Threading;
+using TestLibrary;
+
+[StructLayout(LayoutKind.Sequential)]
+public struct StructMAIntf
+{
+    [MarshalAs(UnmanagedType.Interface)]
+    public SafeFileHandle hnd;
+}
+
+public class SHtoIntfTester
+{
+    public static int Main()
+    {
+        try
+        {
+            RunTests();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+
+    [DllImport("PInvoke_SafeHandle_MarshalAs_Interface")]
+    public static extern bool SH_MAIntf([MarshalAs(UnmanagedType.Interface)]SafeFileHandle sh, Int32 shVal, Int32 shfld1Val, Int32 shfld2Val);
+
+    [DllImport("PInvoke_SafeHandle_MarshalAs_Interface")]
+    public static extern bool SH_MAIntf_Ref([MarshalAs(UnmanagedType.Interface)]ref SafeFileHandle sh, Int32 shVal, Int32 shfld1Val, Int32 shfld2Val);
+
+    [DllImport("PInvoke_SafeHandle_MarshalAs_Interface")]
+    public static extern bool SHFld_MAIntf(StructMAIntf s, Int32 shndVal, Int32 shfld1Val, Int32 shfld2Val);
+
+    public static void RunTests()
+    {
+        Console.WriteLine("RunTests started");
+
+        ////////////////////////////////////////////////////////
+        SafeFileHandle sh = Helper.NewSFH();
+        Int32 shVal = Helper.SHInt32(sh);
+        sh.shfld1 = Helper.NewSFH(); //SH field of SFH class
+        Int32 shfld1Val = Helper.SHInt32(sh.shfld1);
+        sh.shfld2 = Helper.NewSFH(); //SFH field of SFH class
+        Int32 shfld2Val = Helper.SHInt32(sh.shfld2);
+
+        //NOTE: SafeHandle is now ComVisible(false)...QIs for IDispatch or the class interface on a 
+        //    type with a ComVisible(false) type in its hierarchy are no longer allowed; so calling 
+        //    the DW ctor with a SH subclass causes an invalidoperationexception to be thrown since
+        //    the ctor QIs for IDispatch
+        Console.WriteLine("Testing SH_MAIntf...");
+        Assert.Throws<InvalidOperationException>(() => SH_MAIntf(sh, shVal, shfld1Val, shfld2Val), "Did not throw InvalidOperationException!");
+
+        ////////////////////////////////////////////////////////
+        sh = Helper.NewSFH();
+        shVal = Helper.SHInt32(sh);
+        sh.shfld1 = Helper.NewSFH(); //SH field of SFH class
+        shfld1Val = Helper.SHInt32(sh.shfld1);
+        sh.shfld2 = Helper.NewSFH(); //SFH field of SFH class
+        shfld2Val = Helper.SHInt32(sh.shfld2);
+
+        //NOTE: SafeHandle is now ComVisible(false)...QIs for IDispatch or the class interface on a 
+        //    type with a ComVisible(false) type in its hierarchy are no longer allowed; so calling 
+        //    the DW ctor with a SH subclass causes an invalidoperationexception to be thrown since
+        //    the ctor QIs for IDispatch
+        Console.WriteLine("Testing SH_MAIntf_Ref...");
+        Assert.Throws<InvalidOperationException>(() => SH_MAIntf_Ref(ref sh, shVal, shfld1Val, shfld2Val), "Did not throw InvalidOperationException!");
+
+        ////////////////////////////////////////////////////////
+        StructMAIntf s = new StructMAIntf();
+        s.hnd = Helper.NewSFH();
+        Int32 shndVal = Helper.SHInt32(s.hnd);
+        s.hnd.shfld1 = Helper.NewSFH(); //SH field of SFH field of struct
+        shfld1Val = Helper.SHInt32(s.hnd.shfld1);
+        s.hnd.shfld2 = Helper.NewSFH(); //SFH field of SFH field of struct
+        shfld2Val = Helper.SHInt32(s.hnd.shfld2);
+
+        //NOTE: SafeHandle is now ComVisible(false)...QIs for IDispatch or the class interface on a 
+        //    type with a ComVisible(false) type in its hierarchy are no longer allowed; so calling 
+        //    the DW ctor with a SH subclass causes an invalidoperationexception to be thrown since
+        //    the ctor QIs for IDispatch
+        Console.WriteLine("Testing SHFld_MAIntf...");
+        Assert.Throws<InvalidOperationException>(() => SHFld_MAIntf(s, shndVal, shfld1Val, shfld2Val), "Did not throw InvalidOperationException!");
+    
+        Console.WriteLine("RunTests end");
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/Interface/InterfaceTest.csproj
new file mode 100644 (file)
index 0000000..15130a4
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>InterfaceTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="InterfaceTest.cs" />
+    <Compile Include="..\*.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/SafeHandles/InvalidMarshalAs/InvalidMarshalAsTest.cs b/tests/src/Interop/PInvoke/SafeHandles/InvalidMarshalAs/InvalidMarshalAsTest.cs
new file mode 100644 (file)
index 0000000..eadb7aa
--- /dev/null
@@ -0,0 +1,801 @@
+// 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 SafeHandlesTests;
+using TestLibrary;
+
+#pragma warning disable 618
+public class SHTester_MA
+{
+    public static int Main()
+    {
+        try
+        {
+            RunSHInvalidMATests();
+            RunSHInvalidretMATests();
+            RunSHFldInvalidMATests();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+
+    }
+
+    /// <summary>
+    /// All the invalid MarshalAs signatures follow
+    /// </summary>
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA1([MarshalAs(UnmanagedType.AnsiBStr)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA2([MarshalAs(UnmanagedType.AsAny)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA3([MarshalAs(UnmanagedType.Bool)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA4([MarshalAs(UnmanagedType.BStr)]SafeFileHandle sh);
+
+    // NOTE: Specified unmanaged type is only valid on fields.
+    //[DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError=true)]
+    //public static extern bool SHInvalid_MA5([MarshalAs(UnmanagedType.ByValArray)]SafeFileHandle sh);
+
+    //NOTE: Specified unmanaged type is only valid on fields.
+    //[DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError=true)]
+    //public static extern bool SHInvalid_MA6([MarshalAs(UnmanagedType.ByValTStr, SizeConst=10)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA7([MarshalAs(UnmanagedType.Currency)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA9([MarshalAs(UnmanagedType.Error)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA10([MarshalAs(UnmanagedType.FunctionPtr)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA11([MarshalAs(UnmanagedType.I1)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA12([MarshalAs(UnmanagedType.I2)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA13([MarshalAs(UnmanagedType.I4)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA14([MarshalAs(UnmanagedType.I8)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA15([MarshalAs(UnmanagedType.IDispatch)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA16([MarshalAs(UnmanagedType.Interface)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA17([MarshalAs(UnmanagedType.IUnknown)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA18([MarshalAs(UnmanagedType.LPArray)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA19([MarshalAs(UnmanagedType.LPStr)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA20([MarshalAs(UnmanagedType.LPStruct)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA21([MarshalAs(UnmanagedType.LPTStr)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA22([MarshalAs(UnmanagedType.LPWStr)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA23([MarshalAs(UnmanagedType.R4)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA24([MarshalAs(UnmanagedType.R8)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA25([MarshalAs(UnmanagedType.SafeArray)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA26([MarshalAs(UnmanagedType.Struct)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA27([MarshalAs(UnmanagedType.SysInt)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA28([MarshalAs(UnmanagedType.SysUInt)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA29([MarshalAs(UnmanagedType.TBStr)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA30([MarshalAs(UnmanagedType.U1)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA31([MarshalAs(UnmanagedType.U2)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA32([MarshalAs(UnmanagedType.U4)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA33([MarshalAs(UnmanagedType.U8)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA34([MarshalAs(UnmanagedType.VariantBool)]SafeFileHandle sh);
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_MA", SetLastError = true)]
+    public static extern bool SHInvalid_MA35([MarshalAs(UnmanagedType.VBByRefStr)]SafeFileHandle sh);
+
+    /// <summary>
+    ///runs all tests involving pinvoke signatures with invalid MarshalAs attributes
+    /// </summary>
+    public static void RunSHInvalidMATests()
+    {
+        Console.WriteLine("\nRunSHInvalidMATests():");
+
+        SafeFileHandle hnd = Helper.NewSFH();
+
+        Console.WriteLine("Testing SHInvalid_MA1...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA1(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA2...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA2(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA3...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA3(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA4...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA4(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("SHInvalid_MA5 cannot be tested (see PInvoke signatures in test source for comments)...");
+        Console.WriteLine("SHInvalid_MA6 cannot be tested (see PInvoke signatures in test source for comments)...");
+
+        Console.WriteLine("Testing SHInvalid_MA7...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA7(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("SHInvalid_MA8 cannot be tested (see PInvoke signatures in test source for comments)...");
+
+        Console.WriteLine("Testing SHInvalid_MA9...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA9(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA10...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA10(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA11...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA11(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA12...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA12(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA13...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA13(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA14...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA14(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA15...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA15(hnd), "FAILED!  Exception not thrown.");
+
+        //NOTE: UnmanagedType.Interface is the only MA attribute that is valid
+        //Console.WriteLine("Testing SHInvalid_MA16...");
+
+        Console.WriteLine("Testing SHInvalid_MA17...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA17(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA18...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA18(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA19...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA19(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA20...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA20(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA21...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA21(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA22...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA22(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA23...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA23(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA24...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA24(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA25...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA25(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA26...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA26(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA27...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA27(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA28...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA28(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA29...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA29(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA30...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA30(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA31...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA31(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA32...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA32(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA33...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA33(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA34...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA34(hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHInvalid_MA35...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_MA35(hnd), "FAILED!  Exception not thrown.");
+    }
+
+    /// <summary>
+    /// All the invalid return MarshalAs signatures follow
+    /// </summary>
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.AnsiBStr)]
+    public static extern SafeFileHandle SHInvalid_retMA1();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.AsAny)]
+    public static extern SafeFileHandle SHInvalid_retMA2();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.Bool)]
+    public static extern SafeFileHandle SHInvalid_retMA3();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.BStr)]
+    public static extern SafeFileHandle SHInvalid_retMA4();
+
+    //NOTE: Specified unmanaged type is only valid on fields.
+    //[DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError=true)]
+    //[return:MarshalAs(UnmanagedType.ByValArray)]
+    //public static extern SafeFileHandle SHInvalid_retMA5();
+
+    //NOTE: Specified unmanaged type is only valid on fields.
+    //[DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError=true)]
+    //[return:MarshalAs(UnmanagedType.ByValTStr)]
+    //public static extern SafeFileHandle SHInvalid_retMA6();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.Currency)]
+    public static extern SafeFileHandle SHInvalid_retMA7();
+
+    //NOTE: Specified unmanaged type also needs MarshalType or MarshalTypeRef which indicate the custom marshaler
+    //[DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError=true)]
+    //[return:MarshalAs(UnmanagedType.CustomMarshaler)]
+    //public static extern SafeFileHandle SHInvalid_retMA8();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.Error)]
+    public static extern SafeFileHandle SHInvalid_retMA9();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.FunctionPtr)]
+    public static extern SafeFileHandle SHInvalid_retMA10();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.I1)]
+    public static extern SafeFileHandle SHInvalid_retMA11();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.I2)]
+    public static extern SafeFileHandle SHInvalid_retMA12();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.I4)]
+    public static extern SafeFileHandle SHInvalid_retMA13();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.I8)]
+    public static extern SafeFileHandle SHInvalid_retMA14();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.IDispatch)]
+    public static extern SafeFileHandle SHInvalid_retMA15();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.Interface)]
+    public static extern SafeFileHandle SHInvalid_retMA16();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.IUnknown)]
+    public static extern SafeFileHandle SHInvalid_retMA17();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.LPArray)]
+    public static extern SafeFileHandle SHInvalid_retMA18();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.LPStr)]
+    public static extern SafeFileHandle SHInvalid_retMA19();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.LPStruct)]
+    public static extern SafeFileHandle SHInvalid_retMA20();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.LPTStr)]
+    public static extern SafeFileHandle SHInvalid_retMA21();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.LPWStr)]
+    public static extern SafeFileHandle SHInvalid_retMA22();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.R4)]
+    public static extern SafeFileHandle SHInvalid_retMA23();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.R8)]
+    public static extern SafeFileHandle SHInvalid_retMA24();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.SafeArray)]
+    public static extern SafeFileHandle SHInvalid_retMA25();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.Struct)]
+    public static extern SafeFileHandle SHInvalid_retMA26();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.SysInt)]
+    public static extern SafeFileHandle SHInvalid_retMA27();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.SysUInt)]
+    public static extern SafeFileHandle SHInvalid_retMA28();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.TBStr)]
+    public static extern SafeFileHandle SHInvalid_retMA29();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.U1)]
+    public static extern SafeFileHandle SHInvalid_retMA30();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.U2)]
+    public static extern SafeFileHandle SHInvalid_retMA31();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.U4)]
+    public static extern SafeFileHandle SHInvalid_retMA32();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.U8)]
+    public static extern SafeFileHandle SHInvalid_retMA33();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.VariantBool)]
+    public static extern SafeFileHandle SHInvalid_retMA34();
+
+    [DllImport("PInvoke_SafeHandle", EntryPoint="SHInvalid_retMA", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.VBByRefStr)]
+    public static extern SafeFileHandle SHInvalid_retMA35();
+
+    /// <summary>
+    ///runs all tests involving pinvoke signatures with invalid return MarshalAs attributes
+    /// </summary>
+    public static void RunSHInvalidretMATests()
+    {
+        Console.WriteLine("\nRunSHInvalidretMATests():");
+
+        Console.WriteLine("Testing SHInvalid_retMA1...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA1(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA2...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA2(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA3...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA3(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA4...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA4(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("SHInvalid_retMA5 cannot be tested (see PInvoke signatures in test source for comments)...");
+        Console.WriteLine("SHInvalid_retMA6 cannot be tested (see PInvoke signatures in test source for comments)...");
+
+        Console.WriteLine("Testing SHInvalid_retMA7...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA7(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("SHInvalid_retMA8 cannot be tested (see PInvoke signatures in test source for comments)...");
+
+        Console.WriteLine("Testing SHInvalid_retMA9...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA9(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA10...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA10(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA11...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA11(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA12...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA12(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA13...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA13(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA14...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA14(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA15...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA15(), "FAILED!  Exception not thrown.");
+
+        //NOTE: If the return type is marked as Unman.Intf, then the unmanaged code should return an Interface
+        //             pointer and not an integer---doing so will cause unexpected behavior
+        /*     
+        Console.WriteLine("Testing SHInvalid_retMA16...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA16(), "FAILED!  Exception not thrown.");
+        */
+
+        Console.WriteLine("Testing SHInvalid_retMA17...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA17(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA18...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA18(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA19...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA19(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA20...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA20(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA21...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA21(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA22...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA22(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA23...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA23(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA24...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA24(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA25...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA25(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA26...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA26(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA27...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA27(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA28...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA28(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA29...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA29(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA30...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA30(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA31...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA31(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA32...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA32(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA33...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA33(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA34...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA34(), "FAILED!  Exception not thrown.");
+        
+        Console.WriteLine("Testing SHInvalid_retMA35...");
+        Assert.Throws<MarshalDirectiveException>(() => SHInvalid_retMA35(), "FAILED!  Exception not thrown.");
+    }
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA1 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA2 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA3 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA4 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA5 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA6 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA7 s);
+
+    //NOTE: no corresponding struct definition
+    //[DllImport("PInvoke_SafeHandle", SetLastError=true)]
+    //public static extern bool SHFldInvalid_MA(StructMA8 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA9 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA10 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA11 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA12 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA13 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA14 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA15 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA16 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA17 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA18 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA19 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA20 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA21 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA22 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA23 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA24 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA25 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA26 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA27 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA28 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA29 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA30 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA31 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA32 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA33 s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHFldInvalid_MA(StructMA34 s);
+
+    /// <summary>
+    ///runs all tests involving pinvoke signatures passing/returning structures 
+    ///containing SH fields with invalid MarshalAs attributes
+    /// </summary>
+    public static void RunSHFldInvalidMATests()
+    {
+        Console.WriteLine("\nRunSHFldInvalidMATests():");
+
+        StructMA1 s1 = new StructMA1();
+        s1.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA1...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s1), "FAILED!  Exception not thrown.");
+
+        StructMA2 s2 = new StructMA2();
+        s2.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA2...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s2), "FAILED!  Exception not thrown.");
+
+        StructMA3 s3 = new StructMA3();
+        s3.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA3...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s3), "FAILED!  Exception not thrown.");
+
+        StructMA4 s4 = new StructMA4();
+        s4.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA4...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s4), "FAILED!  Exception not thrown.");
+
+        StructMA5 s5 = new StructMA5();
+        s5.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA5...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s5), "FAILED!  Exception not thrown.");
+
+        StructMA6 s6 = new StructMA6();
+        s6.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA6...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s6), "FAILED!  Exception not thrown.");
+        
+        StructMA7 s7 = new StructMA7();
+        s7.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA7...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s7), "FAILED!  Exception not thrown.");
+        
+        /* 
+        StructMA8 s8 = new StructMA8();
+        s5.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA8...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s8), "FAILED!  Exception not thrown.");
+        */
+
+        StructMA9 s9 = new StructMA9();
+        s9.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA9...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s9), "FAILED!  Exception not thrown.");
+        
+        StructMA10 s10 = new StructMA10();
+        s10.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA10...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s10), "FAILED!  Exception not thrown.");
+        
+        StructMA11 s11 = new StructMA11();
+        s11.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA11...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s11), "FAILED!  Exception not thrown.");
+        
+        StructMA12 s12 = new StructMA12();
+        s12.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA12...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s12), "FAILED!  Exception not thrown.");
+        
+        StructMA13 s13 = new StructMA13();
+        s13.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA13...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s13), "FAILED!  Exception not thrown.");
+        
+        StructMA14 s14 = new StructMA14();
+        s14.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA14...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s14), "FAILED!  Exception not thrown.");
+        
+        StructMA15 s15 = new StructMA15();
+        s15.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA15...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s15), "FAILED!  Exception not thrown.");
+        
+        //NOTE: UnmanagedType.Interface is the only MA attribute allowed
+        /*
+        StructMA16 s16 = new StructMA16();
+        s16.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA16...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s16), "FAILED!  Exception not thrown.");
+        */
+
+        StructMA17 s17 = new StructMA17();
+        s17.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA17...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s17), "FAILED!  Exception not thrown.");
+        
+        StructMA18 s18 = new StructMA18();
+        s18.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA18...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s18), "FAILED!  Exception not thrown.");
+        
+        StructMA19 s19 = new StructMA19();
+        s19.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA19...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s19), "FAILED!  Exception not thrown.");
+        
+        StructMA20 s20 = new StructMA20();
+        s20.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA20...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s20), "FAILED!  Exception not thrown.");
+        
+        StructMA21 s21 = new StructMA21();
+        s21.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA21...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s21), "FAILED!  Exception not thrown.");
+        
+        StructMA22 s22 = new StructMA22();
+        s22.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA22...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s22), "FAILED!  Exception not thrown.");
+        
+        StructMA23 s23 = new StructMA23();
+        s23.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA23...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s23), "FAILED!  Exception not thrown.");
+        
+        StructMA24 s24 = new StructMA24();
+        s24.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA24...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s24), "FAILED!  Exception not thrown.");
+        
+        StructMA25 s25 = new StructMA25();
+        s25.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA25...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s25), "FAILED!  Exception not thrown.");
+        
+        StructMA26 s26 = new StructMA26();
+        s26.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA26...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s26), "FAILED!  Exception not thrown.");
+        
+        StructMA27 s27 = new StructMA27();
+        s27.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA27...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s27), "FAILED!  Exception not thrown.");
+        
+        StructMA28 s28 = new StructMA28();
+        s28.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA28...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s28), "FAILED!  Exception not thrown.");
+        
+        StructMA29 s29 = new StructMA29();
+        s29.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA29...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s29), "FAILED!  Exception not thrown.");
+        
+        StructMA30 s30 = new StructMA30();
+        s30.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA30...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s30), "FAILED!  Exception not thrown.");
+        
+        StructMA31 s31 = new StructMA31();
+        s31.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA31...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s31), "FAILED!  Exception not thrown.");
+        
+        StructMA32 s32 = new StructMA32();
+        s32.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA32...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s32), "FAILED!  Exception not thrown.");
+        
+        StructMA33 s33 = new StructMA33();
+        s33.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA33...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s33), "FAILED!  Exception not thrown.");
+        
+        StructMA34 s34 = new StructMA34();
+        s34.hnd = Helper.NewSFH();
+        Console.WriteLine("Testing StructMA34...");
+        Assert.Throws<TypeLoadException>(() => SHFldInvalid_MA(s34), "FAILED!  Exception not thrown.");
+    }
+}
+#pragma warning restore 618
+
+
+
+
+
+
diff --git a/tests/src/Interop/PInvoke/SafeHandles/InvalidMarshalAs/InvalidMarshalAsTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/InvalidMarshalAs/InvalidMarshalAsTest.csproj
new file mode 100644 (file)
index 0000000..811ba20
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>InvalidMarshalAsTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="InvalidMarshalAsTest.cs" />
+    <Compile Include="..\*.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/SafeHandles/Misc.Unsupported/MiscUnsupportedTest.cs b/tests/src/Interop/PInvoke/SafeHandles/Misc.Unsupported/MiscUnsupportedTest.cs
new file mode 100644 (file)
index 0000000..d5e8455
--- /dev/null
@@ -0,0 +1,114 @@
+// 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 SafeHandlesTests;
+using TestLibrary;
+
+public class SHTester_Misc
+{
+    public static int Main()
+    {
+        try
+        {
+            RunSHMiscTests();
+            RunChildSHStructParamTests();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHArrayParam(SafeFileHandle[] arr, Int32[] arrInt32s, int length);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern StructWithSHFld SHReturnStruct();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_In2([In]StructWithSHArrayFld s, Int32[] arrInt32s, int length);
+
+    /// <summary>
+    ///runs all other miscellaneous tests;
+    ///it uses the PInvoke signatures defined above it 
+    ///1-passing arrays of SHs as parameters
+    ///2-passing arrays of structures (with SH subclass fields) as parameters
+    ///3-returning SHs from unmanaged code as pure return values
+    ///4-returning structures (with SH subclass fields) from unmanaged code as pure return values
+    ///5-passing nested structures (with the nested structure having a SH subclass field)
+    ///6-passing structures with SH Array fields
+    ///7-passing mixed params (SH, SH subclass, subclass of SH subclass)
+    ///8-passing struct params that have many handle fields [in, ref (with and without changes to flds)]
+    ///9-passing SH subclass in Dispatch\UnknownWrapper, expecting a VARIANT (of type VT_DISPATCH or
+    ///VT_UNKNOWN) on the managed side; as params and as fields
+    /// </summary>
+    public static void RunSHMiscTests()
+    {
+        Console.WriteLine("\nRunSHMiscTests():");
+
+        ///1-passing arrays of SHs as parameters
+
+        SafeFileHandle[] hndArray = new SafeFileHandle[Helper.N];
+        //the following array will contain the 32-bit values corresponding to hndArray's elements
+        Int32[] hndArrayInt32s = new Int32[Helper.N];
+        for (int i = 0; i < Helper.N; i++)
+        {
+            hndArray[i] = Helper.NewSFH();
+            hndArrayInt32s[i] = Helper.SHInt32(hndArray[i]);
+        }
+        Console.WriteLine("Testing SHArrayParam...");
+        Assert.Throws<MarshalDirectiveException>(() => SHArrayParam(hndArray, hndArrayInt32s, Helper.N), "FAILED!  Exception not thrown.");
+
+        //4-returning structures (with SH subclass fields) from unmanaged code as pure return values
+        Console.WriteLine("Testing SHReturnStruct...");
+        Assert.Throws<MarshalDirectiveException>(() => SHReturnStruct(), "FAILED!  Exception not thrown.");
+
+        //6-passing structures with SH Array fields
+        hndArray = new SafeFileHandle[Helper.N];
+        //the following array will contain the 32-bit values corresponding to hndArray's elements
+        hndArrayInt32s = new Int32[Helper.N];
+        for (int i = 0; i < Helper.N; i++)
+        {
+            hndArray[i] = Helper.NewSFH();
+            hndArrayInt32s[i] = Helper.SHInt32(hndArray[i]);
+        }
+        StructWithSHArrayFld sWithArrFld = new StructWithSHArrayFld();
+        sWithArrFld.sharr = hndArray; //assign hnd array to hnd array field
+        Console.WriteLine("Testing SHStructParam_In2...");
+        Assert.Throws<TypeLoadException>(() => SHStructParam_In2(sWithArrFld, hndArrayInt32s, Helper.N), "FAILED!  Exception not thrown.");
+    }
+
+    /// <summary>
+    ///passing SafeFileHandle subclass parameters to unmanaged code in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it 
+    ///1-passing SafeFileHandle subclass parameters individually in separate methods (In, out, ref)
+    ///2-passing SafeFileHandle subclass parameters in combination in the same method
+    /// </summary>
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern StructWithChildSHFld SHStructParam_OutRetVal();
+
+    /// <summary>
+    ///passing structures (with SafeFileHandle subclass fields) as parameters in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it
+    ///1-passing structures (In, out, ref) (with SafeFileHandle subclass fields) individually in separate methods
+    ///2-passing structures (In, out, ref) (with SafeFileHandle subclass fields) in combination in the same method
+    /// </summary>
+    public static void RunChildSHStructParamTests()
+    {
+        Console.WriteLine("\nRunChildSHStructParamTests():");
+
+        StructWithChildSHFld s = new StructWithChildSHFld();
+        s.hnd = Helper.NewChildSFH(); //get a new SH
+        Int32 hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+
+        Console.WriteLine("Testing SHStructParam_OutRetVal...");
+        Assert.Throws<MarshalDirectiveException>(() => s = SHStructParam_OutRetVal(), "FAILED!  Exception not thrown.");
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SafeHandles/Misc.Unsupported/MiscUnsupportedTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/Misc.Unsupported/MiscUnsupportedTest.csproj
new file mode 100644 (file)
index 0000000..56ecdd8
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>MiscUnsupportedTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="MiscUnsupportedTest.cs" />
+    <Compile Include="..\*.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/SafeHandles/Misc/MiscTest.cs b/tests/src/Interop/PInvoke/SafeHandles/Misc/MiscTest.cs
new file mode 100644 (file)
index 0000000..2d87481
--- /dev/null
@@ -0,0 +1,365 @@
+// 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 SafeHandlesTests;
+using TestLibrary;
+
+#pragma warning disable 618
+public class SHTester_Misc
+{
+    public static int Main()
+    {
+        try
+        {
+            RunSHMiscTests();
+            RunChildSHParamTests();
+            RunChildSHStructParamTests();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructArrayParam(StructWithSHFld[] arr, Int32[] arrInt32s, int length);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern SFH_NoCloseHandle SHReturn();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructNestedParam(StructNestedParent sn, Int32 arrInt32s);
+
+    [DllImport("PInvoke_SafeHandle")]
+    public static extern bool SHMixedParam1(SafeHandle sh1, out SFH_NoCloseHandle sh2, ref ChildSFH_NoCloseHandle sh3, StructWithBaseSHFld s1,
+        StructWithSHFld s2, ref StructWithChildSHFld s3, Int32 sh1Value, Int32 sh3Value, Int32 s1fldValue, Int32 s2fldValue, Int32 s3fldValue);
+
+    [DllImport("PInvoke_SafeHandle")]
+    public static extern bool SHStructWithManySHFldsParam_In(StructWithManySHFlds s, Int32[] arrInt32s);
+
+    [DllImport("PInvoke_SafeHandle")]
+    public static extern bool SHStructWithManySHFldsParam_Ref1(ref StructWithManySHFlds s, Int32[] arrInt32s);
+
+    [DllImport("PInvoke_SafeHandle")]
+    public static extern bool SHStructWithManySHFldsParam_Ref2(ref StructWithManySHFlds s, Int32[] arrInt32s);
+
+    [DllImport(@"PInvoke_SafeHandle_MarshalAs_Interface.dll", CharSet = CharSet.Ansi)] //the MA attribute indicates that obj is to be marshaled as a VARIANT
+    public static extern bool SHObjectParam([MarshalAs(UnmanagedType.Struct)]Object obj, Int32 shValue, Int32 shfld1Value, Int32 shfld2Value, String wrapper);
+
+    [DllImport(@"PInvoke_SafeHandle_MarshalAs_Interface.dll", CharSet = CharSet.Ansi)]
+    public static extern bool SHStructWithObjectFldParam(StructWithObjFld s, Int32 shValue, Int32 shfld1Value, Int32 shfld2Value, String objtype);
+
+    /// <summary>
+    ///runs all other miscellaneous tests;
+    ///it uses the PInvoke signatures defined above it 
+    ///1-passing arrays of SHs as parameters
+    ///2-passing arrays of structures (with SH subclass fields) as parameters
+    ///3-returning SHs from unmanaged code as pure return values
+    ///4-returning structures (with SH subclass fields) from unmanaged code as pure return values
+    ///5-passing nested structures (with the nested structure having a SH subclass field)
+    ///6-passing structures with SH Array fields
+    ///7-passing mixed params (SH, SH subclass, subclass of SH subclass)
+    ///8-passing struct params that have many handle fields [in, ref (with and without changes to flds)]
+    ///9-passing SH subclass in Dispatch\UnknownWrapper, expecting a VARIANT (of type VT_DISPATCH or
+    ///VT_UNKNOWN) on the managed side; as params and as fields
+    /// </summary>
+    public static void RunSHMiscTests()
+    {
+        Console.WriteLine("\nRunSHMiscTests():");
+
+        SafeFileHandle[] hndArray = new SafeFileHandle[Helper.N];
+        //the following array will contain the 32-bit values corresponding to hndArray's elements
+        Int32[] hndArrayInt32s = new Int32[Helper.N];
+
+        //2-passing arrays of structures (with SH subclass fields) as parameters
+        StructWithSHFld[] structArray = new StructWithSHFld[Helper.N];
+        //the following array will contain the 32-bit values corresponding to structArray's elements
+        Int32[] structArrayInt32s = new Int32[Helper.N];
+        for (int i = 0; i < Helper.N; i++)
+        {
+            structArray[i] = new StructWithSHFld();
+            structArray[i].hnd = Helper.NewSFH();
+            structArrayInt32s[i] = Helper.SHInt32(structArray[i].hnd);
+        }
+
+        Console.WriteLine("Testing SHStructArrayParam...");
+        Assert.Throws<InvalidOperationException>(() => SHStructArrayParam(structArray, structArrayInt32s, Helper.N), "FAILED! Expected Exception Not Thrown!");
+
+        //3-returning SHs from unmanaged code as pure return values
+        SFH_NoCloseHandle hnd;
+        Console.WriteLine("Testing SHReturn...");
+        hnd = SHReturn();
+        Assert.IsTrue(Helper.IsChanged(hnd), "FAILED! SHReturn did not return hnd as expected.");
+
+        //5-passing nested structures (with the nested structure having a SH subclass field)
+        StructNestedParent sn = new StructNestedParent();
+        sn.snOneDeep = new StructNestedOneDeep();
+        sn.snOneDeep.s = new StructWithSHFld();
+        sn.snOneDeep.s.hnd = Helper.NewSFH();
+        Int32 hndInt32 = Helper.SHInt32(sn.snOneDeep.s.hnd);
+        Console.WriteLine("Testing SHStructNestedParam...");
+        Assert.IsTrue(SHStructNestedParam(sn, hndInt32), "FAILED! SHStructNestedParam did not receive param as expected.");
+        //check that the value of the HANDLE field did not change
+        Assert.IsFalse(Helper.IsChanged(sn.snOneDeep.s.hnd), "FAILED! SHStructNestedParam did not return param as expected.");
+
+        //7-passing mixed params (SH, SH subclass, subclass of SH subclass)
+        SafeHandle sh1 = Helper.NewSFH();
+        SFH_NoCloseHandle sh2;
+        ChildSFH_NoCloseHandle sh3 = Helper.NewChildSFH_NoCloseHandle();
+        StructWithBaseSHFld s1 = new StructWithBaseSHFld(); s1.hnd = Helper.NewSFH();
+        StructWithSHFld s2 = new StructWithSHFld(); s2.hnd = Helper.NewSFH();
+        StructWithChildSHFld s3 = new StructWithChildSHFld(); s3.hnd = Helper.NewChildSFH();
+        Int32 sh1Value = Helper.SHInt32(sh1);
+        Int32 sh3Value = Helper.SHInt32(sh3);
+        Int32 s1fldValue = Helper.SHInt32(s1.hnd);
+        Int32 s2fldValue = Helper.SHInt32(s2.hnd);
+        Int32 s3fldValue = Helper.SHInt32(s3.hnd);
+
+        Console.WriteLine("Testing SHMixedParam1...");
+        Assert.IsTrue(SHMixedParam1(sh1, out sh2, ref sh3, s1, s2, ref s3, sh1Value, sh3Value, s1fldValue, s2fldValue, s3fldValue), "FAILED! SHMixedParam1 did not receive params as expected.");
+        //check the values after the call
+        Assert.IsFalse(Helper.IsChanged(sh1) || !Helper.IsChanged(sh2) || Helper.IsChanged(sh3) || Helper.IsChanged(s1.hnd) ||
+                 Helper.IsChanged(s2.hnd) || Helper.IsChanged(s3.hnd), "FAILED! SHMixedParam1 did not return params as expected.");
+        
+        //8-passing struct params that have many handle fields [in, ref (with and without changes to flds)]
+
+        //initialize a new StructWithManySHFlds
+        Int32[] arrInt32s = null;
+        StructWithManySHFlds s = Helper.NewStructWithManySHFlds(ref arrInt32s);
+
+        Console.WriteLine("Testing SHStructWithManySHFldsParam_In...");
+        Assert.IsTrue(SHStructWithManySHFldsParam_In(s, arrInt32s), "FAILED! SHStructWithManySHFldsaram_In did not receive param as expected.");
+        //check that the value of the HANDLE fields did not change
+        Assert.IsFalse(Helper.IsChangedStructWithManySHFlds(s, arrInt32s), "FAILED! SHStructWithManySHFldsParam_In did not return param as expected.");
+
+        Console.WriteLine("Testing SHStructWithManySHFldsParam_Ref1...");
+        Assert.IsTrue(SHStructWithManySHFldsParam_Ref1(ref s, arrInt32s), "FAILED! SHStructWithManySHFldsaram_Ref1 did not receive param as expected.");
+        //check that the value of the HANDLE fields did not change
+        Assert.IsFalse(Helper.IsChangedStructWithManySHFlds(s, arrInt32s), "FAILED! SHStructWithManySHFldsParam_Ref1 did not return param as expected.");
+
+        Console.WriteLine("Testing SHStructWithManySHFldsParam_Ref2...");
+        Assert.Throws<NotSupportedException>(() => SHStructWithManySHFldsParam_Ref2(ref s, arrInt32s), "FAILED! Expected Exception Not Thrown!");
+
+        //9-passing SH subclass in Dispatch\UnknownWrapper, expecting a VARIANT (of type VT_DISPATCH or
+        //VT_UNKNOWN) on the managed side
+        SafeFileHandle sfh = Helper.NewSFH(); //SafeFileHandle
+        sfh.shfld1 = Helper.NewSFH();
+        sfh.shfld2 = Helper.NewSFH();
+        Int32 shValue = Helper.SHInt32(sfh);
+        Int32 shfld1Value = Helper.SHInt32(sfh.shfld1);
+        Int32 shfld2Value = Helper.SHInt32(sfh.shfld2);
+
+        SafeFileHandle sfh2 = Helper.NewSFH(); //SafeFileHandle
+        sfh2.shfld1 = Helper.NewSFH();
+        sfh2.shfld2 = Helper.NewSFH();
+        Int32 sh2Value = Helper.SHInt32(sfh2);
+        Int32 sh2fld1Value = Helper.SHInt32(sfh2.shfld1);
+        Int32 sh2fld2Value = Helper.SHInt32(sfh2.shfld2);
+
+        //re-initialize
+        sfh = Helper.NewSFH(); //SafeFileHandle
+        sfh.shfld1 = Helper.NewSFH();
+        sfh.shfld2 = Helper.NewSFH();
+        shValue = Helper.SHInt32(sfh);
+        shfld1Value = Helper.SHInt32(sfh.shfld1);
+        shfld2Value = Helper.SHInt32(sfh.shfld2);
+        String sfhstr = "SafeFileHandle";
+
+        Console.WriteLine("Testing SHObjectParam with SFH...");
+        Assert.Throws<ArgumentException>(() => SHObjectParam(sfh, shValue, shfld1Value, shfld2Value, sfhstr), "FAILED! Expected Exception Not Thrown!");
+
+        //re-initialize SH's that will be wrapped for the structure fields
+        sfh = Helper.NewSFH(); //SafeFileHandle
+        sfh.shfld1 = Helper.NewSFH();
+        sfh.shfld2 = Helper.NewSFH();
+        shValue = Helper.SHInt32(sfh);
+        shfld1Value = Helper.SHInt32(sfh.shfld1);
+        shfld2Value = Helper.SHInt32(sfh.shfld2);
+
+        sfh2 = Helper.NewSFH(); //SafeFileHandle
+        sfh2.shfld1 = Helper.NewSFH();
+        sfh2.shfld2 = Helper.NewSFH();
+        sh2Value = Helper.SHInt32(sfh2);
+        sh2fld1Value = Helper.SHInt32(sfh2.shfld1);
+        sh2fld2Value = Helper.SHInt32(sfh2.shfld2);
+
+        //re-initialize
+        sfh = Helper.NewSFH(); //SafeFileHandle
+        sfh.shfld1 = Helper.NewSFH();
+        sfh.shfld2 = Helper.NewSFH();
+        shValue = Helper.SHInt32(sfh);
+        shfld1Value = Helper.SHInt32(sfh.shfld1);
+        shfld2Value = Helper.SHInt32(sfh.shfld2);
+
+        StructWithObjFld sWithSFHFld = new StructWithObjFld();
+        sWithSFHFld.obj = sfh;
+
+        Console.WriteLine("Testing SHStructWithObjectFldParam with sWithSFHFld...");
+        Assert.Throws<ArgumentException>(() => SHStructWithObjectFldParam(sWithSFHFld, shValue, shfld1Value, shfld2Value, sfhstr), "FAILED! Expected Exception Not Thrown!");
+    }
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_In([In]ChildSafeFileHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Out(out ChildSFH_NoCloseHandle sh1);
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern ChildSFH_NoCloseHandle SHParam_OutRetVal();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Ref(ref ChildSFH_NoCloseHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Multiple([In]ChildSafeFileHandle sh1, out ChildSFH_NoCloseHandle sh2, ref ChildSFH_NoCloseHandle sh3, Int32 sh1Value, Int32 sh3Value);
+
+    /// <summary>
+    ///passing SafeFileHandle subclass parameters to unmanaged code in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it 
+    ///1-passing SafeFileHandle subclass parameters individually in separate methods (In, out, ref)
+    ///2-passing SafeFileHandle subclass parameters in combination in the same method
+    /// </summary>
+    public static void RunChildSHParamTests()
+    {
+        Console.WriteLine("\nRunChildSHParamTests():");
+
+        //1-passing SafeFileHandle subclass parameters individually in separate methods (In, out, ref)
+
+        //get a new SH
+        ChildSafeFileHandle hnd = Helper.NewChildSFH();
+        Int32 hndInt32 = Helper.SHInt32(hnd); //get the 32-bit value associated with hnd
+
+        Console.WriteLine("Testing SHParam_In...");
+        Assert.IsTrue(SHParam_In(hnd, hndInt32), "FAILED! SHParam_In did not receive hnd as expected.");
+        //check that the value of the HANDLE did not change
+        Assert.IsFalse(Helper.IsChanged(hnd), "FAILED! SHParam_In did not return hnd as expected.");
+
+        Console.WriteLine("Testing SHParam_Out...");
+        ChildSFH_NoCloseHandle hndout;
+        SHParam_Out(out hndout);
+        //check that the value of the HANDLE changed
+        Assert.IsTrue(Helper.IsChanged(hndout), "FAILED! SHParam_Out did not return hndout as expected.");
+
+        Console.WriteLine("Testing SHParam_OutRetVal...");
+        hndout = null;
+        hndout = SHParam_OutRetVal();
+        //check that the value of the HANDLE changed
+        Assert.IsTrue(Helper.IsChanged(hndout), "FAILED! SHParam_OutRetVal did not return hndout as expected.");
+
+        hndout = Helper.NewChildSFH_NoCloseHandle(); //get a new value
+        hndInt32 = Helper.SHInt32(hndout);
+        Console.WriteLine("Testing SHParam_Ref...");
+        Assert.IsTrue(SHParam_Ref(ref hndout, hndInt32), "FAILED! SHParam_Ref did not receive hndout as expected.");
+        //check that the value of the HANDLE changed
+        Assert.IsTrue(Helper.IsChanged(hndout), "FAILED! SHParam_Ref did not return hndout as expected.");
+
+        //2-passing SafeFileHandle subclass parameters in combination in the same method
+
+        //initialize parameters
+        ChildSafeFileHandle hnd1 = Helper.NewChildSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(hnd1); //get the 32-bit value associated with hnd1
+
+        ChildSFH_NoCloseHandle hnd2 = null; //out parameter
+
+        ChildSFH_NoCloseHandle hnd3 = Helper.NewChildSFH_NoCloseHandle();
+        Int32 hnd3Int32 = Helper.SHInt32(hnd3); //get the 32-bit value associated with hnd3
+
+        Console.WriteLine("Testing SHParam_Multiple...");
+        Assert.IsTrue(SHParam_Multiple(hnd1, out hnd2, ref hnd3, hnd1Int32, hnd3Int32), "FAILED! SHParam_Multiple did not receive parameter(s) as expected.");
+        //check that the value of the HANDLES are as expected
+        Assert.IsFalse(Helper.IsChanged(hnd1), "FAILED! SHParam_Multiple did not return handle:hnd1 as expected.");
+        Assert.IsTrue(Helper.IsChanged(hnd2), "FAILED! SHParam_Multiple did not return handle:hnd2 as expected.");
+        Assert.IsTrue(Helper.IsChanged(hnd3), "FAILED! SHParam_Multiple did not return handle:hnd3 as expected.");
+    }
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_In([In]StructWithChildSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Out(out StructWithChildSHFld s);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref1(ref StructWithChildSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref2(ref StructWithChildSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple1([In]StructWithChildSHFld sh1, out StructWithChildSHFld sh2,
+        ref StructWithChildSHFld sh3, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple2([In]StructWithChildSHFld sh1, ref StructWithChildSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple3([In]StructWithChildSHFld sh1, ref StructWithChildSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    /// <summary>
+    ///passing structures (with SafeFileHandle subclass fields) as parameters in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it
+    ///1-passing structures (In, out, ref) (with SafeFileHandle subclass fields) individually in separate methods
+    ///2-passing structures (In, out, ref) (with SafeFileHandle subclass fields) in combination in the same method
+    /// </summary>
+    public static void RunChildSHStructParamTests()
+    {
+        Console.WriteLine("\nRunChildSHStructParamTests():");
+
+        //1-passing structures (In, out, ref) (with SafeFileHandle subclass fields) individually in separate methods
+
+        //initialize a new StructWithChildSHFld
+        StructWithChildSHFld s = new StructWithChildSHFld();
+        s.hnd = Helper.NewChildSFH(); //get a new SH
+        Int32 hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+
+        Console.WriteLine("Testing SHStructParam_In...");
+        Assert.IsTrue(SHStructParam_In(s, hndInt32), "FAILED! SHStructParam_In did not receive param as expected.");
+        //check that the value of the HANDLE field did not change
+        Assert.IsFalse(Helper.IsChanged(s.hnd), "FAILED! SHStructParam_In did not return param as expected.");
+
+        Console.WriteLine("Testing SHStructParam_Out...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Out(out s), "FAILED!  Expected Exception not thrown.");
+
+        s.hnd = Helper.NewChildSFH(); //get a new SH
+        hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+        Console.WriteLine("Testing SHStructParam_Ref1 (does not change value of handle field)...");
+        Assert.IsTrue(SHStructParam_Ref1(ref s, hndInt32), "FAILED! SHStructParam_Ref1 did not receive param as expected.");
+        //check that the value of the HANDLE field is not changed
+        Assert.IsFalse(Helper.IsChanged(s.hnd), "FAILED! SHStructParam_Ref1 did not return param as expected.");
+
+        Console.WriteLine("Testing SHStructParam_Ref2 (does change value of handle field)...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Ref2(ref s, hndInt32), "FAILED!  Expected Exception not thrown.");
+
+        //2-passing structures (In, out, ref) (with SafeFileHandle subclass fields) in combination in the same method
+
+        //initialize parameters
+        StructWithChildSHFld s1 = new StructWithChildSHFld();
+        s1.hnd = Helper.NewChildSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(s1.hnd); //get the 32-bit value associated with s1.hnd
+
+        StructWithChildSHFld s2; //out parameter
+
+        StructWithChildSHFld s3 = new StructWithChildSHFld();
+        s3.hnd = Helper.NewChildSFH();
+        Int32 hnd3Int32 = Helper.SHInt32(s3.hnd); //get the 32-bit value associated with s3.hnd
+
+        Console.WriteLine("Testing SHStructParam_Multiple1 (takes an out struct as one of the params and so is expected to result in an exception)...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Multiple1(s1, out s2, ref s3, hnd1Int32, hnd3Int32), "FAILED!  Exception not thrown.");
+
+        s3.hnd = Helper.NewChildSFH();
+        hnd3Int32 = Helper.SHInt32(s3.hnd); //get the 32-bit value associated with s3.hnd
+        Console.WriteLine("Testing SHStructParam_Multiple2 (takes a ref struct as one of the params)...");
+        Assert.IsTrue(SHStructParam_Multiple2(s1, ref s3, hnd1Int32, hnd3Int32), "FAILED! SHStructParam_Multiple2 did not receive parameter(s) as expected.");
+        //check that the value of the HANDLES are as expected
+        Assert.IsFalse(Helper.IsChanged(s1.hnd) || Helper.IsChanged(s3.hnd), "FAILED! SHStructParam_Multiple2 did not return handles as expected.");
+
+        Console.WriteLine("Testing SHStructParam_Multiple3 (takes a ref struct as one of the params and changes it and so is expected to result in an exception)...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Multiple3(s1, ref s3, hnd1Int32, hnd3Int32), "FAILED!  Expected Exception not thrown.");
+    }
+}
+#pragma warning restore 618
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SafeHandles/Misc/MiscTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/Misc/MiscTest.csproj
new file mode 100644 (file)
index 0000000..ff40161
--- /dev/null
@@ -0,0 +1,39 @@
+<?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>MiscTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="MiscTest.cs" />
+    <Compile Include="..\*.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\CMakeLists.txt" />
+    <ProjectReference Include="..\Interface\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/SafeHandles/ReleaseHandle/CMakeLists.txt b/tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/CMakeLists.txt
new file mode 100644 (file)
index 0000000..baf7c1e
--- /dev/null
@@ -0,0 +1,14 @@
+#VCXPROJ 
+cmake_minimum_required (VERSION 2.6) 
+project (PInvoke_SafeHandle_ReleaseHandle) 
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") 
+include_directories(${INC_PLATFORM_DIR}) 
+set(SOURCES 
+    ReleaseHandleNative.cpp 
+) 
+# Additional files to reference: 
+#    ReleaseHandleNative.def 
+# add the executable 
+add_library (PInvoke_SafeHandle_ReleaseHandle SHARED ${SOURCES}) 
+# add the install targets 
+install (TARGETS PInvoke_SafeHandle_ReleaseHandle DESTINATION bin) 
diff --git a/tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleNative.cpp b/tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleNative.cpp
new file mode 100644 (file)
index 0000000..2f90a35
--- /dev/null
@@ -0,0 +1,41 @@
+// 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.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <xplatform.h>
+
+bool g_myResourceReleaseMethodCalled = false;
+
+extern "C" DLL_EXPORT void __stdcall MyResourceReleaseMethod(HANDLE hnd)
+{
+    g_myResourceReleaseMethodCalled = true;
+    
+    //call CloseHandle to actually release the handle corresponding to the SafeFileHandle
+    CloseHandle(hnd);
+}
+
+extern "C" DLL_EXPORT bool GetMyResourceReleaseMethodCalled()
+{
+    return g_myResourceReleaseMethodCalled;
+}
+
+extern "C" DLL_EXPORT void ResetMyResourceReleaseMethodCalled()
+{
+    g_myResourceReleaseMethodCalled = false;
+}
+
+extern "C" DLL_EXPORT void __stdcall SHReleasing_OutParams(IUnknown* ppIUnknFOO, HANDLE* phnd, IUnknown** ppIUnknBAR, int* pInt)
+{
+    //initialize the hnd out param
+    *phnd = (HANDLE)123;
+
+    //initialize the IUnknBAR out param
+    *ppIUnknBAR = ppIUnknFOO;
+    ppIUnknFOO->AddRef(); //addref Foo
+
+    //initialize the int out param
+    *pInt = 123;
+}
diff --git a/tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleTest.cs b/tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleTest.cs
new file mode 100644 (file)
index 0000000..b8e16c7
--- /dev/null
@@ -0,0 +1,102 @@
+// 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.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.ConstrainedExecution;
+using SafeHandlesTests;
+using TestLibrary;
+
+class SafeFileHandle : SafeHandle //SafeHandle subclass
+{
+    private static readonly IntPtr _invalidHandleValue = new IntPtr(-1);
+
+    //0 or -1 considered invalid
+    public override bool IsInvalid
+    {
+        get { return handle == IntPtr.Zero || handle == _invalidHandleValue; }
+    }
+
+    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+    [DllImport("PInvoke_SafeHandle_ReleaseHandle")]
+    private static extern bool MyResourceReleaseMethod(IntPtr handle);
+
+    //default constructor which just calls the base class constructor
+    public SafeFileHandle()
+        : base(IntPtr.Zero, true)
+    {
+    }
+
+    override protected bool ReleaseHandle()
+    {
+        //     this method will not actually call any resource releasing API method
+        //     since the out/ref SFH param is not actually initialized to an OS allocated
+        //     HANDLE---instead the unmanaged side just initializes/changes it to some integer;
+        //     If a resource releasing API method like CloseHandle were called then
+        //     it would return false and an unhandled exception would be thrown by the
+        //     runtime indicating that the release method failed
+        MyResourceReleaseMethod(handle);
+        return true;
+    }
+
+} //end of SafeFileHandle class
+
+class Foo
+{
+    int FooMethod(int x, int y) { return x + y; }
+}
+
+class Bar
+{
+    void BarMethod() { }
+}
+
+internal class SHReleasingTester
+{
+    [DllImport("PInvoke_SafeHandle_ReleaseHandle")]
+    private static extern void SHReleasing_OutParams(
+        [MarshalAs(UnmanagedType.Interface)]Foo foo,
+        out SafeFileHandle sh,
+        [MarshalAs(UnmanagedType.Interface)]out Bar bar, out int x);
+
+    [DllImport("PInvoke_SafeHandle_ReleaseHandle")]
+    [return:MarshalAs(UnmanagedType.I1)]private static extern bool GetMyResourceReleaseMethodCalled();
+
+    [DllImport("PInvoke_SafeHandle_ReleaseHandle")]
+    private static extern void ResetMyResourceReleaseMethodCalled();
+
+    public static int Main()
+    {
+        try{
+            Console.WriteLine("SHReleasing_OutParams");
+            SafeFileHandle sh;
+            Foo foo = new Foo();
+            Bar bar;
+            int x;
+
+            ResetMyResourceReleaseMethodCalled();
+
+            //this unmanaged method will try to set the out Bar parameter to a Foo type
+            //this should cause an InvalidCastException on the way back from unmanaged
+            Assert.Throws<InvalidCastException>(() => SHReleasing_OutParams(foo, out sh, out bar, out x), "SHReleasing_OutParams");
+
+            //force the finalizer for the SFH param to run
+            Console.WriteLine("\tForcing finalizer for the SFH param to run...");
+            sh = null;
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+
+            Assert.IsTrue(GetMyResourceReleaseMethodCalled(), "MyResourceReleaseMethod was NOT called");
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    }
+} 
+
diff --git a/tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/ReleaseHandle/ReleaseHandleTest.csproj
new file mode 100644 (file)
index 0000000..54fa726
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>ReleaseHandleTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="ReleaseHandleTest.cs" />
+    <Compile Include="..\*.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/SafeHandles/SafeFileHandle.cs b/tests/src/Interop/PInvoke/SafeHandles/SafeFileHandle.cs
new file mode 100644 (file)
index 0000000..b88696f
--- /dev/null
@@ -0,0 +1,237 @@
+// 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 System.Runtime.ConstrainedExecution;
+
+//     special subclass for out/ref ChildSFH params that are changed in unmanaged code 
+//     (see comments above ReleaseHandle for details)
+namespace SafeHandlesTests{
+    public class ChildSFH_NoCloseHandle : SafeFileHandle
+    {
+        ///////////////////////////////////////////////////////////
+        private static readonly IntPtr _invalidHandleValue = new IntPtr(-1);
+
+        //0 or -1 considered invalid
+        public override bool IsInvalid
+        {
+            get { return handle == IntPtr.Zero || handle == _invalidHandleValue; }
+        }
+
+        //each SafeHandle subclass will expose a static method for instance creation
+        [DllImport("api-ms-win-core-file-l1-2-1", EntryPoint = "CreateFileW", SetLastError = true)]
+        public static extern ChildSFH_NoCloseHandle CreateChildSafeFileHandle(String lpFileName,
+            DesiredAccess dwDesiredAccess, ShareMode dwShareMode,
+            IntPtr lpSecurityAttributes, CreationDisposition dwCreationDisposition,
+            FlagsAndAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);
+
+        //default constructor which just calls the base class constructor
+        public ChildSFH_NoCloseHandle()
+            : base()
+        {
+        }
+
+        //     this method will not actually call any resource releasing API method
+        //     since the out/ref ChildSFH param is not actually initialized to an OS allocated
+        //     HANDLE---instead the unmanaged side just initializes/changes it to some integer;
+        //     If a resource releasing API method like CloseHandle were called then
+        //     it would return false and an unhandled exception would be thrown by the
+        //     runtime indicating that the release method failed
+        override protected bool ReleaseHandle()
+        {
+            return true;
+        }
+
+    } //end fo ChildSFH_NoCloseHandle
+
+    public class ChildSafeFileHandle : SafeFileHandle
+    {
+        //each SafeHandle subclass will expose a static method for instance creation
+        [DllImport("api-ms-win-core-file-l1-2-1", EntryPoint = "CreateFileW", SetLastError = true)]
+        public static extern ChildSafeFileHandle CreateChildSafeFileHandle(String lpFileName,
+            DesiredAccess dwDesiredAccess, ShareMode dwShareMode,
+            IntPtr lpSecurityAttributes, CreationDisposition dwCreationDisposition,
+            FlagsAndAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);
+
+        //default constructor which just calls the base class constructor
+        public ChildSafeFileHandle()
+            : base()
+        {
+        }
+
+    } //end fo ChildSafeFileHandle
+
+    // special subclass for out/ref SFH params that are changed in unmanaged code 
+    // (see comments above ReleaseHandle for details)
+    public class SFH_NoCloseHandle : SafeHandle //SafeHandle subclass
+    {
+        ///////////////////////////////////////////////////////////
+        private static readonly IntPtr _invalidHandleValue = new IntPtr(-1);
+
+        //0 or -1 considered invalid
+        public override bool IsInvalid
+        {
+            get { return handle == IntPtr.Zero || handle == _invalidHandleValue; }
+        }
+
+        //each SafeHandle subclass will expose a static method for instance creation
+        [DllImport("api-ms-win-core-file-l1-2-1", EntryPoint = "CreateFileW", SetLastError = true)]
+        public static extern SFH_NoCloseHandle CreateFile(String lpFileName,
+                                                DesiredAccess dwDesiredAccess, ShareMode dwShareMode,
+                                                IntPtr lpSecurityAttributes, CreationDisposition dwCreationDisposition,
+                                                FlagsAndAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);
+
+        //default constructor which just calls the base class constructor
+        public SFH_NoCloseHandle()
+            : base(IntPtr.Zero, true)
+        {
+        }
+
+        //     this method will not actually call any resource releasing API method
+        //     since the out/ref SFH param is not actually initialized to an OS allocated
+        //     HANDLE---instead the unmanaged side just initializes/changes it to some integer;
+        //     If a resource releasing API method like CloseHandle were called then
+        //     it would return false and an unhandled exception would be thrown by the
+        //     runtime indicating that the release method failed
+        override protected bool ReleaseHandle()
+        {
+            return true;
+        }
+
+    } //end of SFH_NoCloseHandle class
+
+    public class SafeFileHandle : SafeHandle //SafeHandle subclass
+    {
+        //public fields and properties
+        public SafeHandle shfld1;
+
+        public SafeFileHandle shfld2;
+        public SafeFileHandle shfld2_prop
+        {
+            [return: MarshalAs(UnmanagedType.Interface)]
+            get { return shfld2; }
+            [param: MarshalAs(UnmanagedType.Interface)]
+            set { shfld2 = value; }
+        }
+
+        ///////////////////////////////////////////////////////////
+        private static readonly IntPtr _invalidHandleValue = new IntPtr(-1);
+
+        //0 or -1 considered invalid
+        public override bool IsInvalid
+        {
+            get { return handle == IntPtr.Zero || handle == _invalidHandleValue; }
+        }
+
+        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+        [DllImport("api-ms-win-core-handle-l1-1-0", SetLastError = true)]
+        private static extern bool CloseHandle(IntPtr handle);
+
+
+        //each SafeHandle subclass will expose a static method for instance creation
+        [DllImport("api-ms-win-core-file-l1-2-1", EntryPoint = "CreateFileW", SetLastError = true)]
+        public static extern SafeFileHandle CreateFile(String lpFileName,
+                                                DesiredAccess dwDesiredAccess, ShareMode dwShareMode,
+                                                IntPtr lpSecurityAttributes, CreationDisposition dwCreationDisposition,
+                                                FlagsAndAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);
+
+        //default constructor which just calls the base class constructor
+        public SafeFileHandle()
+            : base(IntPtr.Zero, true)
+        {
+        }
+
+        override protected bool ReleaseHandle()
+        {
+            return CloseHandle(handle);
+        }
+
+    } //end of SafeFileHandle class
+
+    /// <summary>
+    /// The following public enums are for use in creating the 
+    /// new file i.e. setting attributes etc. on the file
+    /// </summary>
+
+    // This enumeration defines the level of desired access. The
+    // enumeration contains a special member for querying the
+    // device without accessing it.
+    public enum DesiredAccess : uint
+    {
+        QueryDeviceOnly = 0,
+        GENERIC_READ = 0x80000000,
+        GENERIC_WRITE = 0x40000000,
+        GENERIC_EXECUTE = 0x20000000,
+        GENERIC_ALL = 0x10000000,
+        DELETE = 0x00010000,
+        READ_CONTROL = 0x00020000,
+        WRITE_DAC = 0x00040000,
+        WRITE_OWNER = 0x00080000,
+        SYNCHRONIZE = 0x00100000,
+        STANDARD_RIGHTS_REQUIRED = 0x000F0000,
+        STANDARD_RIGHTS_READ = READ_CONTROL,
+        STANDARD_RIGHTS_WRITE = READ_CONTROL,
+        STANDARD_RIGHTS_EXECUTE = READ_CONTROL,
+        STANDARD_RIGHTS_ALL = 0x001F0000,
+        SPECIFIC_RIGHTS_ALL = 0x0000FFFF,
+        ACCESS_SYSTEM_SECURITY = 0x01000000,
+        MAXIMUM_ALLOWED = 0x02000000
+    }
+
+    // This enumeration defines the type of sharing to support. It
+    // includes a special member for no sharing at all.
+    public enum ShareMode
+    {
+        NotShared = 0,
+        FILE_SHARE_READ = 0x00000001,
+        FILE_SHARE_WRITE = 0x00000002,
+        FILE_SHARE_DELETE = 0x00000004
+    }
+
+    // This enumeration defines how the call will treat files or
+    // other objects that already exist. You must provide one of
+    // these values as input.
+    public enum CreationDisposition
+    {
+        CREATE_NEW = 1,
+        CREATE_ALWAYS = 2,
+        OPEN_EXISTING = 3,
+        OPEN_ALWAYS = 4,
+        TRUNCATE_EXISTING = 5
+    }
+
+    // This enumeration defines additional flags and attributes the
+    // call will use when opening an object. This enumeration contains
+    // as special value for no flags or attributes.
+    public enum FlagsAndAttributes : uint
+    {
+        None = 0,
+        FILE_ATTRIBUTE_READONLY = 0x00000001,
+        FILE_ATTRIBUTE_HIDDEN = 0x00000002,
+        FILE_ATTRIBUTE_SYSTEM = 0x00000004,
+        FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
+        FILE_ATTRIBUTE_NORMAL = 0x00000080,
+        FILE_ATTRIBUTE_TEMPORARY = 0x00000100,
+        FILE_ATTRIBUTE_OFFLINE = 0x00001000,
+        FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
+        FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
+        FILE_FLAG_WRITE_THROUGH = 0x80000000,
+        FILE_FLAG_OVERLAPPED = 0x40000000,
+        FILE_FLAG_NO_BUFFERING = 0x20000000,
+        FILE_FLAG_RANDOM_ACCESS = 0x10000000,
+        FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000,
+        FILE_FLAG_DELETE_ON_CLOSE = 0x04000000,
+        FILE_FLAG_BACKUP_SEMANTICS = 0x02000000,
+        FILE_FLAG_POSIX_SEMANTICS = 0x01000000,
+        FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000,
+        FILE_FLAG_OPEN_NO_RECALL = 0x00100000,
+        SECURITY_ANONYMOUS = 0x00000000,
+        SECURITY_IDENTIFICATION = 0x00010000,
+        SECURITY_IMPERSONATION = 0x00020000,
+        SECURITY_DELEGATION = 0x00030000,
+        SECURITY_CONTEXT_TRACKING = 0x00040000,
+        SECURITY_EFFECTIVE_ONLY = 0x00080000
+    }
+}
diff --git a/tests/src/Interop/PInvoke/SafeHandles/SafeHandleNative.cpp b/tests/src/Interop/PInvoke/SafeHandles/SafeHandleNative.cpp
new file mode 100644 (file)
index 0000000..0cbcd87
--- /dev/null
@@ -0,0 +1,334 @@
+// 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.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <xplatform.h>
+#include "StructDefs.h" //all the unmanaged struct defs are in this file
+
+//Global Handle Return Value
+HANDLE returnHandleValue = (HANDLE)123;
+
+//Method for Invalid MarshalAs tests
+extern "C" DLL_EXPORT BOOL __stdcall SHFldInvalid_MA(StructWithSHFld s)
+{
+    printf("\t\tIN SHFldInvalid_MA!\n");
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT HANDLE __stdcall SHInvalid_retMA16()
+{
+    printf("\t\tIN SHInvalid_retMA16!\n");
+    HANDLE *hnd = (HANDLE*)GlobalAlloc(0, sizeof(HANDLE));  //new HANDLE
+    *hnd = returnHandleValue;
+    return (*hnd);
+}
+
+//////////////Methods used by RunParamTests
+///////////////////////////////////////////
+extern "C" DLL_EXPORT BOOL __stdcall SHParam_In(HANDLE sh1, int sh1Value)
+{
+    if( (intptr_t)sh1 != sh1Value )
+        return FALSE;
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHParam_Out(HANDLE* sh1)
+{
+    *sh1 = returnHandleValue;
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT HRESULT _stdcall SHParam_OutRetVal(HANDLE* sh1)
+{
+    *sh1 = returnHandleValue;
+    return S_OK;
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHParam_Ref(HANDLE* sh1, int sh1Value)
+{
+    if( (intptr_t)*sh1 != sh1Value )
+        return FALSE;
+    //change the value of the HANDLE---this is equivalent to assigning a new HANDLE
+    *sh1 = returnHandleValue;
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHParam_Multiple(HANDLE sh1, HANDLE* sh2, HANDLE* sh3, int sh1Value, int sh3Value)
+{
+    if( (intptr_t)sh1 != sh1Value )
+    {
+        printf("\t\tIn SHParam_Multiple: sh1 value not received as expected.\n");
+        return FALSE;
+    }
+    else if( (intptr_t)*sh3 != sh3Value )
+    {
+        printf("\t\tIn SHParam_Multiple: sh3 value not received as expected.\n");
+        //printf("\t\t\t*sh3 = %d, sh3Value = %d\n", (int)*sh3, sh3Value);
+        return FALSE;
+    }
+    //change the out and ref values
+    *sh2 = returnHandleValue;
+    *sh3 = returnHandleValue;
+    return TRUE; 
+}
+
+//////////////Methods used by RunStructParamTests
+/////////////////////////////////////////////////
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_In(StructWithSHFld s, int shndValue)
+{
+    if( (intptr_t)s.hnd != shndValue ) 
+        return FALSE;
+    s.hnd = returnHandleValue; //try to change hnd value; should not be reflected on managed side
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_Out(StructWithSHFld* ps)
+{
+    ps->hnd = returnHandleValue;
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT HRESULT _stdcall SHStructParam_OutRetVal(StructWithSHFld* ps)
+{
+    ps->hnd = returnHandleValue;
+    return S_OK;
+}
+
+//this method will NOT change the value of the ref SH subclass field
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_Ref1(StructWithSHFld* ps, int shndValue)
+{
+    if( (intptr_t)(ps->hnd) != shndValue ) 
+        return FALSE;
+    return TRUE;
+}
+
+//this method will change the value of the SH subclass field
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_Ref2(StructWithSHFld* ps, int shndValue)
+{
+    if( (intptr_t)(ps->hnd) != shndValue ) 
+        return FALSE;
+    //change the value of the HANDLE---this is equivalent to assigning a new HANDLE
+    ps->hnd = returnHandleValue;
+    return TRUE;
+}
+
+//this takes an out param and so is expected to result in an exception
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_Multiple1(StructWithSHFld s1, StructWithSHFld* ps2,
+                                       StructWithSHFld* ps3, int s1hndValue, 
+                                       int s3hndValue)
+{
+    if( (intptr_t)s1.hnd != s1hndValue || (intptr_t)(ps3->hnd) != s3hndValue ) 
+        return FALSE;
+    //change the handle field of the out parameter
+    ps2->hnd = returnHandleValue;
+    return TRUE;
+}
+
+//this takes a ref param and does not change the SH subclass field
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_Multiple2(StructWithSHFld s1, StructWithSHFld* ps2,
+                                       int s1hndValue, int s2hndValue)
+{
+    if( (intptr_t)s1.hnd != s1hndValue || (intptr_t)(ps2->hnd) != s2hndValue ) 
+        return FALSE;
+    return TRUE;
+}
+
+//this takes a ref param and tries to change the SH subclass field and so is expected to result in an exception
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_Multiple3(StructWithSHFld s1, StructWithSHFld* ps2,
+                                       int s1hndValue, int s2hndValue)
+{
+    if( (intptr_t)s1.hnd != s1hndValue || (intptr_t)(ps2->hnd) != s2hndValue ) 
+        return FALSE;
+    //change the handle field of the ref parameter
+    ps2->hnd = returnHandleValue;
+    return TRUE;
+}
+
+//////////////Methods used by RunMiscellaneousTests
+///////////////////////////////////////////////////
+extern "C" DLL_EXPORT BOOL __stdcall SHArrayParam(HANDLE* ptoArr, int* ptoArrInt32s, int length)
+{
+    for(int i = 0; i < length; i++)
+        if( (intptr_t)ptoArr[i] != ptoArrInt32s[i] )
+        {
+            printf("\t\tptoArr[%d] = %d, ptoArrInt32s[%d] = %d\n", i, (int)(intptr_t)ptoArr[i], i, ptoArrInt32s[i]);
+            return FALSE;
+        }
+        return TRUE;
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHStructArrayParam(StructWithSHFld* ptoArr, int* ptoArrInt32s, int length)
+{
+    for(int i = 0; i < length; i++)
+        if( (intptr_t)(ptoArr[i].hnd) != ptoArrInt32s[i] )
+            return FALSE;
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT HANDLE __stdcall SHReturn()
+{
+    HANDLE *hnd = (HANDLE*)GlobalAlloc(0, sizeof(HANDLE));  //new HANDLE
+    *hnd = returnHandleValue;
+    return (*hnd);
+}
+
+extern "C" DLL_EXPORT StructWithSHFld __stdcall        SHReturnStruct()
+{
+    StructWithSHFld* ptoStruct = (StructWithSHFld*)GlobalAlloc(0, sizeof(StructWithSHFld));
+    ptoStruct->hnd = returnHandleValue;
+    return (*ptoStruct);
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHStructNestedParam(StructNestedParent sn, int shndValue)
+{
+    if( (intptr_t)sn.snOneDeep.s.hnd != shndValue ) 
+        return FALSE;
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHStructParam_In2(StructWithSHArrayFld s, int* ptoArrInt32s, int length)
+{
+    for(int i = 0; i < length; i++)
+        if( (intptr_t)s.sharr[i] != ptoArrInt32s[i] )
+        {
+            printf("\t\ts.sharr[%d] = %d, ptoArrInt32s[%d] = %d\n", i, (int)(intptr_t)s.sharr[i], i, ptoArrInt32s[i]);
+            return FALSE;
+        }
+        return TRUE;
+}
+
+extern "C" DLL_EXPORT BOOL __stdcall SHMixedParam1(HANDLE sh1, HANDLE* sh2, HANDLE* sh3, StructWithSHFld s1, StructWithSHFld s2,
+                             StructWithSHFld* s3, int sh1Value, int sh3Value, int s1fldValue, int s2fldValue,
+                             int s3fldValue)
+{
+    if( (intptr_t)sh1 != sh1Value || (intptr_t)*sh3 != sh3Value || (intptr_t)s1.hnd != s1fldValue || (intptr_t)s2.hnd != s2fldValue
+        || (intptr_t)s3->hnd != s3fldValue )
+        return FALSE;
+    *sh2 = returnHandleValue; //the out parameter
+    return TRUE;
+}
+
+BOOL static CertifyStructWithManySHFlds(StructWithManySHFlds s, int* ptoArrInt32s)
+{
+    if( (intptr_t)s.hnd1 != ptoArrInt32s[0] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd1 != ptoArrInt32s[0]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd2 != ptoArrInt32s[1] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd2 != ptoArrInt32s[1]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd3 != ptoArrInt32s[2] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd3 != ptoArrInt32s[2]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd4 != ptoArrInt32s[3] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd4 != ptoArrInt32s[3]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd5 != ptoArrInt32s[4] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd5 != ptoArrInt32s[4]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd6 != ptoArrInt32s[5] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd6 != ptoArrInt32s[5]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd7 != ptoArrInt32s[6] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd7 != ptoArrInt32s[6]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd8 != ptoArrInt32s[7] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd8 != ptoArrInt32s[7]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd9 != ptoArrInt32s[8] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd9 != ptoArrInt32s[8]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd10 != ptoArrInt32s[9] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd10 != ptoArrInt32s[9]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd11 != ptoArrInt32s[10] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd11 != ptoArrInt32s[10]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd12 != ptoArrInt32s[11] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd12 != ptoArrInt32s[11]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd13 != ptoArrInt32s[12] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd13 != ptoArrInt32s[12]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd14 != ptoArrInt32s[13] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd14 != ptoArrInt32s[13]\n");
+        return FALSE;
+    }
+    else if( (intptr_t)s.hnd15 != ptoArrInt32s[14] )
+    {
+        printf("\t\tCertifyStructWithManySHFlds: s.hnd15 != ptoArrInt32s[14]\n");
+        return FALSE;
+    }
+    else
+        return TRUE;
+}
+
+//ptoArrInt32 is a pointer to an array of int32s---each element of which matches a fld of the struct
+extern "C" DLL_EXPORT BOOL __stdcall SHStructWithManySHFldsParam_In(StructWithManySHFlds s, int* ptoArrInt32s)
+{
+    if ( !CertifyStructWithManySHFlds(s, ptoArrInt32s) )
+        return FALSE;
+    s.hnd14    = returnHandleValue; //try to change a fld; should not be reflected on managed side
+    return TRUE;
+}
+
+//ptoArrInt32 is a pointer to an array of int32s---each element of which matches a fld of the struct
+//Ref1 will not change any of the flds
+extern "C" DLL_EXPORT BOOL __stdcall SHStructWithManySHFldsParam_Ref1(StructWithManySHFlds* ptos, int* ptoArrInt32s)
+{
+    return CertifyStructWithManySHFlds(*ptos, ptoArrInt32s);
+}
+
+//ptoArrInt32 is a pointer to an array of int32s---each element of which matches a fld of the struct
+//Ref2 will change one of the flds
+extern "C" DLL_EXPORT BOOL __stdcall SHStructWithManySHFldsParam_Ref2(StructWithManySHFlds* ptos, int* ptoArrInt32s)
+{
+    if( !CertifyStructWithManySHFlds(*ptos, ptoArrInt32s) )
+        return FALSE;
+    else
+        (*ptos).hnd14 = returnHandleValue; //change one of the flds
+    return TRUE;
+}
+
+extern "C" DLL_EXPORT int __stdcall SHInvalid_MA()
+{
+    // NOT REACHABLE from test because we are supposed to throw MarshalDirectionException from stubs
+    // This is needed to make ProjectN happy because ProjectN always do early binding
+    return 0;
+}
+
+extern "C" DLL_EXPORT int __stdcall SHInvalid_retMA()
+{
+    // NOT REACHABLE from test because we are supposed to throw MarshalDirectionException from stubs
+    // This is needed to make ProjectN happy because ProjectN always do early binding
+    return 0;
+}
diff --git a/tests/src/Interop/PInvoke/SafeHandles/SigDiff/SigDiffTest.cs b/tests/src/Interop/PInvoke/SafeHandles/SigDiff/SigDiffTest.cs
new file mode 100644 (file)
index 0000000..c67673b
--- /dev/null
@@ -0,0 +1,176 @@
+// 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 SafeHandlesTests;
+using TestLibrary;
+
+public class SHTester_SigDiff
+{
+    public static int Main()
+    {
+        try
+        {
+            RunSHParamTests();
+            RunSHStructParamTests();
+
+            return 100;
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+    } //end of Main
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_In([In]SafeHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Out(out SafeHandle sh1);
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern SafeHandle SHParam_OutRetVal();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Ref(ref SafeHandle sh1, Int32 sh1Value);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHParam_Multiple([In]SafeHandle sh1, out SafeHandle sh2, ref SafeHandle sh3, Int32 sh1Value, Int32 sh3Value);
+
+    /// <summary>
+    ///passing SH parameters to unmanaged code in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it 
+    ///1-  passing SH parameters individually in separate methods (In, out, ref)
+    ///2-  passing SH parameters in combination in the same method
+    /// </summary>
+    public static void RunSHParamTests()
+    {
+        Console.WriteLine("\nRunSHParamTests():");
+
+        //1-  passing SH parameters individually in separate methods (In, out, ref)
+
+        //get a new SH
+        SafeHandle hnd = Helper.NewSFH(); //NOTE that this is equivalent to SafeHandle = SafeFileHandle.CreateFile();
+        Int32 hndInt32 = Helper.SHInt32(hnd); //get the 32-bit value associated with hnd
+
+        Console.WriteLine("Testing SHParam_In...");
+        Assert.IsTrue(SHParam_In(hnd, hndInt32), "FAILED! SHParam_In did not receive hnd as expected.");
+        //check that the value of the HANDLE did not change
+        Assert.IsFalse(Helper.IsChanged(hnd), "FAILED! SHParam_In did not return hnd as expected.");
+
+        Console.WriteLine("Testing SHParam_Out...");
+        Assert.Throws<MarshalDirectiveException>(() => SHParam_Out(out hnd), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHParam_OutRetVal...");
+        hnd = null;
+        Assert.Throws<MarshalDirectiveException>(() => hnd = SHParam_OutRetVal(), "FAILED!  Exception not thrown.");
+
+        hnd = Helper.NewSFH(); //get a new value
+        hndInt32 = Helper.SHInt32(hnd);
+        Console.WriteLine("Testing SHParam_Ref...");
+        Assert.Throws<MarshalDirectiveException>(() => SHParam_Ref(ref hnd, hndInt32), "FAILED!  Exception not thrown.");
+
+        //2-  passing SH parameters in combination in the same method
+
+        //initialize parameters
+        SafeHandle hnd1 = Helper.NewSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(hnd1); //get the 32-bit value associated with hnd1
+
+        SafeHandle hnd2 = null; //out parameter
+
+        SafeHandle hnd3 = Helper.NewSFH();
+        Int32 hnd3Int32 = Helper.SHInt32(hnd3); //get the 32-bit value associated with hnd3
+
+        Console.WriteLine("Testing SHParam_Multiple...");
+        Assert.Throws<MarshalDirectiveException>(() => SHParam_Multiple(hnd1, out hnd2, ref hnd3, hnd1Int32, hnd3Int32), "FAILED!  Exception not thrown.");
+    } //end of static method RunParamTests
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_In([In]StructWithBaseSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Out(out StructWithBaseSHFld s);
+
+    [DllImport("PInvoke_SafeHandle", PreserveSig = false, SetLastError = true)]
+    public static extern StructWithBaseSHFld SHStructParam_OutRetVal();
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref1(ref StructWithBaseSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Ref2(ref StructWithBaseSHFld s, Int32 shfldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple1([In]StructWithBaseSHFld sh1, out StructWithBaseSHFld sh2,
+        ref StructWithBaseSHFld sh3, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple2([In]StructWithBaseSHFld sh1, ref StructWithBaseSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    [DllImport("PInvoke_SafeHandle", SetLastError = true)]
+    public static extern bool SHStructParam_Multiple3([In]StructWithBaseSHFld sh1, ref StructWithBaseSHFld sh2, Int32 sh1fldValue, Int32 sh2fldValue);
+
+    /// <summary>
+    ///passing structures (with SH fields) as parameters in various combinations and forms;
+    ///it uses the PInvoke signatures defined above it
+    ///1-  passing structures (In, out, ref) (with SH fields) individually in separate methods
+    ///2-  passing structures (In, out, ref) (with SH fields) in combination in the same method
+    /// </summary>
+    public static void RunSHStructParamTests()
+    {
+        Console.WriteLine("\nRunSHStructParamTests():");
+
+        //1-  passing structures (In, out, ref) (with SH fields) individually in separate methods
+
+        //initialize a new StructWithBaseSHFld
+        StructWithBaseSHFld s = new StructWithBaseSHFld();
+        s.hnd = Helper.NewSFH(); //get a new SH
+        Int32 hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+
+        Console.WriteLine("Testing SHStructParam_In...");
+        Assert.IsTrue(SHStructParam_In(s, hndInt32), "FAILED! SHStructParam_In did not receive param as expected.");
+        Assert.IsFalse(Helper.IsChanged(s.hnd), "FAILED! SHStructParam_In did not return param as expected.");
+
+        Console.WriteLine("Testing SHStructParam_Out...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Out(out s), "FAILED!  Exception not thrown.");
+
+        Console.WriteLine("Testing SHStructParam_OutRetVal...");
+        Assert.Throws<MarshalDirectiveException>(() => s = SHStructParam_OutRetVal(), "FAILED!  Exception not thrown.");
+
+        s.hnd = Helper.NewSFH(); //get a new SH
+        hndInt32 = Helper.SHInt32(s.hnd); //get the 32-bit value associated with s.hnd
+        Console.WriteLine("Testing SHStructParam_Ref1 (does not change value of handle field)...");
+        Assert.IsTrue(SHStructParam_Ref1(ref s, hndInt32), "FAILED! SHStructParam_Ref1 did not receive param as expected.");
+        //check that the value of the HANDLE field is not changed
+        Assert.IsFalse(Helper.IsChanged(s.hnd), "FAILED! SHStructParam_Ref1 did not return param as expected.");
+
+        Console.WriteLine("Testing SHStructParam_Ref2 (does change value of handle field)...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Ref2(ref s, hndInt32), "FAILED!  Exception not thrown.");
+
+        //2-  passing structures (In, out, ref) (with SH fields) in combination in the same method
+
+        //initialize parameters
+        StructWithBaseSHFld s1 = new StructWithBaseSHFld();
+        s1.hnd = Helper.NewSFH();
+        Int32 hnd1Int32 = Helper.SHInt32(s1.hnd); //get the 32-bit value associated with s1.hnd
+        StructWithBaseSHFld s2; //out parameter
+        StructWithBaseSHFld s3 = new StructWithBaseSHFld();
+        s3.hnd = Helper.NewSFH();
+        Int32 hnd3Int32 = Helper.SHInt32(s3.hnd); //get the 32-bit value associated with s3.hnd
+
+        Console.WriteLine("Testing SHStructParam_Multiple1 (takes an out struct as one of the params and so is expected to result in an exception)...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Multiple1(s1, out s2, ref s3, hnd1Int32, hnd3Int32), "FAILED!  Exception not thrown.");
+
+        s3.hnd = Helper.NewSFH();
+        hnd3Int32 = Helper.SHInt32(s3.hnd); //get the 32-bit value associated with s3.hnd
+        Console.WriteLine("Testing SHStructParam_Multiple2 (takes a ref struct as one of the params)...");
+        Assert.IsTrue(SHStructParam_Multiple2(s1, ref s3, hnd1Int32, hnd3Int32), "FAILED! SHStructParam_Multiple2 did not receive parameter(s) as expected.");
+        Assert.IsFalse(Helper.IsChanged(s1.hnd) || Helper.IsChanged(s3.hnd), "FAILED! SHStructParam_Multiple2 did not return handles as expected.");
+
+        Console.WriteLine("Testing SHStructParam_Multiple3 (takes a ref struct as one of the params and changes its handle field and so is expected to result in an exception)...");
+        Assert.Throws<NotSupportedException>(() => SHStructParam_Multiple3(s1, ref s3, hnd1Int32, hnd3Int32), "FAILED!  Exception not thrown.");
+    }
+}
\ No newline at end of file
diff --git a/tests/src/Interop/PInvoke/SafeHandles/SigDiff/SigDiffTest.csproj b/tests/src/Interop/PInvoke/SafeHandles/SigDiff/SigDiffTest.csproj
new file mode 100644 (file)
index 0000000..67a5e53
--- /dev/null
@@ -0,0 +1,38 @@
+<?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>SigDiffTest</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>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+  </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="SigDiffTest.cs" />
+    </ItemGroup><Import Project="../../../Interop.settings.targets" /><ItemGroup>
+    <Compile Include="..\*.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\CMakeLists.txt" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/PInvoke/SafeHandles/StructDefs.cs b/tests/src/Interop/PInvoke/SafeHandles/StructDefs.cs
new file mode 100644 (file)
index 0000000..a50f9ec
--- /dev/null
@@ -0,0 +1,331 @@
+// 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 SafeHandlesTests;
+
+#pragma warning disable 618
+namespace SafeHandlesTests{
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructWithSHFld
+    {
+        public SafeFileHandle hnd; //SH subclass field
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructWithBaseSHFld
+    {
+        public SafeHandle hnd; //SH field
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructWithChildSHFld
+    {
+        public ChildSafeFileHandle hnd; //SafeFileHandle subclass field
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructNestedParent
+    {
+        public StructNestedOneDeep snOneDeep;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructNestedOneDeep
+    {
+        public StructWithSHFld s;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructWithSHArrayFld
+    {
+        public SafeFileHandle[] sharr;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructWithManySHFlds
+    {
+        public SafeHandle hnd1;
+        public SafeFileHandle hnd2;
+        public ChildSafeFileHandle hnd3;
+
+        public SafeHandle hnd4;
+        public SafeFileHandle hnd5;
+        public ChildSafeFileHandle hnd6;
+
+        public SafeHandle hnd7;
+        public SafeFileHandle hnd8;
+        public ChildSafeFileHandle hnd9;
+
+        public SafeHandle hnd10;
+        public SafeFileHandle hnd11;
+        public ChildSafeFileHandle hnd12;
+
+        public SafeHandle hnd13;
+        public SafeFileHandle hnd14;
+        public ChildSafeFileHandle hnd15;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructWithObjFld
+    {
+        //the MA attribute indicates that obj is to be marshaled as a VARIANT
+        [MarshalAs(UnmanagedType.Struct)]
+        public Object obj;
+    }
+
+    ////The following Structure definitions are for negative testing purposes
+    ///
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA1
+    {
+        [MarshalAs(UnmanagedType.AnsiBStr)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA2
+    {
+        [MarshalAs(UnmanagedType.AsAny)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA3
+    {
+        [MarshalAs(UnmanagedType.Bool)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA4
+    {
+        [MarshalAs(UnmanagedType.BStr)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA5
+    {
+        [MarshalAs(UnmanagedType.ByValArray)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA6
+    {
+        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA7
+    {
+        [MarshalAs(UnmanagedType.Currency)]
+        public SafeFileHandle hnd;
+    }
+
+    //NOTE: Specified unmanaged type also needs MarshalType or MarshalTypeRef which indicates the custom marshaler
+    //[StructLayout(LayoutKind.Sequential)]
+    //public struct StructMA8
+    //{
+    // [MarshalAs(UnmanagedType.CustomMarshaler)]
+    // public SafeFileHandle hnd;
+    //}
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA9
+    {
+        [MarshalAs(UnmanagedType.Error)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA10
+    {
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA11
+    {
+        [MarshalAs(UnmanagedType.I1)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA12
+    {
+        [MarshalAs(UnmanagedType.I2)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA13
+    {
+        [MarshalAs(UnmanagedType.I4)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA14
+    {
+        [MarshalAs(UnmanagedType.I8)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA15
+    {
+        [MarshalAs(UnmanagedType.IDispatch)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA16
+    {
+        [MarshalAs(UnmanagedType.Interface)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA17
+    {
+        [MarshalAs(UnmanagedType.IUnknown)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA18
+    {
+        [MarshalAs(UnmanagedType.LPArray)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA19
+    {
+        [MarshalAs(UnmanagedType.LPStr)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA20
+    {
+        [MarshalAs(UnmanagedType.LPStruct)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA21
+    {
+        [MarshalAs(UnmanagedType.LPTStr)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA22
+    {
+        [MarshalAs(UnmanagedType.LPWStr)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA23
+    {
+        [MarshalAs(UnmanagedType.R4)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA24
+    {
+        [MarshalAs(UnmanagedType.R8)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA25
+    {
+        [MarshalAs(UnmanagedType.SafeArray)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA26
+    {
+        [MarshalAs(UnmanagedType.Struct)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA27
+    {
+        [MarshalAs(UnmanagedType.SysInt)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA28
+    {
+        [MarshalAs(UnmanagedType.SysUInt)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA29
+    {
+        [MarshalAs(UnmanagedType.TBStr)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA30
+    {
+        [MarshalAs(UnmanagedType.U1)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA31
+    {
+        [MarshalAs(UnmanagedType.U2)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA32
+    {
+        [MarshalAs(UnmanagedType.U4)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA33
+    {
+        [MarshalAs(UnmanagedType.U8)]
+        public SafeFileHandle hnd;
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct StructMA34
+    {
+        [MarshalAs(UnmanagedType.VariantBool)]
+        public SafeFileHandle hnd;
+    }
+
+    //NOTE: This unmanagedtype is not valid for fields
+    //[StructLayout(LayoutKind.Sequential)]
+    //public struct StructMA35
+    //{
+    // [MarshalAs(UnmanagedType.VBByRefStr)]
+    // public SafeFileHandle hnd;
+    //}
+}
+#pragma warning restore 618
+
+
+
diff --git a/tests/src/Interop/PInvoke/SafeHandles/StructDefs.h b/tests/src/Interop/PInvoke/SafeHandles/StructDefs.h
new file mode 100644 (file)
index 0000000..f96b089
--- /dev/null
@@ -0,0 +1,56 @@
+// 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.
+
+struct StructWithSHFld
+{
+    HANDLE hnd;
+};
+
+struct StructNestedOneDeep
+{
+    StructWithSHFld s;
+};
+
+struct StructNestedParent
+{
+    StructNestedOneDeep snOneDeep;
+};
+
+struct StructWithSHArrayFld
+{
+    HANDLE* sharr;
+};
+
+struct StructWithManySHFlds
+{
+    HANDLE hnd1;
+    HANDLE hnd2;
+    HANDLE hnd3;
+
+    HANDLE hnd4;
+    HANDLE hnd5;
+    HANDLE hnd6;
+
+    HANDLE hnd7;
+    HANDLE hnd8;
+    HANDLE hnd9;
+
+    HANDLE hnd10;
+    HANDLE hnd11;
+    HANDLE hnd12;
+
+    HANDLE hnd13;
+    HANDLE hnd14;
+    HANDLE hnd15;
+};
+
+struct StructMAIntf
+{
+    IDispatch* ptoIntf;
+};
+
+struct StructWithVARIANTFld
+{
+    VARIANT v;
+};
\ No newline at end of file