|
| 1 | +--- |
| 2 | +title: "Breaking change - Single-file apps no longer look for native libraries in executable directory" |
| 3 | +description: "Learn about the breaking change in .NET 10 where single-file apps no longer look for native libraries in the executable directory." |
| 4 | +ms.date: 06/25/2025 |
| 5 | +ms.custom: https://github.com/dotnet/docs/issues/46356 |
| 6 | +--- |
| 7 | + |
| 8 | +# Single-file apps no longer look for native libraries in executable directory |
| 9 | + |
| 10 | +Previously, in [single-file .NET applications](../../../deploying/single-file/overview.md), the directory of the single-file executable was added to the `NATIVE_DLL_SEARCH_DIRECTORIES` property during startup. Consequently, .NET always [probed](../../../dependency-loading/default-probing.md#unmanaged-native-library-probing) the application directory when unmanaged libraries were loaded. On non-Windows with [NativeAOT](../../../deploying/native-aot/index.md), the `rpath` was set to the application directory by default, such that it also always looked for native libraries in the application directory. |
| 11 | + |
| 12 | +The application directory is no longer added to `NATIVE_DLL_SEARCH_DIRECTORIES` in single-file apps, and the `rpath` setting has been removed in NativeAOT. In both cases, <xref:System.Runtime.InteropServices.DllImportSearchPath.AssemblyDirectory?displayProperty=nameWithType> (included in the default behaviour for P/Invokes) means the application directory. If you specify that value or leave the default, .NET looks in the application directory. If you specify flags without that value, .NET no longer looks in the application directory. |
| 13 | + |
| 14 | +## Version introduced |
| 15 | + |
| 16 | +.NET 10 Preview 6 |
| 17 | + |
| 18 | +## Previous behavior |
| 19 | + |
| 20 | +Previously, single-file applications always looked in the application directory when loading native libraries. On non-Windows operating systems, NativeAOT applications always looked in the application directory when loading native libraries. |
| 21 | + |
| 22 | +For example, the following P/Invoke looked *in the application directory* for `lib` and loaded it from there if it existed: |
| 23 | + |
| 24 | +```csharp |
| 25 | +[DllImport("lib") |
| 26 | +[DefaultDllImportSearchPaths(DllImportSearchPath.System32)] |
| 27 | +static extern void Method() |
| 28 | +``` |
| 29 | + |
| 30 | +## New behavior |
| 31 | + |
| 32 | +Starting in .NET 10, single-file applications only look in the application directory if the search paths for a native library load indicate including the assembly directory. |
| 33 | + |
| 34 | +```csharp |
| 35 | +// Look in System32 on Windows. |
| 36 | +// Search the OS on non-Windows. |
| 37 | +[DllImport("lib") |
| 38 | +[DefaultDllImportSearchPaths(DllImportSearchPath.System32)] |
| 39 | +static extern void Method() |
| 40 | + |
| 41 | +// Look next to the single-file app because assembly directory |
| 42 | +// means application directory for single-file apps. |
| 43 | +[DllImport("lib") |
| 44 | +[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] |
| 45 | +static extern void Method() |
| 46 | + |
| 47 | +// Look next to the single-file app (because assembly |
| 48 | +// directory is searched by default), then default OS search. |
| 49 | +[DllImport("lib") |
| 50 | +static extern void Method() |
| 51 | +``` |
| 52 | + |
| 53 | +## Type of breaking change |
| 54 | + |
| 55 | +This is a [behavioral change](../../categories.md#behavioral-change). |
| 56 | + |
| 57 | +## Reason for change |
| 58 | + |
| 59 | +The existing behavior (always look in the application directory even if search paths exclude it) has caused confusion. It's also inconsistent with how the search flags are handled in regular (non-single-file, non-NativeAOT) .NET applications. |
| 60 | + |
| 61 | +## Recommended action |
| 62 | + |
| 63 | +If the application/assembly directory is desired for a P/Invoke or native library load and wasn't previously specified, specify <xref:System.Runtime.InteropServices.DllImportSearchPath.AssemblyDirectory?displayProperty=nameWithType>. |
| 64 | + |
| 65 | +If the `RPATH` setting is desired in NativeAOT, explicitly add the corresponding [linker arguments](../../../deploying/native-aot/interop.md#linking) to your project. |
| 66 | + |
| 67 | +## Affected APIs |
| 68 | + |
| 69 | +- P/Invokes |
| 70 | +- <xref:System.Runtime.InteropServices.NativeLibrary.Load*?displayProperty=fullName> |
| 71 | +- <xref:System.Runtime.InteropServices.NativeLibrary.TryLoad*?displayProperty=fullName> |
| 72 | + |
| 73 | +## See also |
| 74 | + |
| 75 | +- [Specifying DllImportSearchPath.AssemblyDirectory only searches the assembly directory](search-assembly-directory.md) |
0 commit comments