9

DllImport在我的解决方案中使用。
我的问题是我有两个版本的同一个 DLL,一个是为 32 位构建的,另一个是为 64 位构建的。

它们都公开了具有相同名称和相同签名的相同功能。我的问题是我必须使用两种静态方法来公开这些方法,然后在运行时使用IntPtrsize 来确定要调用的正确方法。

private static class Ccf_32
{
    [DllImport(myDllName32)]
    public static extern int func1();
}

private static class Ccf_64
{
    [DllImport(myDllName64)]
    public static extern int func1();
}

我必须这样做,因为myDllName32并且myDllName64必须保持不变,而且我还没有找到在运行时设置它的方法。

有没有人对此有一个优雅的解决方案,所以我可以摆脱代码重复和恒定IntPtr大小检查。

如果我可以设置文件名,我只需要检查一次,就可以摆脱大量重复的代码。

4

9 回答 9

20

我更喜欢通过使用来自 kernel32.dll 的LoadLibrary调用来强制特定 DLL 从特定路径加载来执行此操作。

如果您将 32 位和 64 位 DLL 命名为相同但将它们放在不同的路径中,则可以使用以下代码根据您正在运行的 Windows 版本加载正确的。您需要做的就是在引用任何引用ccf类的代码之前调用ExampleDllLoader.LoadDll() :

private static class ccf
{
    [DllImport("myDllName")]
    public static extern int func1();
}

public static class ExampleDllLoader
{
    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    private extern static IntPtr LoadLibrary(string librayName);

    public static void LoadDll()
    {
        String path;

        //IntPtr.Size will be 4 in 32-bit processes, 8 in 64-bit processes 
        if (IntPtr.Size == 4)
            path = "c:/example32bitpath/myDllName.dll";
        else
            path = "c:/example64bitpath/myDllName.dll";

        LoadLibrary(path);
    }
}
于 2009-12-13T14:37:50.440 回答
13

您可能可以使用#if关键字来实现这一点。如果您定义一个名为 的条件编译器符号win32,以下代码将使用 win32 块,如果您删除它,它将使用另一个块:

#if win32
    private static class ccf_32
    {
        [DllImport(myDllName32)]
        public static extern int func1();
    }
#else    
    private static class ccf_64
    {
        [DllImport(myDllName64)]
        public static extern int func1();
    }
#endif

这可能意味着您可以删除您现在拥有的类包装:

    private static class ccf
    {
#if win32
        [DllImport(myDllName32)]
        public static extern int func1();
#else    
        [DllImport(myDllName64)]
        public static extern int func1();
#endif
    }

为方便起见,我想您可以创建构建配置来控制编译符号。

于 2009-08-23T23:28:17.327 回答
10

我知道这是一个非常老的问题(我是新手——回答老问题是不是很糟糕?),但我只需要解决同样的问题。我必须动态引用基于操作系统的 32 位或 64 位 DLL,而我的 .EXE 是为任何 CPU 编译的。

您可以使用 DLLImport,而无需使用 LoadLibrary()。

我通过使用SetDLLDirectory做到了这一点。与名称相反,SetDLLDirectory添加到 DLL 搜索路径,而不是替换整个路径。这允许我在 Win32 和 Win64 子目录中拥有一个同名的 DLL(本次讨论中的“TestDLL.dll”),并适当地调用。

public partial class frmTest : Form
{
    static bool Win32 = Marshal.SizeOf(typeof(IntPtr)) == 4;
    private string DLLPath = Win32 ? @"\Win32" : @"\Win64";

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool SetDllDirectory(string lpPathName);
    [DllImport("TestDLL.dll", SetLastError = true)]
    static extern IntPtr CreateTestWindow();

    private void btnTest_Click(object sender, EventArgs e)
    {
        string dllDir = String.Concat(Directory.GetCurrentDirectory(), DLLPath);
        SetDllDirectory(dllDir);

        IntPtr newWindow = CreateTestWindow();
    }
}
于 2012-05-01T13:28:27.357 回答
2

为什么不将它们包装成一个方法?

private static class ccf_32_64
{
    private static class ccf_32
    {
        [DllImport(myDllName32)]
        private static extern int func1();
    }

    private static class ccf_64
    {
        [DllImport(myDllName64)]
        private static extern int func1();
    }

    public static int func1()
    {
        if (32bit)
        {
            return ccf_32.func1();
        }
        else
        {
            return ccf_64.func1();
        }
    }
}
于 2009-08-23T23:28:31.710 回答
1

另一种选择是让非托管 DLL 的 32 位和 64 位版本具有相同的名称,但将它们放在构建输出中的不同文件夹中(例如,x86\ 和 x64\)。

然后,您的安装程序或您正在分发的其他任何内容都会更新,因此它知道为正在安装的平台安装正确的 DLL。

于 2009-08-24T00:07:52.567 回答
1

您可以创建两个方法并在运行时选择一个,这样您就可以保持Any CPU

public static class Ccf
{
    [DllImport(myDllName32)]
    private static extern int func32();

    [DllImport(myDllName64)]
    private static extern int func64();


    public static int func()
    {
        if(Environment.Is64BitProcess)
        {
            return func64();
        }
        return func32();
    }

}

于 2017-10-26T20:51:16.393 回答
0

嗯,我想知道您是否可以使用基于 32 位和 64 位 dll 的方法创建一个接口,然后创建一个类。

我不确定是否有明确的方法来确定您是否正在运行 64 位,但以下方法可能有效:允许不安全代码并具有获取指向某个地址的指针的不安全函数,然后确定指针是 4 还是大小为 8 个字节。根据结果​​确定要创建接口的哪个实现。

于 2009-11-17T05:26:08.953 回答
0

您可以通过检查 IntPtr 类型(无论如何称为本机 int)的大小来确定您是否正在运行 64Bits。然后您可以使用导入的 LoadLibraryW 调用加载适当的 DLL,使用 GetProcAddress 获取函数指针,然后查看Marshal.GetDelegateForFunctionPointer

这并不像看起来那么复杂。您必须同时 DllImport LoadLibraryW 和 GetProcAddress。

于 2009-11-17T05:36:08.157 回答
0

你不能以你想要的方式做到这一点。您需要将DllImport属性视为在编译时使用的元数据。因此,您无法更改它动态导入的 DLL。

如果您想让您的托管代码以“任何 CPU”为目标,那么您需要将 32 位和 64 位库都导入为两个不同的函数,您可以根据运行时环境调用这些函数,或者使用一些额外的 Win32 API调用以在运行时延迟加载正确版本的非托管程序集,以及额外的 Win32 调用以执行所需的方法。缺点是您不会为任何类型的代码提供编译时支持以实现类型安全等。

于 2009-08-24T00:37:09.117 回答