在C#DllImport中使用32位或64位DLL
这是情况,我在我的dot.net应用程序中使用基于C的dll。 有2个DLL,一个是32位的叫MyDll32.dll,另一个是64位的叫MyDll64.dll的版本。
有一个静态variables包含DLL文件名:stringDLL_FILE_NAME。
并以如下方式使用:
[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")] private static extern int is_Func1(int var1, int var2);
迄今为止简单。
你可以想象,这个软件是在“Any CPU”打开的情况下编译的。
我也有以下代码来确定系统是否应该使用64位文件或32位文件。
#if WIN64 public const string DLL_FILE_NAME = "MyDll64.dll"; #else public const string DLL_FILE_NAME = "MyDll32.dll"; #endif
现在你应该看到这个问题了.DLL_FILE_NAME是在编译时定义的,而不是在执行时间,所以根据执行上下文没有加载正确的dll。
什么才是解决这个问题的正确方法? 我不想要两个执行文件(一个用于32位,另一个用于64位)? 如何在DllImport语句中使用DLL_FILE_NAME 之前设置它?
我发现最简单的方法是导入两个不同名称的方法,并调用正确的方法。 在调用之前,DLL不会被加载,所以很好:
[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); }
当然,如果你有很多import产品,手工维护可能会变得非常麻烦。
这是另一个替代scheme,要求两个DLL具有相同的名称并放置在不同的文件夹中。 例如:
-
win32/MyDll.dll
-
win64/MyDll.dll
诀窍是在CLR执行之前手动加载LoadLibrary
的DLL。 它会看到一个MyDll.dll
已经被加载并使用它。
这可以在父类的静态构造函数中轻松完成。
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); }
编辑2017/02/01 :使用Assembly.CodeBase
以便它即使启用阴影复制时工作。
在这种情况下,我应该这样做(使2个文件夹,x64和x86 +把相应的DLL,同名),在这两个文件夹中:
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); }
有一个静态variables包含DLL文件名
这不是一个静态variables。 在编译时这是一个常量。 您不能在运行时更改编译时间常量。
什么才是解决这个问题的正确方法?
老实说,我会build议只针对x86和忘记所有64位版本,让你的应用程序运行在WOW64,除非你的应用程序有一个迫切的需要作为x64运行。
如果需要x64,您可以:
-
更改DLL具有相同的名称,如
MyDll.dll
,并在安装/部署时,把正确的位置。 (如果操作系统是x64,请部署64位版本的DLL,否则为x86版本)。 -
有两个单独的版本,一个用于x86,另一个用于x64。
你所描述的被称为“并排汇编”(同一个程序集的两个版本,一个32和另一个64位)…我想你会发现这些帮助:
- 使用并行程序集加载DLL的x64或x32版本
- http://blogs.msdn.com/b/gauravseth/archive/2006/03/07/545104.aspx
- http://www.thescarms.com/dotnet/Assembly.aspx
在这里你可以find你的场景(.NET DLLdynamic链接C ++ / CLI DLL引用本地DLL)的演练。
build议:
只要将它构build为x86并完成它…或者有2个构build(一个x86和一个x64)…因为上述技术相当复杂…
另一种方法可能是
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); }
我已经使用了vcsjones提到的方法之一:
“更改DLL具有相同的名称,如MyDll.dll,并在安装/部署时,把正确的位置。
这种方法需要维护两个构build平台,但请参阅以下链接以获取更多详细信息: https : //stackoverflow.com/a/6446638/38368