How to select the path for DllImport at run-time when the name of the required DLL can change?
Asked Answered
P

1

1

I have asked this questions today, but it was falsely closed and I have no option to re-open it. Therefore, I have to ask it again.

I have a class that has a long list of definitions like these:

[DllImport(NativeLibraryName, EntryPoint = "FunctionName", CallingConvention = CallingConvention.Cdecl)]
public static extern void FunctionName();

The parameter "NativeLibraryName" is set by a compiler switch. However, I would like to set this parameter at run-time. My problem is that there are two different DLLs I need to consider: one is for 32-bit systems and one is for 64-bit systems and they are different in name. What I would like to achieve is to determine whether the application is run with 64- or 32-bit and use the proper DLL. I need to load the correct DLL or otherwise I run into BadImageFormatExceptions.

Switching to Delegates

I could use Reflection to generate a file that does the same as the file that is given using delegates like this. Here, I could provide the path to the correct DLL at run-time.

Renaming DLLs

I could also rename the two DLLs and have them the same name, but put them in different directories. I could then select the folder I want to load the DLL from.

Both options would work fine, but the project I am working on is a fork of an existing project and I want to change as little as possible so that I can easily merge updates from the original project into mine.

I am glad for any of your ideas.

This thread was falsely suggested as solution to my problem and the original thread was closed after that. This thread talks about a similar problem, but it does not provide a solution that is applicable to my problem. In fact, presents actually one of the solutions I came up with myself, but I am looking for better ways to do it.

Pendley answered 13/11, 2021 at 21:57 Comment(1)
Does this answer your question? How can I specify a [DllImport] path at runtime?Colcannon
R
7

An easy way is to ensure that the DLLs have the same file name (e.g. MyNativeLibrary.dll) and store them in separate sub-folders, e.g.:

  • x86\MyNativeLibrary.dll for the 32-bit DLL
  • x64\MyNativeLibrary.dll for the 64-bit DLL

When declaring your P/Invoke methods, you use the name of the library without specifying a hard-coded path e.g.:

public static class MyUtilityClass
{
    [DllImport("MyNativeLibrary.dll")]
    public static extern int DoSomething(int x, int y);
}

Finally, at the start if your application use NativeLibrary.Load(string) passing in the full path of the DLL you wish to load. This has to be done before attempting to call any function of your DLL.

// Pseudo-code. You might need to adapt to find the path where your DLLs are located

// Get the folder path where the current `App.exe` is located
var startupPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName);

// Assume the DLL is in a sub-folder of the folder where `App.exe` is located
var myLibraryFullPath = Environment.Is64BitProcess
    ? Path.Combine(startupPath, @"x64\MyNativeLibrary.dll")
    : Path.Combine(startupPath, @"x86\MyNativeLibrary.dll");

// Load the appropriate DLL into the current process
NativeLibrary.Load(myLibraryFullPath);

Once an unmanaged DLL is loaded into the process, any future P/Invokes decorated with [DllImport("MyNativeLibrary.dll")] will bind to the already-loaded DLL, so that's why it works.

Roslynrosmarin answered 13/11, 2021 at 22:35 Comment(2)
This was the same base idea I mentioned in my thread with "Renaming DLLs". However, you also provided a nice clean implementation. I was experimenting with generating delegates, but your solution is way cleaner. All I have to do is rename a given DLL whenever the project I forked gets a new update with a new DLL.Pendley
@c-augusto-proiete Does this approach work also for multi-platform (Windows, WSL/Ubuntu) native libraries, instead of the x32, x64 native libraries from your example?Singletree

© 2022 - 2024 — McMap. All rights reserved.