Specify the search path for DllImport in .NET
Asked Answered
T

4

59

Is there a way to specify the paths to be searched for a given assembly that is imported with DllImport?

[DllImport("MyDll.dll")]
static extern void Func();

This will search for the dll in the app dir and in the PATH environment variable. But at times the dll will be placed elsewhere. Can this information be specified in app.config or manifest file to avoid dynamic loading and dynamic invocation?

Televisor answered 19/5, 2010 at 10:34 Comment(0)
P
71

Call SetDllDirectory with your additional DLL paths before you call into the imported function for the first time.

P/Invoke signature:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);

To set more than one additional DLL search path, modify the PATH environment variable, e.g.:

static void AddEnvironmentPaths(string[] paths)
{
    string path = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
    path += ";" + string.Join(";", paths);

    Environment.SetEnvironmentVariable("PATH", path);
}

There's more info about the DLL search order here on MSDN.


Updated 2013/07/30:

Updated version of the above using Path.PathSeparator:

static void AddEnvironmentPaths(IEnumerable<string> paths)
{
    var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty };

    string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths));

    Environment.SetEnvironmentVariable("PATH", newPath);
}
Pricking answered 19/5, 2010 at 10:39 Comment(7)
Thanks, works great after invoking SetDllDirectory from static constructor.Televisor
AFAIK, unfortunatelly this does not solve the issue of multiple additional search directories.Televisor
@Stefan, see my revised answer for adding multiple directories.Pricking
Thanks. I think that's the best we can get.Televisor
You should better use Path.PathSeparator Chemosphere
Yeah, Path.PathSeparator plus the PATH environment variable worked GREAT!Mcmahon
I added a Path.PathSeparator version to my answer.Pricking
B
15

Try calling AddDllDirectory with your additional DLL paths before you call into the imported function for the first time.

If your Windows version is lower than 8 you will need to install this patch, which extends the API with the missing AddDllDirectory function for Windows 7, 2008 R2, 2008 and Vista (there is no patch for XP, though).

Beitz answered 29/5, 2012 at 13:35 Comment(3)
Sounds like a good idea, but what should the DllImport look like?Digression
This is a much better solution because, unlike the accepted answer, this API will work well also when the app is running as Microsoft Store app (UWP or Packaged Win32 app as UWP). The accepted answer will fail for the Microsoft Store app because the OS blocks the list of the current environment to UWP apps. So DllImport will fail to see the path with the dll. To use it in C#, you can copy import this API this way: [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int AddDllDirectory(string lpPathName);Sibylle
The cookie is declared as typedef PVOID DLL_DIRECTORY_COOKIE, *PDLL_DIRECTORY_COOKIE; in .\um\libloaderapi.h so the return type is nint not int.Papp
P
7

This might be useful DefaultDllImportSearchPathsAttribute Class
E.g.

[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]

Also note you can use AddDllDirectory as well so you aren't screwing up anything already there:

[StructLayout(LayoutKind.Sequential)]
struct DllDirectoryCookie { private nint value; }
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern DllDirectoryCookie AddDllDirectory(string path);

And clean up after you:

[StructLayout(LayoutKind.Sequential)]
struct BOOL { int value; /*...*/ operator bool(BOOL x) => x.value != 0; }
[DllImport("kernel32.dll", SetLastError = true)]
static extern BOOL RemoveDllDirectory(DllDirectoryCookie cookie);
Papp answered 29/7, 2017 at 2:55 Comment(4)
can you show where to place the [assembly:... attribute ?Paginal
I believe it can be anywhere. It applies to the assembly anyway (globally to the .dll). The convention is to put it in the .\Properties\AssemblyInfo.cs file where . is the project directory. It might have to be outside any namespace declaration or that is just where it has always been when I encountered it or wrote it myself.Papp
AddDllDirectory returns a special cookie value meant to passed to RemoveDllDirectory, not a bool.Bland
Updated the P/Invoke declaration; also added RemoveDllDirectory.Papp
D
0

I think that the question, even older is still quite relenant however the framework evolved at the moment, perhaps the "best" way to do this is by using the framework itself. any one who is interested, I recommend using `DefaultDllImportSearchPaths attributes on any native/external library. Here is a sample:

[DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int NetServerGetInfo(string serverName, int level, out IntPtr pSERVER_INFO_XXX);
Dorise answered 9/1, 2023 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.