Is there a way to have the particular DLL referenced by a P/Invoke (DllImport) signature depend on the CPU architecture?
I\'m working on an application that loads a
There is no way to have a single PInvoke signature and get the behavior you want. The attribute is burned into metadata and must have constant values. One hack you could do though is to have multiple methods.
public static class NativeMethods32 {
[DllImport("Foo.dll")]
public static extern int SomeMethod();
}
public static class NativeMethods64 {
[DllImport("Foo_x864.dll")]
public static extern int SomeMethod();
}
public static class NativeMethods {
public static bool Is32Bit { return 4 == IntPtr.Size; }
public static SomeMethod() {
return Is32Bit ?
NativeMethods32.SomeMethod();
NativeMethods64.SomeMethod();
}
}
However this is not the preferred approach. An easier approach would be to make the DLL have the same name on multiple platforms and create a platform agnostic PInvoke signature. This is the approach most / all windows libraries take.
I developed a special library for the target: InteropDotNet. It introduces new RuntimeDllImport
attribute with dynamical library path resolving (on the fly). In the default way, you can write
[RuntimeDllImport("NativeLib",
CallingConvention = CallingConvention.Cdecl, EntryPoint = "sum")]
int Sum(int a, int b);
And the library will be resolved depends on environment. For example, paths for Win/Linux, x86/x64:
x86/NativeLib.dll
x86/libNativeLib.so
x64/NativeLib.dll
x64/libNativeLib.so
"If instead of different DLL names it was the same name in different folders, does that open any other options?"
Maybe this would work for you:
public static class NativeMethods
{
// here we just use "Foo" and at runtime we load "Foo.dll" dynamically
// from any path on disk depending on the logic you want to implement
[DllImport("Foo", EntryPoint = "bar")]
private void bar();
[DllImport("kernel32")]
private unsafe static extern void* LoadLibrary(string dllname);
[DllImport("kernel32")]
private unsafe static extern void FreeLibrary(void* handle);
private sealed unsafe class LibraryUnloader
{
internal LibraryUnloader(void* handle)
{
this.handle = handle;
}
~LibraryUnloader()
{
if (handle != null)
FreeLibrary(handle);
}
private void* handle;
} // LibraryUnloader
private static readonly LibraryUnloader unloader;
static NativeMethods()
{
string path;
if (IntPtr.Size == 4)
path = "path/to/the/32/bit/Foo.dll";
else
path = "path/to/the/64/bit/Foo.dll";
unsafe
{
void* handle = LoadLibrary(path);
if (handle == null)
throw new DllNotFoundException("unable to find the native Foo library: " + path);
unloader = new LibraryUnloader(handle);
}
}
}
It consists in explicitly loading the native library with its full path before P/Invoke itself tries to load it.
What do you think?