Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support DllImportSearchPath.AssemblyDirectory for NativeAOT applications #90120

Merged
merged 5 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,35 @@ 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);
jkotas marked this conversation as resolved.
Show resolved Hide resolved
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();
int lastError = Marshal.GetLastPInvokeError();
if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER)
{
errorTracker.TrackErrorCode(lastError);
return hmod;
goto exit;
}
}

hmod = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, flags & 0xFF);
if (hmod == IntPtr.Zero)
{
errorTracker.TrackErrorCode(Marshal.GetLastWin32Error());
errorTracker.TrackErrorCode(Marshal.GetLastPInvokeError());
}

exit:
if (set)
{
Interop.Kernel32.SetThreadErrorMode(prev, out _);
}

return hmod;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
ret = LoadLibraryHelper(Path.Combine(AppContext.BaseDirectory, currLibNameVariation), loadWithAlteredPathFlags | dllImportSearchPathFlags, ref errorTracker);
if (ret != IntPtr.Zero)
{
return ret;
}
}

ret = LoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, ref errorTracker);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
22 changes: 22 additions & 0 deletions src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ public static void AssemblyDirectory_Found()
Assert.Equal(3, sum);
}

[ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.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()
Expand Down Expand Up @@ -70,3 +77,18 @@ 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);
}

// 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)]
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
static extern int NativeSum(int arg1, int arg2);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,24 @@

<Target Name="SetUpSubdirectoryNative" AfterTargets="CopyNativeProjectBinaries">
<ItemGroup>
<_FilesToMove Include="$(OutDir)/libNativeLibrary.*" />
<_FilesToMove Include="$(OutDir)/NativeLibrary.*" />
<NativeLibrariesToMove Include="$(OutDir)/libNativeLibrary.*" />
<NativeLibrariesToMove Include="$(OutDir)/NativeLibrary.*" />
</ItemGroup>
<Move SourceFiles="@(_FilesToMove)" DestinationFiles="@(_FilesToMove -> '$(LibrarySubdirectory)/%(Filename)%(Extension)')"/>
<Move SourceFiles="@(NativeLibrariesToMove)" DestinationFiles="@(NativeLibrariesToMove -> '$(LibrarySubdirectory)/%(Filename)%(Extension)')"/>
</Target>

<Target Name="SetUpSubdirectoryManaged" AfterTargets="Build">
<ItemGroup>
<_FilesToCopy Include="$(OutDir)/$(TargetName).dll" />
<AssembliesToCopy Include="$(OutDir)/$(TargetName).dll" />
</ItemGroup>
<Copy SourceFiles="@(_FilesToCopy)" DestinationFiles="@(_FilesToCopy -> '$(LibrarySubdirectory)/%(Filename)%(Extension)')"/>
<Copy SourceFiles="@(AssembliesToCopy)" DestinationFiles="@(AssembliesToCopy -> '$(LibrarySubdirectory)/%(Filename)%(Extension)')"/>
</Target>

<Target Name="SetUpAOTDirectory" Condition="'$(TestBuildMode)' == 'nativeaot'" AfterTargets="Build">
<ItemGroup>
<NativeLibrariesToCopy Include="$(LibrarySubdirectory)/libNativeLibrary.*" />
<NativeLibrariesToCopy Include="$(LibrarySubdirectory)/NativeLibrary.*" />
</ItemGroup>
<Copy SourceFiles="@(NativeLibrariesToCopy)" DestinationFiles="@(NativeLibrariesToCopy -> '$(NativeOutputPath)/%(Filename)-in-native%(Extension)')" />
</Target>
</Project>
10 changes: 10 additions & 0 deletions src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 11 additions & 3 deletions src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@

<Target Name="SetUpSubdirectoryNative" AfterTargets="CopyNativeProjectBinaries">
<ItemGroup>
<AssembliesToCopy Include="$(OutDir)/libNativeLibrary.*" />
<AssembliesToCopy Include="$(OutDir)/NativeLibrary.*" />
<NativeLibrariesToCopy Include="$(OutDir)/libNativeLibrary.*" />
<NativeLibrariesToCopy Include="$(OutDir)/NativeLibrary.*" />
</ItemGroup>
<Copy SourceFiles="@(AssembliesToCopy)" DestinationFiles="@(AssembliesToCopy -> '$(LibrarySubdirectory)/%(Filename)$(FileNameSuffix)%(Extension)')" />
<Copy SourceFiles="@(NativeLibrariesToCopy)" DestinationFiles="@(NativeLibrariesToCopy -> '$(LibrarySubdirectory)/%(Filename)$(FileNameSuffix)%(Extension)')" />
</Target>

<Target Name="SetUpSubdirectoryManaged" AfterTargets="Build">
Expand All @@ -36,4 +36,12 @@
</ItemGroup>
<Copy SourceFiles="@(AssembliesToCopy)" DestinationFiles="@(AssembliesToCopy -> '$(LibrarySubdirectory)/%(Filename)$(FileNameSuffix)%(Extension)')" />
</Target>

<Target Name="SetUpAOTDirectory" Condition="'$(TestBuildMode)' == 'nativeaot'" AfterTargets="Build">
<ItemGroup>
<NativeLibrariesToCopyAOT Include="$(OutDir)/libNativeLibrary.*" />
<NativeLibrariesToCopyAOT Include="$(OutDir)/NativeLibrary.*" />
</ItemGroup>
<Copy SourceFiles="@(NativeLibrariesToCopyAOT)" DestinationFiles="@(NativeLibrariesToCopyAOT -> '$(NativeOutputPath)/%(Filename)-in-native%(Extension)')" />
</Target>
</Project>
3 changes: 0 additions & 3 deletions src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -778,9 +778,6 @@
<ExcludeList Include="$(XunitTestBinBase)/Interop/MarshalAPI/FunctionPointer/FunctionPtrTest/*">
<Issue>https://github.com/dotnet/runtimelab/issues/164</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/NativeLibrary/API/NativeLibraryTests/*">
<Issue>https://github.com/dotnet/runtimelab/issues/165</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/NativeLibrary/AssemblyLoadContext/ResolveUnmanagedDllTests/*">
<Issue>https://github.com/dotnet/runtimelab/issues/165</Issue>
</ExcludeList>
Expand Down
Loading