From 5ceff61e966aef079bdcfee6b637c39600ee7dcb Mon Sep 17 00:00:00 2001 From: Swaroop Sridhar Date: Fri, 4 Jan 2019 18:22:32 -0800 Subject: [PATCH] Add NativeLibrary Resolve Event This change adds the Native library resolving event, to be raised as the last attempt to resolve a native DLL in an AssemblyLoadContext. With this change, the DllImport resolution sequence is as follows (stopping at any step with successful resolution): * If the invoking-assembly is not in the default load context, call AssemblyLoadContext.LoadUnmanagedDll() * Run the default load logic, try loading from: * AppDomain cache * NATIVE_DLL_SEARCH_DIRECTORIES * Invoking-assembly directory, System32, etc. based on DllImportSearchPaths * Raise the ResolvingUnmanagedDll event API Review: https://github.com/dotnet/corefx/issues/32850 The ResolveEventTests triggered a pre-existing bug in the exception handling code (#21964). Disabling the test on ARM64 Windows until the issue is fixed. --- .../Runtime/Loader/AssemblyLoadContext.cs | 50 ++++++- src/vm/dllimport.cpp | 126 ++++++++++++++---- src/vm/dllimport.h | 3 +- src/vm/metasig.h | 1 + src/vm/mscorlib.h | 1 + tests/issues.targets | 6 + tests/src/Interop/CMakeLists.txt | 1 + .../NativeLibraryResolveEvent/CMakeLists.txt | 13 ++ .../ResolveEventTests.cs | 118 ++++++++++++++++ .../ResolveEventTests.csproj | 36 +++++ .../NativeLibraryResolveEvent/ResolvedLib.cpp | 12 ++ .../TestAsm/TestAsm.cs | 16 +++ .../TestAsm/TestAsm.csproj | 33 +++++ tests/src/Interop/ReadMe.md | 4 + 14 files changed, 391 insertions(+), 29 deletions(-) create mode 100644 tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt create mode 100644 tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs create mode 100644 tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj create mode 100644 tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.cpp create mode 100644 tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs create mode 100644 tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj diff --git a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index 7ec6cf4c29a4..fdb27d95f6d1 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -78,6 +78,7 @@ internal AssemblyLoadContext(bool fRepresentsTPALoadContext, bool isCollectible) m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(thisHandlePtr, fRepresentsTPALoadContext, isCollectible); // Initialize event handlers to be null by default + ResolvingUnmanagedDll = null; Resolving = null; Unloading = null; @@ -304,12 +305,12 @@ private Assembly GetFirstResolvedAssembly(AssemblyName assemblyName) resolvedAssembly = handler(this, assemblyName); if (resolvedAssembly != null) { - break; + return resolvedAssembly; } } } - return resolvedAssembly; + return null; } private Assembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string requestedSimpleName) @@ -413,6 +414,36 @@ private static IntPtr ResolveUnmanagedDll(string unmanagedDllName, IntPtr gchMan return context.LoadUnmanagedDll(unmanagedDllName); } + // This method is invoked by the VM to resolve a native library using the ResolvingUnmanagedDll event + // after trying all other means of resolution. + private static IntPtr ResolveUnmanagedDllUsingEvent(string unmanagedDllName, Assembly assembly, IntPtr gchManagedAssemblyLoadContext) + { + AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target); + return context.GetResolvedUnmanagedDll(assembly, unmanagedDllName); + } + + private IntPtr GetResolvedUnmanagedDll(Assembly assembly, string unmanagedDllName) + { + IntPtr resolvedDll = IntPtr.Zero; + + Func dllResolveHandler = ResolvingUnmanagedDll; + + if (dllResolveHandler != null) + { + // Loop through the event subscribers and return the first non-null native library handle + foreach (Func handler in dllResolveHandler.GetInvocationList()) + { + resolvedDll = handler(assembly, unmanagedDllName); + if (resolvedDll != IntPtr.Zero) + { + return resolvedDll; + } + } + } + + return IntPtr.Zero; + } + public static AssemblyLoadContext Default { get @@ -509,7 +540,22 @@ internal static void OnProcessExit() } } + // Event handler for resolving native libraries. + // This event is raised if the native library could not be resolved via + // the default resolution logic [including AssemblyLoadContext.LoadUnmanagedDll()] + // + // Inputs: Invoking assembly, and library name to resolve + // Returns: A handle to the loaded native library + public event Func ResolvingUnmanagedDll; + + // Event handler for resolving managed assemblies. + // This event is raised if the managed assembly could not be resolved via + // the default resolution logic [including AssemblyLoadContext.Load()] + // + // Inputs: The AssemblyLoadContext and AssemblyName to be loaded + // Returns: The Loaded assembly object. public event Func Resolving; + public event Action Unloading; // Contains the reference to VM's representation of the AssemblyLoadContext diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp index 8c8688200679..37cfda977549 100644 --- a/src/vm/dllimport.cpp +++ b/src/vm/dllimport.cpp @@ -6275,7 +6275,7 @@ INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR sy } // static -NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, PCWSTR wszLibName) +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, PCWSTR wszLibName) { STANDARD_VM_CONTRACT; //Dynamic Pinvoke Support: @@ -6290,7 +6290,8 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, } #endif - LPVOID hmod = NULL; + NATIVE_LIBRARY_HANDLE hmod = NULL; + AppDomain* pDomain = GetAppDomain(); CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext(); Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); @@ -6349,11 +6350,92 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext); // Make the call - CALL_MANAGED_METHOD(hmod,LPVOID,args); + CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args); + + GCPROTECT_END(); + + return hmod; +} + +// Return the AssemblyLoadContext for an assembly +INT_PTR GetManagedAssemblyLoadContext(Assembly* pAssembly) +{ + STANDARD_VM_CONTRACT; + + PTR_ICLRPrivBinder pBindingContext = pAssembly->GetManifestFile()->GetBindingContext(); + if (pBindingContext == NULL) + { + // GetBindingContext() returns NULL for System.Private.CoreLib + return NULL; + } + + UINT_PTR assemblyBinderID = 0; + IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID)); + + AppDomain *pDomain = GetAppDomain(); + ICLRPrivBinder *pCurrentBinder = reinterpret_cast(assemblyBinderID); + +#ifdef FEATURE_COMINTEROP + if (AreSameBinderInstance(pCurrentBinder, pDomain->GetWinRtBinder())) + { + // No ALC associated handle with WinRT Binders. + return NULL; + } +#endif // FEATURE_COMINTEROP + + // The code here deals with two implementations of ICLRPrivBinder interface: + // - CLRPrivBinderCoreCLR for the TPA binder in the default ALC, and + // - CLRPrivBinderAssemblyLoadContext for custom ALCs. + // in order obtain the associated ALC handle. + INT_PTR ptrManagedAssemblyLoadContext = AreSameBinderInstance(pCurrentBinder, pDomain->GetTPABinderContext()) + ? ((CLRPrivBinderCoreCLR *)pCurrentBinder)->GetManagedAssemblyLoadContext() + : ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext(); + + return ptrManagedAssemblyLoadContext; +} + +// static +NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaEvent(NDirectMethodDesc * pMD, PCWSTR wszLibName) +{ + STANDARD_VM_CONTRACT; + + NATIVE_LIBRARY_HANDLE hmod = NULL; + Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly(); + INT_PTR ptrManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(pAssembly); + + if (ptrManagedAssemblyLoadContext == NULL) + { + return NULL; + } + + GCX_COOP(); + + struct { + STRINGREF DllName; + OBJECTREF AssemblyRef; + } gc = { NULL, NULL }; + + GCPROTECT_BEGIN(gc); + + gc.DllName = StringObject::NewString(wszLibName); + gc.AssemblyRef = pAssembly->GetExposedObject(); + + // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDllUsingEvent method + // While ResolveUnmanagedDllUsingEvent() could compute the AssemblyLoadContext using the AssemblyRef + // argument, it will involve another pInvoke to the runtime. So AssemblyLoadContext is passed in + // as an additional argument. + PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLLUSINGEVENT); + DECLARE_ARGHOLDER_ARRAY(args, 3); + args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.DllName); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.AssemblyRef); + args[ARGNUM_2] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext); + + // Make the call + CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args); GCPROTECT_END(); - return (NATIVE_LIBRARY_HANDLE)hmod; + return hmod; } // Try to load the module alongside the assembly where the PInvoke was declared. @@ -6633,15 +6715,13 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke AppDomain* pDomain = GetAppDomain(); // AssemblyLoadContext is not supported in AppX mode and thus, - // we should not perform PInvoke resolution via it when operating in - // AppX mode. + // we should not perform PInvoke resolution via it when operating in AppX mode. if (!AppX::IsAppXProcess()) { - hmod = LoadLibraryModuleViaHost(pMD, pDomain, wszLibName); + hmod = LoadLibraryModuleViaHost(pMD, wszLibName); if (hmod != NULL) { #ifdef FEATURE_PAL - // Register the system library handle with PAL and get a PAL library handle hmod = PAL_RegisterLibraryDirect(hmod, wszLibName); #endif // FEATURE_PAL return hmod.Extract(); @@ -6654,36 +6734,30 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke return hmod.Extract(); } -#ifdef FEATURE_PAL - // In the PAL version of CoreCLR, the CLR module itself exports the functionality - // that the Windows version obtains from kernel32 and friends. In order to avoid - // picking up the wrong instance, we perform this redirection first. - // This is also true for CoreSystem builds, where mscorlib p/invokes are forwarded through coreclr - // itself so we can control CoreSystem library/API name re-mapping from one central location. - if (SString::_wcsicmp(wszLibName, MAIN_CLR_MODULE_NAME_W) == 0) + hmod = LoadLibraryModuleBySearch(pMD, pErrorTracker, wszLibName); + if (hmod != NULL) { - hmod = GetCLRModule(); - } +#ifdef FEATURE_PAL + hmod = PAL_RegisterLibraryDirect(hmod, wszLibName); #endif // FEATURE_PAL - if (hmod == NULL) + // If we have a handle add it to the cache. + pDomain->AddUnmanagedImageToCache(wszLibName, hmod); + return hmod.Extract(); + } + + if (!AppX::IsAppXProcess()) { - hmod = LoadLibraryModuleBySearch(pMD, pErrorTracker, wszLibName); + hmod = LoadLibraryModuleViaEvent(pMD, wszLibName); if (hmod != NULL) { #ifdef FEATURE_PAL - // Register the system library handle with PAL and get a PAL library handle hmod = PAL_RegisterLibraryDirect(hmod, wszLibName); #endif // FEATURE_PAL + return hmod.Extract(); } } - if (hmod != NULL) - { - // If we have a handle add it to the cache. - pDomain->AddUnmanagedImageToCache(wszLibName, hmod); - } - return hmod.Extract(); } diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h index 2f25e59570e1..1203803b9afd 100644 --- a/src/vm/dllimport.h +++ b/src/vm/dllimport.h @@ -124,7 +124,8 @@ class NDirect static NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(AppDomain* pDomain, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker); static NATIVE_LIBRARY_HANDLE LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker); - static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, const wchar_t* wszLibName); + static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, LPCWSTR wszLibName); + static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaEvent(NDirectMethodDesc * pMD, LPCWSTR wszLibName); static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName); static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(Assembly *callingAssembly, BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName); diff --git a/src/vm/metasig.h b/src/vm/metasig.h index 154c91570cc2..cfa844012eea 100644 --- a/src/vm/metasig.h +++ b/src/vm/metasig.h @@ -551,6 +551,7 @@ DEFINE_METASIG_T(IM(RefGuid_OutIntPtr_RetCustomQueryInterfaceResult, r(g(GUID)) #endif //FEATURE_COMINTEROP DEFINE_METASIG_T(SM(IntPtr_AssemblyName_RetAssemblyBase, I C(ASSEMBLY_NAME), C(ASSEMBLYBASE))) +DEFINE_METASIG_T(SM(Str_AssemblyBase_IntPtr_RetIntPtr, s C(ASSEMBLYBASE) I, I)) // ThreadPool DEFINE_METASIG(SM(Obj_Bool_RetVoid, j F, v)) diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h index 62205d39bc88..a1341dd70f68 100644 --- a/src/vm/mscorlib.h +++ b/src/vm/mscorlib.h @@ -864,6 +864,7 @@ DEFINE_METHOD(FIRSTCHANCE_EVENTARGS, CTOR, .ctor, DEFINE_CLASS(ASSEMBLYLOADCONTEXT, Loader, AssemblyLoadContext) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVE, Resolve, SM_IntPtr_AssemblyName_RetAssemblyBase) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLL, ResolveUnmanagedDll, SM_Str_IntPtr_RetIntPtr) +DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLLUSINGEVENT, ResolveUnmanagedDllUsingEvent, SM_Str_AssemblyBase_IntPtr_RetIntPtr) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUSINGEVENT, ResolveUsingResolvingEvent, SM_IntPtr_AssemblyName_RetAssemblyBase) DEFINE_FIELD(ASSEMBLYLOADCONTEXT, ASSEMBLY_LOAD, AssemblyLoad) DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_ASSEMBLY_LOAD, OnAssemblyLoad, SM_Assembly_RetVoid) diff --git a/tests/issues.targets b/tests/issues.targets index 11043ffd2ad1..fd1d96558f93 100644 --- a/tests/issues.targets +++ b/tests/issues.targets @@ -388,6 +388,9 @@ 9565 + + 21964 + 9565 @@ -819,6 +822,9 @@ Issue building native components for the test. Since tests are currently built on Windows, the native components end up at Core_Root instead of Test directory, which is not suitable for the test. + + Issue building native components for the test. Since tests are currently built on Windows, the native components end up at Core_Root instead of Test directory, which is not suitable for the test. + needs triage diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt index 9784ea1c95ab..e46e78a8ff47 100644 --- a/tests/src/Interop/CMakeLists.txt +++ b/tests/src/Interop/CMakeLists.txt @@ -56,6 +56,7 @@ add_subdirectory(StringMarshalling/VBByRefStr) add_subdirectory(MarshalAPI/FunctionPointer) add_subdirectory(MarshalAPI/IUnknown) add_subdirectory(NativeLibrary) +add_subdirectory(NativeLibraryResolveEvent) add_subdirectory(SizeConst) add_subdirectory(DllImportAttribute/ExeFile) add_subdirectory(DllImportAttribute/FileNameContainDot) diff --git a/tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt b/tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt new file mode 100644 index 000000000000..20935806ca18 --- /dev/null +++ b/tests/src/Interop/NativeLibraryResolveEvent/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required (VERSION 2.6) +project (ResolvedLib) +include_directories(${INC_PLATFORM_DIR}) +set(SOURCES ResolvedLib.cpp) + +# add the executable +add_library (ResolvedLib SHARED ${SOURCES}) +target_link_libraries(ResolvedLib ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS ResolvedLib DESTINATION bin) + + diff --git a/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs new file mode 100644 index 000000000000..245bfc652929 --- /dev/null +++ b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.IO; +using System.Runtime.Loader; +using System.Reflection; +using System.Runtime.InteropServices; + +using Console = Internal.Console; + +public class ALC : AssemblyLoadContext +{ + protected override Assembly Load(AssemblyName assemblyName) + { + return Assembly.Load(assemblyName); + } +} + +public class ResolveEventTests +{ + static int HandlerTracker = 1; + + public static int Main() + { + // Events on the Default Load Context + + try + { + AssemblyLoadContext.Default.ResolvingUnmanagedDll += HandlerFail; + NativeSum(10, 10); + } + catch (DllNotFoundException e) + { + if (HandlerTracker != 0) + { + Console.WriteLine("Event Handlers not called as expected"); + return 101; + } + } + catch (Exception e) + { + Console.WriteLine($"Unexpected exception: {e.Message}"); + return 102; + } + + try + { + AssemblyLoadContext.Default.ResolvingUnmanagedDll += HandlerPass; + + if(NativeSum(10, 10) != 20) + { + Console.WriteLine("Unexpected ReturnValue from NativeSum()"); + return 103; + } + if (HandlerTracker != 0) + { + Console.WriteLine("Event Handlers not called as expected"); + return 104; + } + } + catch (Exception e) + { + Console.WriteLine($"Unexpected exception: {e.Message}"); + return 105; + } + + // Events on a Custom Load Context + + try + { + string currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + string testAsmDir = Path.Combine(currentDir, "..", "TestAsm", "TestAsm"); + + ALC alc = new ALC(); + alc.ResolvingUnmanagedDll += HandlerPass; + + var assembly = alc.LoadFromAssemblyPath(Path.Combine(testAsmDir, "TestAsm.dll")); + var type = assembly.GetType("TestAsm"); + var method = type.GetMethod("Sum"); + int value = (int)method.Invoke(null, new object[] { 10, 10 }); + + if(value != 20) + { + Console.WriteLine("Unexpected ReturnValue from TestAsm.Sum()"); + return 106; + } + if (HandlerTracker != 1) + { + Console.WriteLine("Event Handlers not called as expected"); + return 107; + } + } + catch (Exception e) + { + Console.WriteLine($"Unexpected exception: {e.Message}"); + return 108; + } + + return 100; + } + + public static IntPtr HandlerFail(Assembly assembly, string libraryName) + { + HandlerTracker--; + return IntPtr.Zero; + } + + public static IntPtr HandlerPass(Assembly assembly, string libraryName) + { + HandlerTracker++; + return NativeLibrary.Load("ResolvedLib", assembly, null); + } + + [DllImport("NativeLib")] + static extern int NativeSum(int arg1, int arg2); + +} diff --git a/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj new file mode 100644 index 000000000000..d912938457f9 --- /dev/null +++ b/tests/src/Interop/NativeLibraryResolveEvent/ResolveEventTests.csproj @@ -0,0 +1,36 @@ + + + + + Debug + AnyCPU + ResolveEventTests + 2.0 + {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + true + + + + + + + + + False + + + + + + + + + + + + + diff --git a/tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.cpp b/tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.cpp new file mode 100644 index 000000000000..87ae50ed5878 --- /dev/null +++ b/tests/src/Interop/NativeLibraryResolveEvent/ResolvedLib.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" DLL_EXPORT int NativeSum(int a, int b) +{ + return a + b; +} + + diff --git a/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs new file mode 100644 index 000000000000..1517c94f6ec2 --- /dev/null +++ b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.Runtime.InteropServices; + +public class TestAsm +{ + public static int Sum(int a, int b) + { + return NativeSum(a, b); + } + + [DllImport("NativeLib")] + static extern int NativeSum(int arg1, int arg2); +} diff --git a/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj new file mode 100644 index 000000000000..e8f3e3af219f --- /dev/null +++ b/tests/src/Interop/NativeLibraryResolveEvent/TestAsm/TestAsm.csproj @@ -0,0 +1,33 @@ + + + + + Debug + AnyCPU + TestAsm + 2.0 + {F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0} + Library + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + true + true + + + + + + + + + False + + + + + + + + + + diff --git a/tests/src/Interop/ReadMe.md b/tests/src/Interop/ReadMe.md index 9bd1d79313ad..97e420258024 100644 --- a/tests/src/Interop/ReadMe.md +++ b/tests/src/Interop/ReadMe.md @@ -57,6 +57,10 @@ Testing P/Invoke has two aspects: The Marshal API surface area testing is traditionally done via unit testing and far better suited in the [CoreFX](https://github.com/dotnet/corefx/tree/master/src/System.Runtime.InteropServices/tests) repo. Cases where testing the API surface area requires native tests assets will be performed in the [CoreCLR](https://github.com/dotnet/coreclr/tree/master/tests/src/Interop) repo. +### NativeLibrary + +This series has unit tests corresponding to `System.Runtime.NativeLibrary` APIs and related events in `System.Runtime.Loader.AssemblyLoadContext`. + ## Common Task steps ### Adding new native project