CPU 体系结构独立 P/Invoke:DllName 或路径可以是“动态的”吗?
Posted
技术标签:
【中文标题】CPU 体系结构独立 P/Invoke:DllName 或路径可以是“动态的”吗?【英文标题】:CPU Architecture Independent P/Invoke: Can the DllName or path be "dynamic"? 【发布时间】:2010-12-07 02:53:20 【问题描述】:有没有办法让 P/Invoke (DllImport) 签名引用的特定 DLL 取决于 CPU 架构?
我正在开发一个应用程序,该应用程序从第三方供应商的本机 dll 加载大量方法签名,在本例中是用户空间接口 DLL 到一个硬件。该供应商现在已经开始提供 DLL 的 x86 和 x64 版本,我认为我的应用程序将受益于作为 64 位进程运行。除了这个 DLL 之外,一切都是 .NET 代码,因此构建为“任何 CPU”都可以。
本机 DLL 中的所有方法签名在 64 位上都是相同的,但是 DLL 的名称不同(Foo.dll 与 Foo_x64.dll)。有什么方法可以通过 P/Invoke 签名或 app.config 条目让我根据正在运行的 CPU 架构选择要加载的 DLL?
如果不是不同的 DLL 名称而是不同文件夹中的相同名称,是否会打开任何其他选项?
注意:因为此用户空间 DLL 的版本必须与已安装的硬件内核驱动程序相匹配,所以 DLL 不与我们的应用程序捆绑在一起,而是依赖供应商安装程序将其放置在目录中在 %PATH% 中。
【问题讨论】:
***.com/questions/23215518/…的可能重复 我认为重复是相反的,因为这个问题比那个问题早四年:) 【参考方案1】:“如果不是不同的 DLL 名称而是不同文件夹中的相同名称,是否会打开任何其他选项?”
也许这对你有用:
public static class NativeMethods
// here we just use "Foo" and at runtime we load "Foo.dll" dynamically
// from any path on disk depending on the logic you want to implement
[DllImport("Foo", EntryPoint = "bar")]
private void bar();
[DllImport("kernel32")]
private unsafe static extern void* LoadLibrary(string dllname);
[DllImport("kernel32")]
private unsafe static extern void FreeLibrary(void* handle);
private sealed unsafe class LibraryUnloader
internal LibraryUnloader(void* handle)
this.handle = handle;
~LibraryUnloader()
if (handle != null)
FreeLibrary(handle);
private void* handle;
// LibraryUnloader
private static readonly LibraryUnloader unloader;
static NativeMethods()
string path;
if (IntPtr.Size == 4)
path = "path/to/the/32/bit/Foo.dll";
else
path = "path/to/the/64/bit/Foo.dll";
unsafe
void* handle = LoadLibrary(path);
if (handle == null)
throw new DllNotFoundException("unable to find the native Foo library: " + path);
unloader = new LibraryUnloader(handle);
它包括在 P/Invoke 本身尝试加载本机库之前显式加载本机库及其完整路径。
你怎么看?
【讨论】:
它可以与 Window/MS.NET 一起使用,但是 Mono 呢? 不知道 Mono,请告诉我们您的发现 你不必使用unsafe
关键字,你可以使用IntPtr
类型并放弃void*
。对于其他平台,有类似的 API 可以在运行时加载链接库,我怀疑这些系统上的实现会类似。只是与LoadLibrary
不同的 API 调用。顺便说一句,除非您确实需要从内存中卸载库,否则不需要FreeLibrary
,只有当您实际上拥有与应用程序的生命周期不同的 DLL 生命周期时才会出现这种情况。
@tig 指针大小告诉你当前架构是32bit还是64bit【参考方案2】:
没有办法拥有单个 PInvoke 签名并获得您想要的行为。该属性被刻录到元数据中,并且必须具有常量值。您可以做的一种技巧是拥有多种方法。
public static class NativeMethods32
[DllImport("Foo.dll")]
public static extern int SomeMethod();
public static class NativeMethods64
[DllImport("Foo_x864.dll")]
public static extern int SomeMethod();
public static class NativeMethods
public static bool Is32Bit return 4 == IntPtr.Size;
public static SomeMethod()
return Is32Bit ?
NativeMethods32.SomeMethod();
NativeMethods64.SomeMethod();
但这不是首选方法。一种更简单的方法是使 DLL 在多个平台上具有相同的名称,并创建与平台无关的 PInvoke 签名。这是大多数/所有 Windows 库采用的方法。
【讨论】:
同名的东西不适用于我的情况,因为 1) 这是第三方供应商 DLL 2) DLL(s) 安装到系统 PATH 中的文件夹中,以便应用程序可以自动找到它们(谢天谢地,供应商不再安装到 %SystemRoot%\system32) 3)在 64 位操作系统上,32 位和 64 位 DLL 都需要可用 #1 意味着我不能摆弄和 #2 冲突#3。我最终使用了类似于您建议的解决方案。我为所有方法定义了一个接口,并使用 LinFu 在运行时创建了一个代理对象,该对象转发到正确的静态方法。【参考方案3】:我为目标开发了一个特殊的库:InteropDotNet。它引入了新的RuntimeDllImport
属性和动态库路径解析(动态)。默认情况下,你可以写
[RuntimeDllImport("NativeLib",
CallingConvention = CallingConvention.Cdecl, EntryPoint = "sum")]
int Sum(int a, int b);
库的解析取决于环境。例如 Win/Linux、x86/x64 的路径:
x86/NativeLib.dll
x86/libNativeLib.so
x64/NativeLib.dll
x64/libNativeLib.so
【讨论】:
以上是关于CPU 体系结构独立 P/Invoke:DllName 或路径可以是“动态的”吗?的主要内容,如果未能解决你的问题,请参考以下文章
在通过 P/Invoke 获得的 C++ 结构上设置 C# 回调