From a7317de7ae3f7aa99c91ea01b217762e93b110da Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 7 Aug 2023 13:22:57 -0700 Subject: [PATCH 1/5] NAOT - Suppress OS dialog for LoadLibrary failures on Windows. Reenable the NativeLibrary tests --- .../NativeLibrary.NativeAot.Windows.cs | 13 +++++++++++-- .../Windows/Kernel32/Interop.SetThreadErrorMode.cs | 3 ++- src/tests/issues.targets | 3 --- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs index 6f5587fa0c33d..ae25ffa458d3e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs @@ -13,19 +13,22 @@ private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadL { IntPtr hmod; + // Disable the OS dialogs when failing to load. This matches CoreCLR. + uint prev; + bool set = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS | Interop.Kernel32.SEM_NOOPENFILEERRORBOX, out prev); if (((uint)flags & 0xFFFFFF00) != 0) { hmod = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, (int)((uint)flags & 0xFFFFFF00)); if (hmod != IntPtr.Zero) { - return hmod; + goto exit; } int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER) { errorTracker.TrackErrorCode(lastError); - return hmod; + goto exit; } } @@ -35,6 +38,12 @@ private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadL errorTracker.TrackErrorCode(Marshal.GetLastWin32Error()); } + exit: + if (set) + { + Interop.Kernel32.SetThreadErrorMode(prev, out _); + } + return hmod; } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs index 340b75115973f..c27e92419943d 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadErrorMode.cs @@ -14,6 +14,7 @@ internal static partial bool SetThreadErrorMode( uint dwNewMode, out uint lpOldMode); - internal const uint SEM_FAILCRITICALERRORS = 1; + internal const int SEM_FAILCRITICALERRORS = 0x00000001; + internal const int SEM_NOOPENFILEERRORBOX = 0x00008000; } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index d1dc86c455bbb..ec3bf368bd8e2 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -778,9 +778,6 @@ https://github.com/dotnet/runtimelab/issues/164 - - https://github.com/dotnet/runtimelab/issues/165 - https://github.com/dotnet/runtimelab/issues/165 From 8d9144a85e9fef23005cf9acf5af282b1ee89688 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 7 Aug 2023 15:47:20 -0700 Subject: [PATCH 2/5] Update NativeAOT to load from application directory when DllImportSearchPath.AssemblyDirectory is defined. --- .../NativeLibrary.NativeAot.cs | 7 ++++++- .../DllImportSearchPathsTest.cs | 21 +++++++++++++++++++ .../DllImportSearchPathsTest.csproj | 18 +++++++++++----- .../NativeLibrary/API/NativeLibraryTests.cs | 10 +++++++++ .../API/NativeLibraryTests.csproj | 18 +++++++++++----- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs index d79088ae9b4f0..2fcbbd704f626 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs @@ -97,7 +97,12 @@ internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssembl else if ((callingAssembly != null) && searchAssemblyDirectory) { // Try to load the module alongside the assembly where the PInvoke was declared. - // This only makes sense in dynamic scenarios (JIT/interpreter), so leaving this out for now. + // For PInvokes where the DllImportSearchPath.AssemblyDirectory is specified, look next to the application. + ret = LoadLibraryHelper(Path.Combine(AppContext.BaseDirectory, currLibNameVariation), dllImportSearchPathFlags, ref errorTracker); + if (ret != IntPtr.Zero) + { + return ret; + } } ret = LoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, ref errorTracker); diff --git a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs index d3837adbab4a7..add031eca38b1 100644 --- a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs +++ b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs @@ -39,6 +39,15 @@ public static void AssemblyDirectory_Found() Assert.Equal(3, sum); } + public static bool IsNativeAot => TestLibrary.Utilities.IsNativeAot; + + [ConditionalFact(nameof(IsNativeAot))] + public static void AssemblyDirectoryAot_Found() + { + int sum = NativeLibraryPInvokeAot.Sum(1, 2); + Assert.Equal(3, sum); + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] public static void AssemblyDirectory_Fallback_Found() @@ -70,3 +79,15 @@ public static int Sum(int a, int b) [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] static extern int NativeSum(int arg1, int arg2); } + +public class NativeLibraryPInvokeAot +{ + public static int Sum(int a, int b) + { + return NativeSum(a, b); + } + + [DllImport(NativeLibraryToLoad.Name + "-in-native")] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)] + static extern int NativeSum(int arg1, int arg2); +} diff --git a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj index e4ca9dc74471e..9a376d8102918 100644 --- a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj +++ b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj @@ -14,16 +14,24 @@ - <_FilesToMove Include="$(OutDir)/libNativeLibrary.*" /> - <_FilesToMove Include="$(OutDir)/NativeLibrary.*" /> + + - + - <_FilesToCopy Include="$(OutDir)/$(TargetName).dll" /> + - + + + + + + + + + diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 1d29a82321843..556c006d54b76 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -189,6 +189,16 @@ public void LoadLibrary_AssemblyDirectory() EXPECT(TryLoadLibrary_WithAssembly(libName, assemblyInSubdirectory, DllImportSearchPath.AssemblyDirectory)); } + if (TestLibrary.Utilities.IsNativeAot) + { + // For NativeAOT, validate the case where the native library is next to the AOT application. + // The passing of DllImportSearchPath.System32 is done to ensure on Windows the runtime won't fallback + // and try to search the application directory by default. + string libNameAot = $"{NativeLibraryToLoad.Name}-in-native"; + EXPECT(LoadLibrary_WithAssembly(libNameAot, assembly, DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)); + EXPECT(TryLoadLibrary_WithAssembly(libNameAot, assembly, DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)); + } + if (OperatingSystem.IsWindows()) { string currentDirectory = Environment.CurrentDirectory; diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj index 16e89dfaba405..a70c006283c4f 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj @@ -24,16 +24,24 @@ - - + + - + - + - + + + + + + + + + From 29f4df8887ea64e70155e66f52fe5617f8b8c3cd Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 7 Aug 2023 16:27:32 -0700 Subject: [PATCH 3/5] Add loadWithAlteredPathFlags to lookup. --- .../System/Runtime/InteropServices/NativeLibrary.NativeAot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs index 2fcbbd704f626..3abe04a60385e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs @@ -98,7 +98,7 @@ internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssembl { // Try to load the module alongside the assembly where the PInvoke was declared. // For PInvokes where the DllImportSearchPath.AssemblyDirectory is specified, look next to the application. - ret = LoadLibraryHelper(Path.Combine(AppContext.BaseDirectory, currLibNameVariation), dllImportSearchPathFlags, ref errorTracker); + ret = LoadLibraryHelper(Path.Combine(AppContext.BaseDirectory, currLibNameVariation), loadWithAlteredPathFlags | dllImportSearchPathFlags, ref errorTracker); if (ret != IntPtr.Zero) { return ret; From 014e8c7f221ec5059b40209c09eb59781fb868b5 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 7 Aug 2023 17:43:46 -0700 Subject: [PATCH 4/5] Review feedback --- .../NativeLibrary.NativeAot.Windows.cs | 4 ++-- .../DllImportSearchPathsTest.csproj | 16 ++++++++-------- .../NativeLibrary/API/NativeLibraryTests.csproj | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs index ae25ffa458d3e..bac99a01ed35d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs @@ -24,7 +24,7 @@ private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadL goto exit; } - int lastError = Marshal.GetLastWin32Error(); + int lastError = Marshal.GetLastPInvokeError(); if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER) { errorTracker.TrackErrorCode(lastError); @@ -35,7 +35,7 @@ private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadL hmod = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, flags & 0xFF); if (hmod == IntPtr.Zero) { - errorTracker.TrackErrorCode(Marshal.GetLastWin32Error()); + errorTracker.TrackErrorCode(Marshal.GetLastPInvokeError()); } exit: diff --git a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj index 9a376d8102918..02284d1ade536 100644 --- a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj +++ b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj @@ -14,24 +14,24 @@ - - + + - + - + - + - - + + - + diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj index a70c006283c4f..fdc11a4e11219 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj @@ -24,24 +24,24 @@ - - + + - + - + - + - - + + - + From ca8d389c2097f78f42c52037d0bcc50b590e529e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 7 Aug 2023 19:29:47 -0700 Subject: [PATCH 5/5] Review feedback --- .../DllImportSearchPaths/DllImportSearchPathsTest.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs index add031eca38b1..644bb79ac54bb 100644 --- a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs +++ b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs @@ -39,9 +39,7 @@ public static void AssemblyDirectory_Found() Assert.Equal(3, sum); } - public static bool IsNativeAot => TestLibrary.Utilities.IsNativeAot; - - [ConditionalFact(nameof(IsNativeAot))] + [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))] public static void AssemblyDirectoryAot_Found() { int sum = NativeLibraryPInvokeAot.Sum(1, 2); @@ -87,6 +85,9 @@ public static int Sum(int a, int b) return NativeSum(a, b); } + // For NativeAOT, validate the case where the native library is next to the AOT application. + // The passing of DllImportSearchPath.System32 is done to ensure on Windows the runtime won't fallback + // and try to search the application directory by default. [DllImport(NativeLibraryToLoad.Name + "-in-native")] [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)] static extern int NativeSum(int arg1, int arg2);