From b4650943618bfd06296c61f8f46bddce9b851015 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 14 Jan 2019 15:49:45 -0800 Subject: Custom Marshalers in custom-ALC-loaded assemblies results in types loaded from crossing ALCs (#21606) * Create repro for dotnet/coreclr#19654 * Update ICustomMarshaler.csproj * Update ICustomMarshaler.csproj * Clean up repro per feedback. * Add test case for different assemblies with the same CustomMarshaler name. * Move EEMarshalingData cache from AppDomain to LoaderAllocator. This fixes the custom-marshaler conflict when using unloadable assembly contexts. * Internalize the LoaderHeap* parameter. * Add the pointer to the requesting assembly to the hashtable key. * Fix linux-musl build break. * Move Crst out of FEATURE_COMINTEROP block. * Make sure to copy over the assembly pointer to the key that's actually stored in the hash table. * Add comment for m_invokingAssembly. * Move all usages of EEMarshallingData to hang off the correct loader allocator instead of always the global one. * Change to m_InteropDataCrst since this EEMarshallingData can be used in preemptive GC mode. * Always init m_InteropDataCrst (since it's used by EEMarshallingData as well as COM). * PR Feedback. * Remove extraneous inlines. --- tests/src/Interop/CMakeLists.txt | 1 + .../ConflictingNames/CMakeLists.txt | 11 + .../ConflictingNames/CustomMarshaler.cs | 54 ++ .../ConflictingNames/CustomMarshaler.csproj | 28 + .../ConflictingNames/CustomMarshaler2.csproj | 29 + .../ConflictingNames/CustomMarshalerNative.cpp | 12 + .../ConflictingNames/MultipleALCs.csproj | 35 ++ .../ICustomMarshaler/ConflictingNames/RunInALC.cs | 72 +++ .../ConflictingNames/SameNameDifferentAssembly.cs | 28 + .../SameNameDifferentAssembly.csproj | 35 ++ .../Interop/ICustomMarshaler/ICustomMarshaler.cs | 597 --------------------- .../ICustomMarshaler/ICustomMarshaler.csproj | 34 -- .../Primitives/ICustomMarshaler.cs | 597 +++++++++++++++++++++ .../Primitives/ICustomMarshaler.csproj | 31 ++ 14 files changed, 933 insertions(+), 631 deletions(-) create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/CMakeLists.txt create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.cs create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.csproj create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler2.csproj create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshalerNative.cpp create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/MultipleALCs.csproj create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/RunInALC.cs create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.cs create mode 100644 tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.csproj delete mode 100644 tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs delete mode 100644 tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj create mode 100644 tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs create mode 100644 tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.csproj (limited to 'tests') diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt index e46e78a8ff..9b67844e90 100644 --- a/tests/src/Interop/CMakeLists.txt +++ b/tests/src/Interop/CMakeLists.txt @@ -62,6 +62,7 @@ add_subdirectory(DllImportAttribute/ExeFile) add_subdirectory(DllImportAttribute/FileNameContainDot) add_subdirectory(DllImportAttribute/Simple) add_subdirectory(ExecInDefAppDom) +add_subdirectory(ICustomMarshaler/ConflictingNames) add_subdirectory(LayoutClass) if(WIN32) diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/CMakeLists.txt b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CMakeLists.txt new file mode 100644 index 0000000000..b1743b761b --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required (VERSION 2.6) +project (CustomMarshalersConflictingNames) +include_directories(${INC_PLATFORM_DIR}) +set(SOURCES CustomMarshalerNative.cpp ) + +# add the executable +add_library (CustomMarshalerNative SHARED ${SOURCES}) +target_link_libraries(CustomMarshalerNative ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS CustomMarshalerNative DESTINATION bin) diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.cs b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.cs new file mode 100644 index 0000000000..506f8b2181 --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.cs @@ -0,0 +1,54 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +class WrappedString +{ + public WrappedString(string str) + { + _str = str; + } + + internal string _str; +} + +class WrappedStringCustomMarshaler : ICustomMarshaler +{ + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi(((WrappedString)ManagedObj)._str); + public object MarshalNativeToManaged(IntPtr pNativeData) => new WrappedString(Marshal.PtrToStringAnsi(pNativeData)); + + public static ICustomMarshaler GetInstance(string cookie) => new WrappedStringCustomMarshaler(); +} + +// Use an ifdef here to give us two separate public API surfaces to call while allowing us to have the same implementation code +// as well as allowing us to share the custom marshaler implementations above. +// If we wanted to add more tests here, we would want to put the public API surface in the namespace and the private +// details and marshalers in the global scope as done above. +#if CUSTOMMARSHALERS2 +namespace CustomMarshalers2 +#else +namespace CustomMarshalers +#endif +{ + public class CustomMarshalerTest + { + [DllImport("CustomMarshalerNative", CharSet = CharSet.Ansi)] + private static extern int NativeParseInt([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(WrappedStringCustomMarshaler))] WrappedString str); + + public int ParseInt(string str) + { + return NativeParseInt(new WrappedString(str)); + } + } +} + diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.csproj b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.csproj new file mode 100644 index 0000000000..e04f27f049 --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler.csproj @@ -0,0 +1,28 @@ + + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + library + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + + + + + + + False + + + + + + + + + diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler2.csproj b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler2.csproj new file mode 100644 index 0000000000..38c0cbacd2 --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshaler2.csproj @@ -0,0 +1,29 @@ + + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + library + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + $(DefineConstants);CUSTOMMARSHALERS2 + + + + + + + False + + + + + + + + + diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshalerNative.cpp b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshalerNative.cpp new file mode 100644 index 0000000000..11aee8008a --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/CustomMarshalerNative.cpp @@ -0,0 +1,12 @@ +// 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 +#include + + +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseInt(LPCSTR str) +{ + return atoi(str); +} diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/MultipleALCs.csproj b/tests/src/Interop/ICustomMarshaler/ConflictingNames/MultipleALCs.csproj new file mode 100644 index 0000000000..045a2b69dc --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/MultipleALCs.csproj @@ -0,0 +1,35 @@ + + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + + + + + + + + + + + + diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/RunInALC.cs b/tests/src/Interop/ICustomMarshaler/ConflictingNames/RunInALC.cs new file mode 100644 index 0000000000..1d61f2deaf --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/RunInALC.cs @@ -0,0 +1,72 @@ +// 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.Linq; +using System.Reflection; +using System.Runtime.Loader; +using TestLibrary; + +public class RunInALC +{ + public static int Main(string[] args) + { + try + { + ConflictingCustomMarshalerNamesInCollectibleLoadContexts_Succeeds(); + ConflictingCustomMarshalerNamesInNoncollectibleLoadContexts_Succeeds(); + } + catch (System.Exception e) + { + Console.WriteLine(e.ToString()); + return 101; + } + return 100; + } + + static void ConflictingCustomMarshalerNamesInCollectibleLoadContexts_Succeeds() + { + Run(new UnloadableLoadContext()); + Run(new UnloadableLoadContext()); + } + + static void ConflictingCustomMarshalerNamesInNoncollectibleLoadContexts_Succeeds() + { + Run(new CustomLoadContext()); + Run(new CustomLoadContext()); + } + + static void Run(AssemblyLoadContext context) + { + string currentAssemblyDirectory = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + Assembly inContextAssembly = context.LoadFromAssemblyPath(Path.Combine(currentAssemblyDirectory, "CustomMarshaler.dll")); + Type inContextType = inContextAssembly.GetType("CustomMarshalers.CustomMarshalerTest"); + object instance = Activator.CreateInstance(inContextType); + MethodInfo parseIntMethod = inContextType.GetMethod("ParseInt", BindingFlags.Instance | BindingFlags.Public); + Assert.AreEqual(1234, (int)parseIntMethod.Invoke(instance, new object[]{"1234"})); + } +} + +class UnloadableLoadContext : AssemblyLoadContext +{ + public UnloadableLoadContext() + :base(true) + { + + } + + protected override Assembly Load(AssemblyName assemblyName) + { + return null; + } +} + +class CustomLoadContext : AssemblyLoadContext +{ + protected override Assembly Load(AssemblyName assemblyName) + { + return null; + } +} diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.cs b/tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.cs new file mode 100644 index 0000000000..ce9113438c --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.cs @@ -0,0 +1,28 @@ +// 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.Linq; +using System.Reflection; +using System.Runtime.Loader; +using TestLibrary; + +public class RunInALC +{ + public static int Main(string[] args) + { + try + { + Assert.AreEqual(123, new CustomMarshalers.CustomMarshalerTest().ParseInt("123")); + Assert.AreEqual(123, new CustomMarshalers2.CustomMarshalerTest().ParseInt("123")); + return 100; + } + catch (Exception e) + { + Console.WriteLine(e); + return 101; + } + } +} diff --git a/tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.csproj b/tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.csproj new file mode 100644 index 0000000000..6b1f7df357 --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly.csproj @@ -0,0 +1,35 @@ + + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + + + + + + + False + + + + + + + + + + + + + + + diff --git a/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs b/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs deleted file mode 100644 index b07b8109cc..0000000000 --- a/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.cs +++ /dev/null @@ -1,597 +0,0 @@ -// 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.Collections.Generic; -using System.Linq; -using Xunit; - -namespace System.Runtime.InteropServices.Tests -{ - public class ICustomMarshalerTests : XunitBase - { - // To avoid having to create a native test library to reference in tests that - // interact with native libraries, we can use a simple method from the C standard - // library. Unfortunately, the C standard library has different names on Windows - // vs Unix. -#if Windows - public const string LibcLibrary = "msvcrt.dll"; -#else - public const string LibcLibrary = "libc"; -#endif - - [Fact] - public void CustomMarshaler_StringType_Success() - { - int val = 64001; - Assert.Equal(val, MarshalerOnStringTypeMethod(val.ToString())); - } - - public class StringForwardingCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => new StringForwardingCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi")] - public static extern int MarshalerOnStringTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] string str); - - [Fact] - public void CustomMarshaler_ArrayType_Success() - { - int val = 64001; - Assert.Equal(val, MarshalerOnArrayTypeMethod(new string[] { val.ToString() })); - } - - public class ArrayForwardingCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi(((string[])ManagedObj)[0]); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => new ArrayForwardingCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi")] - public static extern int MarshalerOnArrayTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "System.Runtime.InteropServices.Tests.ICustomMarshalerTests+ArrayForwardingCustomMarshaler")] string[] str); - - [Fact] - public void CustomMarshaler_BoxedValueType_Success() - { - int val = 64001; - Assert.Equal(val * 2, MarshalerOnBoxedValueTypeMethod(val)); - } - - public class BoxedValueTypeCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) - { - int unboxedValueType = (int)ManagedObj * 2; - return Marshal.StringToCoTaskMemAnsi(unboxedValueType.ToString()); - } - - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => new BoxedValueTypeCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi")] - public static extern int MarshalerOnBoxedValueTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] object i); - - [Fact] - public void Parameter_CustomMarshalerProvidedOnClassType_ForwardsCorrectly() - { - int val = 64001; - Assert.Equal((val * 2).ToString(), MarshalerOnClassTypeMethod(new StringContainer { Value = val.ToString() }).Value); - } - - public class StringContainer - { - public string Value { get; set; } - } - - public class ClassForwardingCustomMarshaler : ICustomMarshaler - { - private bool CleanedString { get; set; } - - public void CleanUpManagedData(object ManagedObj) {} - - public void CleanUpNativeData(IntPtr pNativeData) - { - if (CleanedString) - { - return; - } - - Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); - CleanedString = true; - } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) - { - return Marshal.StringToCoTaskMemAnsi(((StringContainer)ManagedObj).Value); - } - - public object MarshalNativeToManaged(IntPtr pNativeData) - { - int doubleValue = pNativeData.ToInt32() * 2; - return new StringContainer { Value = doubleValue.ToString() }; - } - - public static ICustomMarshaler GetInstance(string cookie) => new ClassForwardingCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ClassForwardingCustomMarshaler))] - public static extern StringContainer MarshalerOnClassTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ClassForwardingCustomMarshaler))] StringContainer str); - - [Fact] - public void Parameter_CustomMarshalerProvided_CallsMethodsInCorrectOrdering() - { - Assert.Empty(OrderTrackingCustomMarshaler.Events); - - string val1 = "64001"; - Assert.Equal(val1, OrderTrackingMethod(val1)); - - string[] expectedOrderingFirstCall = new string[] - { - "Called GetInstance", - "Called MarshalManagedToNative", - "Called MarshalNativeToManaged", - "Called CleanUpNativeData" - }; - Assert.Equal(expectedOrderingFirstCall, OrderTrackingCustomMarshaler.Events); - - // GetInstance is only called once. - string val2 = "234"; - Assert.Equal(val2, OrderTrackingMethod(val2)); - IEnumerable expectedOrderingSecondCall = expectedOrderingFirstCall.Concat(new string[] - { - "Called MarshalManagedToNative", - "Called MarshalNativeToManaged", - "Called CleanUpNativeData" - }); - Assert.Equal(expectedOrderingSecondCall, OrderTrackingCustomMarshaler.Events); - } - - // This should only be used *once*, as it uses static state. - public class OrderTrackingCustomMarshaler : ICustomMarshaler - { - public static List Events { get; } = new List(); - public static IntPtr MarshaledNativeData { get; set; } - - public void CleanUpManagedData(object ManagedObj) - { - Events.Add("Called CleanUpManagedData"); - } - - public void CleanUpNativeData(IntPtr pNativeData) - { - Assert.Equal(MarshaledNativeData, pNativeData); - Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); - - Events.Add("Called CleanUpNativeData"); - } - - public int GetNativeDataSize() - { - Events.Add("Called GetNativeDataSize"); - return 0; - } - - public IntPtr MarshalManagedToNative(object ManagedObj) - { - Events.Add("Called MarshalManagedToNative"); - MarshaledNativeData = Marshal.StringToCoTaskMemAnsi((string)ManagedObj); - return MarshaledNativeData; - } - - public object MarshalNativeToManaged(IntPtr pNativeData) - { - Events.Add("Called MarshalNativeToManaged"); - return pNativeData.ToInt32().ToString(); - } - - public static ICustomMarshaler GetInstance(string cookie) - { - Assert.Empty(cookie); - Events.Add("Called GetInstance"); - return new OrderTrackingCustomMarshaler(); - } - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] - public static extern string OrderTrackingMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] string str); - - [Fact] - public void CustomMarshaler_BothMarshalTypeRefAndMarshalTypeProvided_PicksMarshalType() - { - Assert.Equal(2, BothTypeRefAndTypeMethod("64001")); - } - - public class OverridingCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("2"); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => new OverridingCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int BothTypeRefAndTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "System.Runtime.InteropServices.Tests.ICustomMarshalerTests+OverridingCustomMarshaler", MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] string str); - - [Fact] - public void Parameter_CookieProvided_PassesCookieToGetInstance() - { - int val = 64001; - Assert.Equal(val, CustomCookieMethod(val.ToString())); - Assert.Equal("Cookie", CookieTrackingCustomMarshaler.Cookie); - } - - public class CookieTrackingCustomMarshaler : ICustomMarshaler - { - public static string Cookie { get; set; } - - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) - { - Cookie = cookie; - return new CookieTrackingCustomMarshaler(); - } - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int CustomCookieMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CookieTrackingCustomMarshaler), MarshalCookie = "Cookie")] string str); - - [Fact] - public void Parameter_NotCustomMarshalerType_UsesSpecifiedMarshaler() - { - int val = 64001; - Assert.Equal(val, NonCustomMarshalerTypeMethod(val.ToString())); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int NonCustomMarshalerTypeMethod([MarshalAs(UnmanagedType.LPStr, MarshalTypeRef = typeof(OverridingCustomMarshaler))] string str); - - [Fact] - public void CustomMarshaler_Generic_Success() - { - Assert.Equal(234, GenericGetInstanceCustomMarshalerMethod("64001")); - } - - public class GenericCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("234"); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) - { - return new GenericCustomMarshaler(); - } - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int GenericGetInstanceCustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(GenericCustomMarshaler))] string str); - - [Fact] - public void CustomMarshaler_ValueTypeWithStringType_Success() - { - Assert.Equal(234, ValueTypeMarshalerOnStringTypeMethod("64001")); - } - - public struct CustomMarshalerValueType : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("234"); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) - { - return new CustomMarshalerValueType(); - } - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int ValueTypeMarshalerOnStringTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CustomMarshalerValueType))] string str); - - [Fact] - public void Parameter_MarshalerOnValueType_ThrowsMarshalDirectiveException() - { - Assert.Throws(() => MarshalerOnValueTypeMethod(0)); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int MarshalerOnValueTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] int str); - - [Fact] - public unsafe void Parameter_MarshalerOnPointer_ThrowsMarshalDirectiveException() - { - Assert.Throws(() => MarshalerOnPointerMethod(null)); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static unsafe extern int MarshalerOnPointerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] int* str); - - [Fact] - public void Parameter_NullICustomMarshaler_ThrowsTypeLoadException() - { - Assert.Throws(() => NullCustomMarshalerMethod("")); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int NullCustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = null)] string str); - - [Fact] - public void Parameter_NotICustomMarshaler_ThrowsApplicationException() - { - Assert.Throws(() => NonICustomMarshalerMethod("")); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int NonICustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(string))] string str); - - [Fact] - public void Parameter_OpenGenericICustomMarshaler_ThrowsTypeLoadException() - { - Assert.Throws(() => OpenGenericICustomMarshalerMethod("")); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int OpenGenericICustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(GenericCustomMarshaler<>))] string str); - - [Fact] - public void Parameter_GetInstanceMethodDoesntExist_ThrowsApplicationException() - { - Assert.Throws(() => NoGetInstanceMethod("")); - } - - public class NoGetInstanceCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int NoGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NoGetInstanceCustomMarshaler))] string str); - - [Fact] - public void Parameter_GetInstanceMethodInstanceMethod_ThrowsApplicationException() - { - Assert.Throws(() => InstanceGetInstanceMethod("")); - } - - public class InstanceGetInstanceCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - public ICustomMarshaler GetInstance(string cookie) => new InstanceGetInstanceCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int InstanceGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(InstanceGetInstanceCustomMarshaler))] string str); - - [Fact] - public void Parameter_GetInstanceMethodNoParameters_ThrowsApplicationException() - { - Assert.Throws(() => NoParametersGetInstanceMethod("")); - } - - public class NoParameterGetInstanceCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance() => new NoParameterGetInstanceCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int NoParametersGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NoParameterGetInstanceCustomMarshaler))] string str); - - [Fact] - public void Parameter_GetInstanceMethodNonStringParameter_ThrowsApplicationException() - { - Assert.Throws(() => NonStringGetInstanceMethod("")); - } - - public class NonStringGetInstanceCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(int x) => new NonStringGetInstanceCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int NonStringGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NonStringGetInstanceCustomMarshaler))] string str); - - [Fact] - public void Parameter_GetInstanceMethodReturnsVoid_ThrowsApplicationException() - { - Assert.Throws(() => VoidGetInstanceMethod("")); - } - - public class VoidGetInstanceCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static void GetInstance(string cookie) { } - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int VoidGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(VoidGetInstanceCustomMarshaler))] string str); - - [Fact] - public void Parameter_GetInstanceMethodReturnsNull_ThrowsApplicationException() - { - Assert.Throws(() => NullGetInstanceMethod("")); - } - - public class NullGetInstanceCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => null; - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int NullGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NullGetInstanceCustomMarshaler))] string str); - - [Fact] - public void Parameter_GetInstanceMethodThrows_ThrowsActualException() - { - Assert.Throws(() => ThrowingGetInstanceMethod("")); - } - - public class ThrowingGetInstanceCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => throw new NotImplementedException(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int ThrowingGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingGetInstanceCustomMarshaler))] string str); - - [Fact] - public void Parameter_MarshalManagedToNativeThrows_ThrowsActualException() - { - Assert.Throws(() => ThrowingMarshalManagedToNativeMethod("")); - } - - public class ThrowingMarshalManagedToNativeCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) { } - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => throw new NotImplementedException(); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => new ThrowingMarshalManagedToNativeCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int ThrowingMarshalManagedToNativeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingMarshalManagedToNativeCustomMarshaler))] string str); - - [Fact] - public void Parameter_CleanUpNativeDataMethodThrows_ThrowsActualException() - { - Assert.Throws(() => ThrowingCleanUpNativeDataMethod("")); - } - - public class ThrowingCleanUpNativeDataCustomMarshaler : ICustomMarshaler - { - public void CleanUpManagedData(object ManagedObj) { } - public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException(); - - public int GetNativeDataSize() => IntPtr.Size; - - public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); - public object MarshalNativeToManaged(IntPtr pNativeData) => null; - - public static ICustomMarshaler GetInstance(string cookie) => new ThrowingMarshalManagedToNativeCustomMarshaler(); - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int ThrowingCleanUpNativeDataMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingCleanUpNativeDataCustomMarshaler))] string str); - - [Fact] - public static void Field_ParentIsStruct_ThrowsTypeLoadException() - { - Assert.Throws(() => StructWithCustomMarshalerFieldMethod(new StructWithCustomMarshalerField())); - } - - public struct StructWithCustomMarshalerField - { - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] - public string Field; - } - - [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] - public static extern int StructWithCustomMarshalerFieldMethod(StructWithCustomMarshalerField c); - - public static int Main(String[] args) - { - return new ICustomMarshalerTests().RunTests(); - } - } -} diff --git a/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj b/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj deleted file mode 100644 index ae3ef0e4cf..0000000000 --- a/tests/src/Interop/ICustomMarshaler/ICustomMarshaler.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {95DFC527-4DC1-495E-97D7-E94EE1F7140D} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - true - $(DefineConstants);Windows - - - - - - - False - - - - - - - - - - - - - - diff --git a/tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs b/tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs new file mode 100644 index 0000000000..b07b8109cc --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs @@ -0,0 +1,597 @@ +// 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.Collections.Generic; +using System.Linq; +using Xunit; + +namespace System.Runtime.InteropServices.Tests +{ + public class ICustomMarshalerTests : XunitBase + { + // To avoid having to create a native test library to reference in tests that + // interact with native libraries, we can use a simple method from the C standard + // library. Unfortunately, the C standard library has different names on Windows + // vs Unix. +#if Windows + public const string LibcLibrary = "msvcrt.dll"; +#else + public const string LibcLibrary = "libc"; +#endif + + [Fact] + public void CustomMarshaler_StringType_Success() + { + int val = 64001; + Assert.Equal(val, MarshalerOnStringTypeMethod(val.ToString())); + } + + public class StringForwardingCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new StringForwardingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi")] + public static extern int MarshalerOnStringTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_ArrayType_Success() + { + int val = 64001; + Assert.Equal(val, MarshalerOnArrayTypeMethod(new string[] { val.ToString() })); + } + + public class ArrayForwardingCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi(((string[])ManagedObj)[0]); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new ArrayForwardingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi")] + public static extern int MarshalerOnArrayTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "System.Runtime.InteropServices.Tests.ICustomMarshalerTests+ArrayForwardingCustomMarshaler")] string[] str); + + [Fact] + public void CustomMarshaler_BoxedValueType_Success() + { + int val = 64001; + Assert.Equal(val * 2, MarshalerOnBoxedValueTypeMethod(val)); + } + + public class BoxedValueTypeCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + int unboxedValueType = (int)ManagedObj * 2; + return Marshal.StringToCoTaskMemAnsi(unboxedValueType.ToString()); + } + + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new BoxedValueTypeCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi")] + public static extern int MarshalerOnBoxedValueTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] object i); + + [Fact] + public void Parameter_CustomMarshalerProvidedOnClassType_ForwardsCorrectly() + { + int val = 64001; + Assert.Equal((val * 2).ToString(), MarshalerOnClassTypeMethod(new StringContainer { Value = val.ToString() }).Value); + } + + public class StringContainer + { + public string Value { get; set; } + } + + public class ClassForwardingCustomMarshaler : ICustomMarshaler + { + private bool CleanedString { get; set; } + + public void CleanUpManagedData(object ManagedObj) {} + + public void CleanUpNativeData(IntPtr pNativeData) + { + if (CleanedString) + { + return; + } + + Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); + CleanedString = true; + } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + return Marshal.StringToCoTaskMemAnsi(((StringContainer)ManagedObj).Value); + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + int doubleValue = pNativeData.ToInt32() * 2; + return new StringContainer { Value = doubleValue.ToString() }; + } + + public static ICustomMarshaler GetInstance(string cookie) => new ClassForwardingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ClassForwardingCustomMarshaler))] + public static extern StringContainer MarshalerOnClassTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ClassForwardingCustomMarshaler))] StringContainer str); + + [Fact] + public void Parameter_CustomMarshalerProvided_CallsMethodsInCorrectOrdering() + { + Assert.Empty(OrderTrackingCustomMarshaler.Events); + + string val1 = "64001"; + Assert.Equal(val1, OrderTrackingMethod(val1)); + + string[] expectedOrderingFirstCall = new string[] + { + "Called GetInstance", + "Called MarshalManagedToNative", + "Called MarshalNativeToManaged", + "Called CleanUpNativeData" + }; + Assert.Equal(expectedOrderingFirstCall, OrderTrackingCustomMarshaler.Events); + + // GetInstance is only called once. + string val2 = "234"; + Assert.Equal(val2, OrderTrackingMethod(val2)); + IEnumerable expectedOrderingSecondCall = expectedOrderingFirstCall.Concat(new string[] + { + "Called MarshalManagedToNative", + "Called MarshalNativeToManaged", + "Called CleanUpNativeData" + }); + Assert.Equal(expectedOrderingSecondCall, OrderTrackingCustomMarshaler.Events); + } + + // This should only be used *once*, as it uses static state. + public class OrderTrackingCustomMarshaler : ICustomMarshaler + { + public static List Events { get; } = new List(); + public static IntPtr MarshaledNativeData { get; set; } + + public void CleanUpManagedData(object ManagedObj) + { + Events.Add("Called CleanUpManagedData"); + } + + public void CleanUpNativeData(IntPtr pNativeData) + { + Assert.Equal(MarshaledNativeData, pNativeData); + Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); + + Events.Add("Called CleanUpNativeData"); + } + + public int GetNativeDataSize() + { + Events.Add("Called GetNativeDataSize"); + return 0; + } + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + Events.Add("Called MarshalManagedToNative"); + MarshaledNativeData = Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + return MarshaledNativeData; + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + Events.Add("Called MarshalNativeToManaged"); + return pNativeData.ToInt32().ToString(); + } + + public static ICustomMarshaler GetInstance(string cookie) + { + Assert.Empty(cookie); + Events.Add("Called GetInstance"); + return new OrderTrackingCustomMarshaler(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] + public static extern string OrderTrackingMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_BothMarshalTypeRefAndMarshalTypeProvided_PicksMarshalType() + { + Assert.Equal(2, BothTypeRefAndTypeMethod("64001")); + } + + public class OverridingCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("2"); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new OverridingCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int BothTypeRefAndTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "System.Runtime.InteropServices.Tests.ICustomMarshalerTests+OverridingCustomMarshaler", MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] string str); + + [Fact] + public void Parameter_CookieProvided_PassesCookieToGetInstance() + { + int val = 64001; + Assert.Equal(val, CustomCookieMethod(val.ToString())); + Assert.Equal("Cookie", CookieTrackingCustomMarshaler.Cookie); + } + + public class CookieTrackingCustomMarshaler : ICustomMarshaler + { + public static string Cookie { get; set; } + + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) + { + Cookie = cookie; + return new CookieTrackingCustomMarshaler(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int CustomCookieMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CookieTrackingCustomMarshaler), MarshalCookie = "Cookie")] string str); + + [Fact] + public void Parameter_NotCustomMarshalerType_UsesSpecifiedMarshaler() + { + int val = 64001; + Assert.Equal(val, NonCustomMarshalerTypeMethod(val.ToString())); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NonCustomMarshalerTypeMethod([MarshalAs(UnmanagedType.LPStr, MarshalTypeRef = typeof(OverridingCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_Generic_Success() + { + Assert.Equal(234, GenericGetInstanceCustomMarshalerMethod("64001")); + } + + public class GenericCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("234"); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) + { + return new GenericCustomMarshaler(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int GenericGetInstanceCustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(GenericCustomMarshaler))] string str); + + [Fact] + public void CustomMarshaler_ValueTypeWithStringType_Success() + { + Assert.Equal(234, ValueTypeMarshalerOnStringTypeMethod("64001")); + } + + public struct CustomMarshalerValueType : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { Marshal.ZeroFreeCoTaskMemAnsi(pNativeData); } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi("234"); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) + { + return new CustomMarshalerValueType(); + } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ValueTypeMarshalerOnStringTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CustomMarshalerValueType))] string str); + + [Fact] + public void Parameter_MarshalerOnValueType_ThrowsMarshalDirectiveException() + { + Assert.Throws(() => MarshalerOnValueTypeMethod(0)); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int MarshalerOnValueTypeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] int str); + + [Fact] + public unsafe void Parameter_MarshalerOnPointer_ThrowsMarshalDirectiveException() + { + Assert.Throws(() => MarshalerOnPointerMethod(null)); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static unsafe extern int MarshalerOnPointerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] int* str); + + [Fact] + public void Parameter_NullICustomMarshaler_ThrowsTypeLoadException() + { + Assert.Throws(() => NullCustomMarshalerMethod("")); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NullCustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = null)] string str); + + [Fact] + public void Parameter_NotICustomMarshaler_ThrowsApplicationException() + { + Assert.Throws(() => NonICustomMarshalerMethod("")); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NonICustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(string))] string str); + + [Fact] + public void Parameter_OpenGenericICustomMarshaler_ThrowsTypeLoadException() + { + Assert.Throws(() => OpenGenericICustomMarshalerMethod("")); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int OpenGenericICustomMarshalerMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(GenericCustomMarshaler<>))] string str); + + [Fact] + public void Parameter_GetInstanceMethodDoesntExist_ThrowsApplicationException() + { + Assert.Throws(() => NoGetInstanceMethod("")); + } + + public class NoGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NoGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NoGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodInstanceMethod_ThrowsApplicationException() + { + Assert.Throws(() => InstanceGetInstanceMethod("")); + } + + public class InstanceGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + public ICustomMarshaler GetInstance(string cookie) => new InstanceGetInstanceCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int InstanceGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(InstanceGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodNoParameters_ThrowsApplicationException() + { + Assert.Throws(() => NoParametersGetInstanceMethod("")); + } + + public class NoParameterGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance() => new NoParameterGetInstanceCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NoParametersGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NoParameterGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodNonStringParameter_ThrowsApplicationException() + { + Assert.Throws(() => NonStringGetInstanceMethod("")); + } + + public class NonStringGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(int x) => new NonStringGetInstanceCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NonStringGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NonStringGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodReturnsVoid_ThrowsApplicationException() + { + Assert.Throws(() => VoidGetInstanceMethod("")); + } + + public class VoidGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static void GetInstance(string cookie) { } + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int VoidGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(VoidGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodReturnsNull_ThrowsApplicationException() + { + Assert.Throws(() => NullGetInstanceMethod("")); + } + + public class NullGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => null; + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int NullGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NullGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_GetInstanceMethodThrows_ThrowsActualException() + { + Assert.Throws(() => ThrowingGetInstanceMethod("")); + } + + public class ThrowingGetInstanceCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => IntPtr.Zero; + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => throw new NotImplementedException(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ThrowingGetInstanceMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingGetInstanceCustomMarshaler))] string str); + + [Fact] + public void Parameter_MarshalManagedToNativeThrows_ThrowsActualException() + { + Assert.Throws(() => ThrowingMarshalManagedToNativeMethod("")); + } + + public class ThrowingMarshalManagedToNativeCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) { } + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => throw new NotImplementedException(); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new ThrowingMarshalManagedToNativeCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ThrowingMarshalManagedToNativeMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingMarshalManagedToNativeCustomMarshaler))] string str); + + [Fact] + public void Parameter_CleanUpNativeDataMethodThrows_ThrowsActualException() + { + Assert.Throws(() => ThrowingCleanUpNativeDataMethod("")); + } + + public class ThrowingCleanUpNativeDataCustomMarshaler : ICustomMarshaler + { + public void CleanUpManagedData(object ManagedObj) { } + public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException(); + + public int GetNativeDataSize() => IntPtr.Size; + + public IntPtr MarshalManagedToNative(object ManagedObj) => Marshal.StringToCoTaskMemAnsi((string)ManagedObj); + public object MarshalNativeToManaged(IntPtr pNativeData) => null; + + public static ICustomMarshaler GetInstance(string cookie) => new ThrowingMarshalManagedToNativeCustomMarshaler(); + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int ThrowingCleanUpNativeDataMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ThrowingCleanUpNativeDataCustomMarshaler))] string str); + + [Fact] + public static void Field_ParentIsStruct_ThrowsTypeLoadException() + { + Assert.Throws(() => StructWithCustomMarshalerFieldMethod(new StructWithCustomMarshalerField())); + } + + public struct StructWithCustomMarshalerField + { + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StringForwardingCustomMarshaler))] + public string Field; + } + + [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] + public static extern int StructWithCustomMarshalerFieldMethod(StructWithCustomMarshalerField c); + + public static int Main(String[] args) + { + return new ICustomMarshalerTests().RunTests(); + } + } +} diff --git a/tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.csproj b/tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.csproj new file mode 100644 index 0000000000..0e68e23a9c --- /dev/null +++ b/tests/src/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.csproj @@ -0,0 +1,31 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + $(DefineConstants);Windows + + + + + + + False + + + + + + + + + + + -- cgit v1.2.3