Using a 32bit or 64bit dll in C# DllImport
Solution 1
I've found the simplest way to do this is to import the two methods with different names, and calling the right one. The DLL won't be loaded until the call is made so it's fine:
[DllImport("MyDll32.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_32(int var1, int var2);
[DllImport("MyDll64.dll", EntryPoint = "Func1", CallingConvention = CallingConvention.Cdecl)]
private static extern int Func1_64(int var1, int var2);
public static int Func1(int var1, int var2) {
return IntPtr.Size == 8 /* 64bit */ ? Func1_64(var1, var2) : Func1_32(var1, var2);
}
Of course, if you have many imports, this can be become quite cumbersome to maintain manually.
Solution 2
Here is another alternative that requires that the two DLLs have the same name and are placed in different folders. For instance:
win32/MyDll.dll
win64/MyDll.dll
The trick is to manually load the DLL with LoadLibrary
before the CLR does it. It will then see that a MyDll.dll
is already loaded and use it.
This can be done easily in the static constructor of the parent class.
static class MyDll
{
static MyDll()
{
var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath;
var myFolder = Path.GetDirectoryName(myPath);
var is64 = IntPtr.Size == 8;
var subfolder = is64 ? "\\win64\\" : "\\win32\\";
LoadLibrary(myFolder + subfolder + "MyDll.dll");
}
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("MyDll.dll")]
public static extern int MyFunction(int var1, int var2);
}
EDIT 2017/02/01: Use Assembly.CodeBase
so that it works even if Shadow Copying is enabled.
Solution 3
In this case, i should do like this (make 2 folders, x64 and x86 + put the corresponding dll, WITH THE SAME NAME, in both folders):
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
class Program {
static void Main(string[] args) {
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86");
bool ok = SetDllDirectory(path);
if (!ok) throw new System.ComponentModel.Win32Exception();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string path);
}
Solution 4
There is a static variable holding the DLL file name
It is not a static variable. It's a constant, at compile time. You can't change a compile time constant at runtime.
What would be the correct way to deal with this issue?
Honestly I would recommend just targeting x86 and forgetting the 64-bit version all together, and letting your application run on WOW64, unless your application has a compelling need to run as x64.
If there is a need for x64, you could:
Change the DLLs to have the same name, such as
MyDll.dll
, and at install / deploy time, put the right one in place. (If the OS is x64, deploy the 64-bit version of the DLL, otherwise the x86 version).Have two separate builds altogether, one for x86 and one for x64.
Solution 5
an alternative approach may be
public static class Sample
{
public Sample()
{
string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\";
string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll";
if (!File.Exists(ResolvedDomainTimeFileName))
{
if (Environment.Is64BitProcess)
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName);
}
else
{
if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll"))
File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName);
}
}
}
[DllImport("ABCLib__Resolved.dll")]
private static extern bool SomeFunctionName(ref int FT);
}
Gilad
Updated on June 18, 2020Comments
-
Gilad almost 4 years
Here is the situation, I'm using a C based dll in my dot.net application. There are 2 dlls, one is 32bit called MyDll32.dll and the other is a 64bit version called MyDll64.dll.
There is a static variable holding the DLL file name: string DLL_FILE_NAME.
and it is used in the following way:
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")] private static extern int is_Func1(int var1, int var2);
Simple so far.
As you can imagine, the software is compiled with "Any CPU" turned on.
I also have the following code to determine if the system should use the 64bit file or the 32bit file.
#if WIN64 public const string DLL_FILE_NAME = "MyDll64.dll"; #else public const string DLL_FILE_NAME = "MyDll32.dll"; #endif
By now you should see the problem.. DLL_FILE_NAME is defined in compilation time and not in execution time so the right dll isn't loaded according to the execution context.
What would be the correct way to deal with this issue? I do not want two execution files (one for 32bit and the other for 64bit)? How can I set DLL_FILE_NAME before it is used in the DllImport statement?
-
Gilad almost 12 yearsI do not want two applications. That is soooo 90s.
-
Yahia almost 12 years@Gilad then follow the links provided, they show some options that are perhaps more like what you want (BEWARE: it gets really complicated)...
-
Yahia almost 12 years@Gilad BTW you can build 2 (x86 and x64) and when the installer runs it can determine which of both to install - the user gets exactly one application...
-
banging almost 12 yearsThere was no 64bit in the 90's so it couldn't have been the 90's :p
-
Yahia almost 12 years@banging not completely true... both the APLHA and the MIPS architectures had 64 Bit offerings in the 90s... Itanium 64 Bit came 2001 (was developed second half of 90s)...
-
stmax almost 12 yearswhy did microsoft screw this up so badly? now they make sill UI "improvements" in VS like uppercase menu items instead of fixing that crap.
-
Cody Gray almost 12 yearsThis has nothing to do with Visual Studio, and it isn't "screwed up badly". You're just doing it wrong.
-
Cody Gray almost 12 yearsAgreed, this is definitely the simplest solution to the problem. The overwhelming majority of apps, particularly line-of-business apps, derive no benefit from being 64-bit native. Even if there might be some marginal performance improvements, the increased overhead of 64-bit tends to cancel those out. The other suggestion you make about using the same names for the DLLs and deploying the correct version depending on the system architecture is exactly what Microsoft does with system DLLs. That seems like a good bet, too.
-
Cody Gray almost 12 yearsFor maximum readability, I would suggest using the
Environment.Is64BitProcess
property instead of checking the size of theIntPtr
type. Of course, this property was introduced with .NET 4.0, so if you're targeting an older version, it won't be available. -
Gilad almost 12 yearsThis is exactly what I wanted to do. Although there is some overhead in coding, it works and does the job.
-
JanW almost 8 yearsThis is by farmost the most elegant answer, which best fits the question. I successfully adopted this for the FreeImage project .NET Wrapper! Awesome Idea. I have used
Environment.Is64BitProcess
instead and resolved the Path for the assembly like Kisdeds answer suggests instead. Thanky you very much! -
Matt over 7 yearsYou could also embed both the 32-bit and 64-bit DLLs as resources inside the .NET DLL and extract the right one/call LoadLibrary if you want to have everything packaged up.
-
lindexi about 6 yearsIt should copy the dll make the app spend more time.
-
cahit beyaz about 6 yearsYou may encounter issues if your native dll requires some other libraries.
-
Guavaman almost 5 yearsAnd be prepared to have your users constantly sending you reports about moronic anti-malware programs out there flagging your application as malware simply because of the use of the LoadLibrary function. Been there, done that.
-
vpalmu over 3 yearsI'm impressed that worked for you. I found that usually I need one more layer of indirection between the decision and the call.