在c#中以编程方式设置dllimport

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

它们都使用相同的名称和相同的签名来公开相同的function。 我的问题是我必须使用两个静态方法来暴露这些,然后在运行时使用IntPtr大小来确定要调用的正确方法。

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

我必须这样做,因为myDllName32myDllName64必须是常量,我没有找到在运行时设置它的方法。

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

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

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

 #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 } 

为方便起见,我猜你可以创建用于控制编译符号的构建配置。

我更喜欢通过使用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); } } 

我知道这是一个非常古老的问题(我是新问题 – 回答一个老问题是不是很糟糕?),但我只需要解决同样的问题。 我必须动态引用基于操作系统的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(); } } 

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

 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(); } } } 

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

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

您可以创建两个方法并在运行时选择一个方法,这样您就可以保留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(); } 

}

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

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

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

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

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

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