add_subdirectory(PInvoke/Miscellaneous/HandleRef)
add_subdirectory(PInvoke/Miscellaneous/MultipleAssembliesWithSamePInvoke)
add_subdirectory(PInvoke/ExactSpelling)
+add_subdirectory(PInvoke/CriticalHandles)
add_subdirectory(PInvoke/AsAny)
add_subdirectory(NativeCallable)
add_subdirectory(PrimitiveMarshalling/Bool)
--- /dev/null
+// 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 TestLibrary;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+internal class MyCriticalHandle : CriticalHandle
+{
+ static int s_uniqueHandleValue;
+ static HashSet<int> s_closedHandles = new HashSet<int>();
+
+ public MyCriticalHandle() : base(new IntPtr(-1))
+ {
+
+ }
+
+ public override bool IsInvalid
+ {
+ get { return false; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ if (!s_closedHandles.Contains(handle.ToInt32()))
+ {
+ s_closedHandles.Add(handle.ToInt32());
+ return true;
+ }
+
+ return false;
+ }
+
+ internal IntPtr Handle
+ {
+ get
+ {
+ return handle;
+ }
+ set
+ {
+ handle = value;
+ }
+ }
+
+ internal static IntPtr GetUniqueHandle()
+ {
+ return new IntPtr(s_uniqueHandleValue++);
+ }
+
+ internal static bool IsHandleClosed(IntPtr handle)
+ {
+ return s_closedHandles.Contains(handle.ToInt32());
+ }
+}
+
+internal class Native
+{
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ [return: MarshalAs(UnmanagedType.Bool)]internal delegate bool IsHandleClosed(IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr In([MarshalAs(UnmanagedType.LPArray)]MyCriticalHandle[] handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void Out(IntPtr handleValue, [MarshalAs(UnmanagedType.LPArray)]out MyCriticalHandle[] handle);
+
+ [DllImport("CriticalHandlesNative", EntryPoint = "Ref", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr InRef([In, MarshalAs(UnmanagedType.LPArray)]ref MyCriticalHandle[] handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr Ref([MarshalAs(UnmanagedType.LPArray)]ref MyCriticalHandle[] handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr RefModify(IntPtr handleValue, [MarshalAs(UnmanagedType.LPArray)]ref MyCriticalHandle[] handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern MyCriticalHandle[] Ret(IntPtr handleValue);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr SetIsHandleClosedCallback([MarshalAs(UnmanagedType.FunctionPtr)]IsHandleClosed isHandleClosed);
+}
+
+public class CriticalHandleArrayTest
+{
+ private static Native.IsHandleClosed s_isHandleClose = (handleValue) =>
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ return MyCriticalHandle.IsHandleClosed(handleValue);
+ };
+
+ public static void In()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ MyCriticalHandle[] myCriticalHandleArray = new MyCriticalHandle[] { new MyCriticalHandle() { Handle = handleValue } };
+ Assert.Throws<MarshalDirectiveException>(() => Native.In(myCriticalHandleArray));
+ }
+
+ public static void Ret()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ Assert.Throws<MarshalDirectiveException>(() => Native.Ret(handleValue));
+ }
+
+ public static void Out()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ MyCriticalHandle[] myCriticalHandleArray;
+ Assert.Throws<MarshalDirectiveException>(() => Native.Out(handleValue, out myCriticalHandleArray));
+ }
+
+ public static void InRef()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ MyCriticalHandle[] myCriticalHandleArray = new MyCriticalHandle[] { new MyCriticalHandle() { Handle = handleValue } };
+ Assert.Throws<MarshalDirectiveException>(() => Native.InRef(ref myCriticalHandleArray));
+ }
+
+ public static void Ref()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ MyCriticalHandle[] myCriticalHandleArray = new MyCriticalHandle[] { new MyCriticalHandle() { Handle = handleValue } };
+ Assert.Throws<MarshalDirectiveException>(() => Native.Ref(ref myCriticalHandleArray));
+ }
+
+ public static void RefModify()
+ {
+ IntPtr handleValue1 = MyCriticalHandle.GetUniqueHandle();
+ IntPtr handleValue2 = MyCriticalHandle.GetUniqueHandle();
+ MyCriticalHandle[] myCriticalHandleArray = new MyCriticalHandle[] { new MyCriticalHandle() { Handle = handleValue1 } };
+ Assert.Throws<MarshalDirectiveException>(() => Native.RefModify(handleValue2, ref myCriticalHandleArray));
+ }
+
+ public static int Main(string[] args)
+ {
+ try
+ {
+ In();
+ Ret();
+ Out();
+ InRef();
+ Ref();
+ RefModify();
+
+ return 100;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+ }
+}
--- /dev/null
+<?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>ArrayTest</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ArrayTest.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>
--- /dev/null
+#VCXPROJ
+cmake_minimum_required (VERSION 2.6)
+project (CriticalHandlesNative)
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+include_directories(${INC_PLATFORM_DIR})
+set(SOURCES
+ CriticalHandlesNative.cpp
+)
+# add the executable
+add_library (CriticalHandlesNative SHARED ${SOURCES})
+target_link_libraries(CriticalHandlesNative ${LINK_LIBRARIES_ADDITIONAL})
+# add the install targets
+install (TARGETS CriticalHandlesNative DESTINATION bin)
--- /dev/null
+// 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 <xplatform.h>
+
+typedef BOOL(__stdcall *HandleCallback)(void*);
+
+extern "C" DLL_EXPORT size_t __stdcall In(void* handle, HandleCallback handleCallback)
+{
+ if (handleCallback != nullptr && !handleCallback(handle))
+ {
+ return (size_t)(-1);
+ }
+
+ return reinterpret_cast<size_t>(handle);
+}
+
+extern "C" DLL_EXPORT void* __stdcall Ret(void* handleValue)
+{
+ return handleValue;
+}
+
+extern "C" DLL_EXPORT void __stdcall Out(void* handleValue, void** pHandle)
+{
+ if (pHandle == nullptr)
+ {
+ return;
+ }
+
+ *pHandle = handleValue;
+}
+
+extern "C" DLL_EXPORT size_t __stdcall Ref(void** pHandle, HandleCallback handleCallback)
+{
+ if (handleCallback != nullptr && !handleCallback(*pHandle))
+ {
+ return (size_t)(-1);
+ }
+
+ return reinterpret_cast<size_t>(*pHandle);
+}
+
+extern "C" DLL_EXPORT size_t __stdcall RefModify(void* handleValue, void** pHandle, HandleCallback handleCallback)
+{
+ if (handleCallback != nullptr && !handleCallback(*pHandle))
+ {
+ return (size_t)(-1);
+ }
+
+ void* originalHandle = *pHandle;
+
+ *pHandle = handleValue;
+
+ return reinterpret_cast<size_t>(originalHandle);
+}
+
+typedef void(__stdcall *InCallback)(void*);
+
+extern "C" DLL_EXPORT void __stdcall InvokeInCallback(InCallback callback, void* handle)
+{
+ callback(handle);
+}
+
+typedef void(__stdcall *RefCallback)(void**);
+
+extern "C" DLL_EXPORT void __stdcall InvokeRefCallback(RefCallback callback, void** pHandle)
+{
+ callback(pHandle);
+}
+
+typedef void*(__stdcall *RetCallback)();
+
+extern "C" DLL_EXPORT void* __stdcall InvokeRetCallback(RetCallback callback)
+{
+ return callback();
+}
--- /dev/null
+// 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 TestLibrary;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+internal class MyCriticalHandle : CriticalHandle
+{
+ static int s_uniqueHandleValue;
+ static HashSet<int> s_closedHandles = new HashSet<int>();
+
+ public MyCriticalHandle() : base(new IntPtr(-1))
+ {
+
+ }
+
+ public override bool IsInvalid
+ {
+ get { return false; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ if (!s_closedHandles.Contains(handle.ToInt32()))
+ {
+ s_closedHandles.Add(handle.ToInt32());
+ return true;
+ }
+
+ return false;
+ }
+
+ internal IntPtr Handle
+ {
+ get
+ {
+ return handle;
+ }
+ set
+ {
+ handle = value;
+ }
+ }
+
+ internal static IntPtr GetUniqueHandle()
+ {
+ return new IntPtr(s_uniqueHandleValue++);
+ }
+
+ internal static bool IsHandleClosed(IntPtr handle)
+ {
+ return s_closedHandles.Contains(handle.ToInt32());
+ }
+}
+
+public class Reverse
+{
+ public static void In()
+ {
+ IntPtr handleValue = new IntPtr(1);
+ Native.InCallback callback = (handle) => { };
+ Assert.Throws<MarshalDirectiveException>(() => Native.InvokeInCallback(callback, handleValue), "Calling P/Invoke that invokes a delegate that has an CriticalHandle parameter");
+ GC.KeepAlive(callback);
+ }
+
+ public static void Ret()
+ {
+ IntPtr handleValue = new IntPtr(2);
+ Native.RetCallback callback = () => new MyCriticalHandle();
+ Assert.Throws<MarshalDirectiveException>(() => Native.InvokeRetCallback(callback), "Calling P/Invoke that invokes a delegate that returns a CriticalHandle parameter");
+ GC.KeepAlive(callback);
+ }
+
+ public static void Out()
+ {
+ IntPtr handleValue = new IntPtr(3);
+ Native.OutCallback callback = (out MyCriticalHandle handle) => handle = null;
+ Assert.Throws<MarshalDirectiveException>(() => Native.InvokeOutCallback(callback, ref handleValue), "Calling P/Invoke that invokes a delegate that has an out CriticalHandle parameter");
+ GC.KeepAlive(callback);
+ }
+
+ public static void InRef()
+ {
+ IntPtr handleValue = new IntPtr(4);
+ Native.InRefCallback callback = (ref MyCriticalHandle handle) => { };
+ Assert.Throws<MarshalDirectiveException>(() => Native.InvokeInRefCallback(callback, ref handleValue), "Calling P/Invoke that invokes a delegate that has an [In] ref CriticalHandle parameter");
+ GC.KeepAlive(callback);
+ }
+
+ public static void Ref()
+ {
+ IntPtr handleValue = new IntPtr(5);
+ Native.RefCallback callback = (ref MyCriticalHandle handle) => { };
+ Assert.Throws<MarshalDirectiveException>(() => Native.InvokeRefCallback(callback, ref handleValue), "Calling P/Invoke that invokes a delegate that has an ref CriticalHandle parameter");
+ GC.KeepAlive(callback);
+ }
+
+ internal class Native
+ {
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ internal delegate void InCallback(MyCriticalHandle handle);
+
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ internal delegate void OutCallback(out MyCriticalHandle handle);
+
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ internal delegate void InRefCallback([In]ref MyCriticalHandle handle);
+
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ internal delegate void RefCallback(ref MyCriticalHandle handle);
+
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ internal delegate MyCriticalHandle RetCallback();
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void InvokeInCallback(InCallback callback, IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", EntryPoint = "InvokeRefCallback", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void InvokeOutCallback(OutCallback callback, ref IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", EntryPoint = "InvokeRefCallback", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void InvokeInRefCallback(InRefCallback callback, ref IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void InvokeRefCallback(RefCallback callback, ref IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr InvokeRetCallback(RetCallback callback);
+ }
+
+ public static int Main(string[] args)
+ {
+ try
+ {
+ In();
+ Ret();
+ Out();
+ InRef();
+ Ref();
+
+ return 100;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+ }
+}
--- /dev/null
+<?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>ReverseTest</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ReverseTest.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>
--- /dev/null
+// 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 TestLibrary;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+internal class MyCriticalHandle : CriticalHandle
+{
+ static int s_uniqueHandleValue;
+ static HashSet<int> s_closedHandles = new HashSet<int>();
+
+ public MyCriticalHandle() : base(new IntPtr(-1))
+ {
+
+ }
+
+ public override bool IsInvalid
+ {
+ get { return false; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ if (!s_closedHandles.Contains(handle.ToInt32()))
+ {
+ s_closedHandles.Add(handle.ToInt32());
+ return true;
+ }
+
+ return false;
+ }
+
+ internal IntPtr Handle
+ {
+ get
+ {
+ return handle;
+ }
+ set
+ {
+ handle = value;
+ }
+ }
+
+ internal static IntPtr GetUniqueHandle()
+ {
+ return new IntPtr(s_uniqueHandleValue++);
+ }
+
+ internal static bool IsHandleClosed(IntPtr handle)
+ {
+ return s_closedHandles.Contains(handle.ToInt32());
+ }
+}
+
+public class CriticalHandleStructTest
+{
+ private static Native.HandleCallback s_handleCallback = (handleValue) =>
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ return !MyCriticalHandle.IsHandleClosed(handleValue);
+ };
+
+ public static void In()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ InWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void InWorker(IntPtr handleValue)
+ {
+ Native.MyCriticalHandleStruct handleStruct = new Native.MyCriticalHandleStruct() { Handle = new MyCriticalHandle() { Handle = handleValue } };
+ IntPtr value;
+ value = Native.In(handleStruct, s_handleCallback);
+ Assert.AreEqual(handleValue.ToInt32(), value.ToInt32(), "Handle value");
+ }
+
+ public static void Ret()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ Assert.Throws<MarshalDirectiveException>(() => Native.Ret(handleValue));
+ }
+
+ public static void Out()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ Native.MyCriticalHandleStruct handleStruct;
+ Assert.Throws<NotSupportedException>(() => Native.Out(handleValue, out handleStruct));
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OutWorker(IntPtr handleValue)
+ {
+ Native.MyCriticalHandleStruct handleStruct;
+ Native.Out(handleValue, out handleStruct);
+ Assert.AreEqual(handleValue.ToInt32(), handleStruct.Handle.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void InRef()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ InRefWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void InRefWorker(IntPtr handleValue)
+ {
+ Native.MyCriticalHandleStruct handleStruct = new Native.MyCriticalHandleStruct() { Handle = new MyCriticalHandle() { Handle = handleValue } };
+ Native.InRef(ref handleStruct, s_handleCallback);
+ Assert.AreEqual(handleValue.ToInt32(), handleStruct.Handle.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void Ref()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ RefWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void RefWorker(IntPtr handleValue)
+ {
+ Native.MyCriticalHandleStruct handleStruct = new Native.MyCriticalHandleStruct() { Handle = new MyCriticalHandle() { Handle = handleValue } };
+ Native.Ref(ref handleStruct, s_handleCallback);
+ Assert.AreEqual(handleValue.ToInt32(), handleStruct.Handle.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void RefModify()
+ {
+ IntPtr handleValue1 = MyCriticalHandle.GetUniqueHandle();
+ IntPtr handleValue2 = MyCriticalHandle.GetUniqueHandle();
+ Native.MyCriticalHandleStruct handleStruct = new Native.MyCriticalHandleStruct() { Handle = new MyCriticalHandle() { Handle = handleValue1 } };
+
+ Assert.Throws<NotSupportedException>(() => Native.RefModify(handleValue2, ref handleStruct, null));
+ }
+
+ internal class Native
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct MyCriticalHandleStruct
+ {
+ internal MyCriticalHandle Handle;
+ }
+
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal delegate bool HandleCallback(IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr In(MyCriticalHandleStruct handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void Out(IntPtr handleValue, out MyCriticalHandleStruct handle);
+
+ [DllImport("CriticalHandlesNative", EntryPoint = "Ref", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr InRef([In]ref MyCriticalHandleStruct handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr Ref(ref MyCriticalHandleStruct handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr RefModify(IntPtr handleValue, ref MyCriticalHandleStruct handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern MyCriticalHandleStruct Ret(IntPtr handleValue);
+ }
+
+ public static int Main(string[] args)
+ {
+ try
+ {
+ In();
+ Ret();
+ Out();
+ InRef();
+ Ref();
+ RefModify();
+
+ return 100;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+ }
+}
--- /dev/null
+<?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>StructTest</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="StructTest.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>
--- /dev/null
+// 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 TestLibrary;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+internal class MyCriticalHandle : CriticalHandle
+{
+ static int s_uniqueHandleValue;
+ static HashSet<int> s_closedHandles = new HashSet<int>();
+
+ public MyCriticalHandle() : base(new IntPtr(-1))
+ {
+
+ }
+
+ public override bool IsInvalid
+ {
+ get { return false; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ if (!s_closedHandles.Contains(handle.ToInt32()))
+ {
+ s_closedHandles.Add(handle.ToInt32());
+ return true;
+ }
+
+ return false;
+ }
+
+ internal IntPtr Handle
+ {
+ get
+ {
+ return handle;
+ }
+ set
+ {
+ handle = value;
+ }
+ }
+
+ internal static IntPtr GetUniqueHandle()
+ {
+ return new IntPtr(s_uniqueHandleValue++);
+ }
+
+ internal static bool IsHandleClosed(IntPtr handle)
+ {
+ return s_closedHandles.Contains(handle.ToInt32());
+ }
+}
+
+public abstract class AbstractCriticalHandle : CriticalHandle
+{
+ public AbstractCriticalHandle() : base(new IntPtr(-1))
+ {
+
+ }
+
+ internal IntPtr Handle
+ {
+ get
+ {
+ return handle;
+ }
+ }
+}
+
+public class CriticalHandleWithNoDefaultCtor : AbstractCriticalHandle
+{
+ public CriticalHandleWithNoDefaultCtor(IntPtr handle)
+ {
+ this.handle = handle;
+ }
+
+ public override bool IsInvalid
+ {
+ get { return false; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ return true;
+ }
+}
+
+public class CriticalHandleTest
+{
+ private static Native.HandleCallback s_handleCallback = (handleValue) =>
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ return !MyCriticalHandle.IsHandleClosed(handleValue);
+ };
+
+ public static void In()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ InWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void InWorker(IntPtr handleValue)
+ {
+ MyCriticalHandle hande = new MyCriticalHandle() { Handle = handleValue };
+ IntPtr value;
+ value = Native.In(hande, s_handleCallback);
+ Assert.AreEqual(handleValue.ToInt32(), value.ToInt32(), "Handle value");
+ }
+
+ public static void Ret()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ RetWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void RetWorker(IntPtr handleValue)
+ {
+ MyCriticalHandle hande = Native.Ret(handleValue);
+ Assert.AreEqual(handleValue.ToInt32(), hande.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void Out()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ OutWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OutWorker(IntPtr handleValue)
+ {
+ MyCriticalHandle hande;
+ Native.Out(handleValue, out hande);
+ Assert.AreEqual(handleValue.ToInt32(), hande.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void InRef()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ InRefWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void InRefWorker(IntPtr handleValue)
+ {
+ MyCriticalHandle hande = new MyCriticalHandle() { Handle = handleValue };
+ Native.InRef(ref hande, s_handleCallback);
+ Assert.AreEqual(handleValue.ToInt32(), hande.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void Ref()
+ {
+ IntPtr handleValue = MyCriticalHandle.GetUniqueHandle();
+ RefWorker(handleValue);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue), "Handle was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void RefWorker(IntPtr handleValue)
+ {
+ MyCriticalHandle hande = new MyCriticalHandle() { Handle = handleValue };
+ Native.Ref(ref hande, s_handleCallback);
+ Assert.AreEqual(handleValue.ToInt32(), hande.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void RefModify()
+ {
+ IntPtr handleValue1 = MyCriticalHandle.GetUniqueHandle();
+ IntPtr handleValue2 = MyCriticalHandle.GetUniqueHandle();
+ RefModifyWorker(handleValue1, handleValue2);
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue1), "Handle 1 was not closed");
+ Assert.IsTrue(MyCriticalHandle.IsHandleClosed(handleValue2), "Handle 2 was not closed");
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void RefModifyWorker(IntPtr handleValue1, IntPtr handleValue2)
+ {
+ MyCriticalHandle hande = new MyCriticalHandle() { Handle = handleValue1 };
+ Native.RefModify(handleValue2, ref hande, s_handleCallback);
+ Assert.AreEqual(handleValue2.ToInt32(), hande.Handle.ToInt32(), "Handle value");
+ }
+
+ internal class Native
+ {
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal delegate bool HandleCallback(IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr In(MyCriticalHandle handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void Out(IntPtr handleValue, out MyCriticalHandle handle);
+
+ [DllImport("CriticalHandlesNative", EntryPoint = "Ref", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr InRef([In]ref MyCriticalHandle handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr Ref(ref MyCriticalHandle handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr RefModify(IntPtr handleValue, ref MyCriticalHandle handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern MyCriticalHandle Ret(IntPtr handleValue);
+ }
+}
+
+public class AbstractCriticalHandleTest
+{
+ public static void In()
+ {
+ IntPtr handleValue = new IntPtr(1);
+ AbstractCriticalHandle handle = new CriticalHandleWithNoDefaultCtor(handleValue);
+ IntPtr value;
+ value = Native.In(handle, null);
+ Assert.AreEqual(handleValue.ToInt32(), value.ToInt32(), "Handle value");
+ }
+
+ public static void Ret()
+ {
+ IntPtr handleValue = new IntPtr(2);
+ Assert.Throws<MarshalDirectiveException>(() => Native.Ret(handleValue), "Calling P/Invoke that returns an abstract critical handle");
+ }
+
+ public static void Out()
+ {
+ IntPtr handleValue = new IntPtr(3);
+ AbstractCriticalHandle handle;
+ Assert.Throws<MarshalDirectiveException>(() => Native.Out(handleValue, out handle), "Calling P/Invoke that has an out abstract critical handle parameter");
+ }
+
+ public static void InRef()
+ {
+ IntPtr handleValue = new IntPtr(4);
+ AbstractCriticalHandle handle = new CriticalHandleWithNoDefaultCtor(handleValue);
+ Native.InRef(ref handle, null);
+ Assert.AreEqual(handleValue.ToInt32(), handle.Handle.ToInt32(), "Handle value");
+ }
+
+ public static void Ref()
+ {
+ IntPtr handleValue = new IntPtr(5);
+ AbstractCriticalHandle handle = new CriticalHandleWithNoDefaultCtor(handleValue);
+ Assert.Throws<MarshalDirectiveException>(() => Native.Ref(ref handle, null), "Calling P/Invoke that has a ref abstract critical handle parameter");
+ }
+
+ internal class Native
+ {
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal delegate bool HandleCallback(IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr In(AbstractCriticalHandle handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void Out(IntPtr handleValue, out AbstractCriticalHandle handle);
+
+ [DllImport("CriticalHandlesNative", EntryPoint = "Ref", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr InRef([In]ref AbstractCriticalHandle handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr Ref(ref AbstractCriticalHandle handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern AbstractCriticalHandle Ret(IntPtr handleValue);
+ }
+}
+
+public class NoDefaultCtorCriticalHandleTest
+{
+ public static void In()
+ {
+ IntPtr handleValue = new IntPtr(1);
+ CriticalHandleWithNoDefaultCtor handle = new CriticalHandleWithNoDefaultCtor(handleValue);
+ IntPtr value;
+ value = Native.In(handle, null);
+ Assert.AreEqual(handleValue.ToInt32(), value.ToInt32(), "Handle value");
+ }
+
+ public static void Ret()
+ {
+ IntPtr handleValue = new IntPtr(2);
+ //TODO: Expected MissingMemberException but throws MissingMethodException
+ Assert.Throws<MissingMethodException>(() => Native.Ret(handleValue), "Calling P/Invoke that returns an no default ctor critical handle");
+ }
+
+ public static void Out()
+ {
+ IntPtr handleValue = new IntPtr(3);
+ CriticalHandleWithNoDefaultCtor handle;
+ //TODO: Expected MissingMemberException but throws MissingMethodException
+ Assert.Throws<MissingMethodException>(() => Native.Out(handleValue, out handle), "Calling P/Invoke that has an out no default ctor critical handle parameter");
+ }
+
+ public static void InRef()
+ {
+ IntPtr handleValue = new IntPtr(4);
+ CriticalHandleWithNoDefaultCtor handle = new CriticalHandleWithNoDefaultCtor(handleValue);
+ //TODO: Expected MissingMemberException but throws MissingMethodException
+ Assert.Throws<MissingMethodException>(() => Native.InRef(ref handle, null), "Calling P/Invoke that has a [In] ref no default ctor critical handle parameter");
+ }
+
+ public static void Ref()
+ {
+ IntPtr handleValue = new IntPtr(5);
+ CriticalHandleWithNoDefaultCtor handle = new CriticalHandleWithNoDefaultCtor(handleValue);
+ //TODO: Expected MissingMemberException but throws MissingMethodException
+ Assert.Throws<MissingMethodException>(() => Native.Ref(ref handle, null), "Calling P/Invoke that has a ref no default ctor critical handle parameter");
+ }
+
+ internal class Native
+ {
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal delegate bool HandleCallback(IntPtr handle);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr In(CriticalHandleWithNoDefaultCtor handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern void Out(IntPtr handleValue, out CriticalHandleWithNoDefaultCtor handle);
+
+ [DllImport("CriticalHandlesNative", EntryPoint = "Ref", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr InRef([In]ref CriticalHandleWithNoDefaultCtor handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern IntPtr Ref(ref CriticalHandleWithNoDefaultCtor handle, HandleCallback handleCallback);
+
+ [DllImport("CriticalHandlesNative", CallingConvention = CallingConvention.StdCall)]
+ internal static extern CriticalHandleWithNoDefaultCtor Ret(IntPtr handleValue);
+ }
+}
+
+public class Test
+{
+ public static int Main(string[] args)
+ {
+ try
+ {
+ CriticalHandleTest.In();
+ CriticalHandleTest.Ret();
+ CriticalHandleTest.Out();
+ CriticalHandleTest.InRef();
+ CriticalHandleTest.Ref();
+ CriticalHandleTest.RefModify();
+
+ AbstractCriticalHandleTest.In();
+ AbstractCriticalHandleTest.Ret();
+ AbstractCriticalHandleTest.Out();
+ AbstractCriticalHandleTest.InRef();
+ AbstractCriticalHandleTest.Ref();
+
+ NoDefaultCtorCriticalHandleTest.In();
+ NoDefaultCtorCriticalHandleTest.Ret();
+ NoDefaultCtorCriticalHandleTest.Out();
+ NoDefaultCtorCriticalHandleTest.InRef();
+ NoDefaultCtorCriticalHandleTest.Ref();
+
+ return 100;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+ }
+}
--- /dev/null
+<?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>Test</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Test.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>