I have a native DLL which comes in both 32 bit and 64 bit versions (x86). I want to create a wrapper which works on both architectures (Any CPU) and loads the correct version of the DLL depending on the current environment (32 Bit or 64 Bit, at runtime!). This process should happen automatically, so that the users of my DLL do not need to target a specific architecture.
Are there any best practices on how to do that? Any examples that could guide me?
I found one possible solution that uses managed proxies for each architecture and then uses the Assembly.Resolve
event to load the correct version. However this requires me to have 3 managed assemblies in addition to the 2 unmanaged libraries, which seems a bit overkill.
Is there any other solution?
Open the .exe file using Notepad to check its headersThe letter that follows the PE header tells you if the file is 32-bit or 64-bit. 32-bit (x86) programs would have PE L as the header. 64-bit (x64) programs would have PE d† as the header.
On 64-bit Windows, a 64-bit process cannot load a 32-bit dynamic-link library (DLL).
Native DLL's are usually DLL's containing raw processor directly-executable code (such as that found in the Win32 API's) as opposed to, for example, managed (MSIL) which contain code that is consumed and JIT compiled to native processor instructions by a runtime such as the . NET CLR. In .
Here is the solution I've used on many projects:
Here is how I declare P/Invoke methods:
[DllImport("MyAssembly.Native.x86.dll", EntryPoint = "MyTest")]
private static extern void MyTest86(MyType myArg);
[DllImport("MyAssembly.Native.x64.dll", EntryPoint = "MyTest")]
private static extern void MyTest64(MyType myArg);
And here is the corresponding 'MyTest' function which is the one I'll always use (the others are here just for correct bitness binding). It has the same signature than the other P/Invoke ones:
public static void MyTest(MyType myArg)
{
if (IntPtr.Size == 8)
{
MyTest64(myArg);
return;
}
MyTest86(myArg);
}
The advantages are:
The inconveniences are:
The way I do it is to p/invoke a call to LoadLibrary
before calling any of the p/invokes to the library.
LoadLibrary
to load it passing the full path to the DLL.This relies on the unmanaged DLL having the same name for both 32 and 64 bit. If that's not the case then you are in trouble. In that scenario you may need to bind explicitly to the DLL by p/invoking GetProcAddress
. This is no fun at all. Or you implement the sort of scaffolding that Simon describes in his answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With