根据 x64/x86 更改 C# DllImport 目标代码

19

我有一个外部的C++ dll 文件需要使用 DLLImport 导入。如果我的应用程序是在 x64 编译下,我需要导入 x64 版本的 dll,如果是 x86 构建,则需要 x86 dll。

最好的方法是什么?

理想情况下,我希望使用一些预处理指令,但我知道这在 C# 中不起作用?

更多信息:该 DLL 是由设置为 AnyCPU 的项目导入的。父项目决定应用程序是以 x64 还是 x86 编译。我们为不同的客户编译两个版本-我希望在这两个版本中共享子项目。


1
导入两个版本(私有方法),但根据环境向客户端代码公开正确的版本怎么样?使用.NET 4只需检查Environment.Is64BitOperatingSystem。请注意,我不会因为依赖本机DLL而保留两个不同版本的C#应用程序(因此我不会为此使用预处理器)。 - Adriano Repetti
Michael - 这几乎是我的问题,但我有一个额外的复杂性,这意味着他们的解决方案行不通。我的dll被一个anycpu项目导入,而父项目决定应用程序是x64还是x86。 - Sugrue
1
@ Sugrue那么您需要使用运行时解决方案,即导入两个并使用Environment.Is64BitProcess,或sizeof(void*),或IntPtr.Size - Michael Graczyk
可能是基于平台导入的C#预处理器指令的重复问题。 - Cœur
2个回答

30

这主要是一个部署问题,只需让您的安装程序根据目标机器上的Windows版本复制正确的DLL即可。

但是没有人喜欢这样做。动态地调用正确的DLL的函数非常痛苦,您必须为每个导出函数编写委托类型,并使用LoadLibrary + GetProcAddress + Marshal.GetDelegateForFunctionPointer来创建委托对象。

但是没有人喜欢这样做。更简单的方法是声明该函数两次,分别赋予不同的名称,并在[DllImport]属性中使用EntryPoint属性指定实际名称。然后在运行时测试您想要调用的内容。

但是没有人喜欢这样做。最有效的技巧是引导Windows为您加载正确的DLL。首先,您需要将DLL复制到Windows不会查找的目录中。最好的方法是在构建目录中创建“x86”和“x64”子目录,并将适当的DLL复制到每个目录中。通过编写一个后置生成事件来完成此操作,以创建目录并复制DLL。

然后通过pinvoke SetDllDirectory()告诉Windows。您指定的路径将被添加到Windows搜索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();
        //etc..
    }
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetDllDirectory(string path);
}

请考虑在64位模式下运行代码是否真正有用。得益于其庞大的虚拟内存地址空间,这种情况非常罕见,且是唯一的实际收益。您仍然需要支持32位版本,在2GB情况下能够正常运行。


1
个人而言,我更喜欢显式调用 LoadLibrary 并传递完整路径,而不是修改 DLL 搜索路径,因为后者总让我感觉很笨拙。当然,基本思想是相同的。 - David Heffernan
LoadLibrary被第二段覆盖。这个想法不好。 - Hans Passant
@HansPassant 不完全是这样的:你可以声明普通的 [DllImport("myLibrary.dll"] extern static void Method1(); 并使用带有完整路径的 LoadLibrary,例如 LoadLibrary("c:\temp\amd64\myLibrary.dll"); 在调用 Method1() pinvoke 之前,Windows 将使用已经加载的 myLibrary.dll。 - wech

5
将x86和x86_64 DLL导入分别命名,然后可以在运行时根据系统架构条件性地调用它们,通过检查Environment.Is64BitProcess的值(如果使用 < .Net 4则使用IntPtr.size)。无论项目是作为x86、x86_64还是AnyCPU构建,这种方法都可以生效。
另一种方法是设置两个不同的构建配置,一个仅支持x86,一个仅支持x86_64,给每个配置指定一个条件编译符号,并在自定义符号上使用#ifdef。

我认为楼主不知道如何使用#ifdef,你能为他提供一个快速的代码示例,以及加载库的代码示例吗?尽管没有示例,但我仍然给了一个+1。 - Scott Chamberlain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接