在C#DllImport中使用32位或64位dll

Gil*_*lad 59 .net c# pinvoke dllimport 32bit-64bit

这是情况,我在我的dot.net应用程序中使用基于C的dll.有2个dll,一个是32位称为MyDll32.dll,另一个是64位版本,名为MyDll64.dll.

有一个保存DLL文件名的静态变量:string DLL_FILE_NAME.

它以下列方式使用:

[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);
Run Code Online (Sandbox Code Playgroud)

到目前为止简单.

可以想象,该软件是在打开"Any CPU"的情况下编译的.

我还有以下代码来确定系统是否应该使用64位文件或32位文件.

#if WIN64
        public const string DLL_FILE_NAME = "MyDll64.dll";
#else
        public const string DLL_FILE_NAME = "MyDll32.dll";        
#endif
Run Code Online (Sandbox Code Playgroud)

到目前为止,您应该看到问题.. DLL_FILE_NAME是在编译时定义的,而不是在执行时间中定义的,因此根据执行上下文不会加载右dll.

处理这个问题的正确方法是什么?我不想要两个执行文件(一个用于32位,另一个用于64位)?如何在DllImport语句中使用DLL_FILE_NAME 之前设置它?

Jul*_*ain 56

我发现最简单的方法是使用不同的名称导入两个方法,并调用正确的方法.在调用之前不会加载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);
}
Run Code Online (Sandbox Code Playgroud)

当然,如果你有很多进口,手动维护可能会非常麻烦.

  • 为了最大限度地提高可读性,我建议使用[`Environment.Is64BitProcess`属性](http://msdn.microsoft.com/en-us/library/system.environment.is64bitprocess)而不是检查`IntPtr`的大小.类型.当然,这个属性是在.NET 4.0中引入的,所以如果你的目标是旧版本,它将无法使用. (30认同)

Ben*_*hon 54

这是另一种替代方案,要求两个DLL具有相同的名称并放在不同的文件夹中.例如:

  • win32/MyDll.dll
  • win64/MyDll.dll

诀窍是LoadLibrary在CLR执行之前手动加载DLL .然后它会看到a 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);
}
Run Code Online (Sandbox Code Playgroud)

编辑2017/02/01:使用Assembly.CodeBase即使启用了Shadow Copying也能正常工作.

  • 这是迄今为止最优雅的答案,最适合这个问题。我成功地将它用于 FreeImage 项目 .NET Wrapper!真棒的想法。我改用了“Environment.Is64BitProcess”,并像 Kisdeds 的回答建议的那样解决了程序集的路径。非常感谢! (3认同)

小智 15

在这种情况下,我应该这样做(制作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);
}
Run Code Online (Sandbox Code Playgroud)


vcs*_*nes 8

有一个保存DLL文件名的静态变量

它不是静态变量.在编译时它是一个常量.您无法在运行时更改编译时常量.

处理这个问题的正确方法是什么?

老实说,我建议只针对x86并忘记64位版本,让你的应用程序在WOW64上运行,除非你的应用程序迫切需要以x64运行.

如果需要x64,您可以:

  • 将DLL更改为具有相同的名称,例如MyDll.dll,在安装/部署时,将正确的DLL 放在适当的位置.(如果操作系统是x64,则部署64位版本的DLL,否则部署x86版本).

  • 有两个单独的版本,一个用于x86,另一个用于x64.