C#跨平台P/Invoke时导入动态库的问题

众所周知C#的DllImport特性只允许将常量字符串赋值给DllName。

C/C++动态库在不同平台上可能具有不同的名称。

用nativedep这个库来举例:在windows上可能叫nativedep.dll、linux上叫libnativedep.so、macos上叫libnativedep.dylib。

幸运的是C#会处理常见的变体,如果使用nativedep作为DllName,以上变体都能正确匹配:

[DllImport("nativedep")]
static extern int ExportedFunction();
复制代码
在 Windows 上运行时,将按以下顺序搜索 DLL:

    nativedep
    nativedep.dll(如果库名称尚未以 .dll 或 .exe 结尾)

在 Linux 或 macOS 上运行时,运行时将尝试在 lib 前添加,并追加规范共享库扩展。 在这些 OS 上,按以下顺序尝试库名称变体:

    nativedep.so / nativedep.dylib
    libnativedep.so / libnativedep.dylib 1
    nativedep
    libnativedep 1

在 Linux 上,如果库名称以 .so 结尾或包含 .so.(注意尾随 .),则搜索顺序会有所不同。 请考虑以下示例:
C#

[DllImport("nativedep.so.6")]
static extern int ExportedFunction();

在这种情况下,将按以下顺序尝试库名称变体:

    nativedep.so.6
    libnativedep.so.6 1
    nativedep.so.6.so
    libnativedep.so.6.so 1

仅当库名称不包含目录分隔符 (/) 时才检查路径。
复制代码

但不幸的是有些库作者不按常理命名,导致默认的行为不管用,比如使用“libnativedep-0.dll”这种名称。这时可以考虑使用 NativeLibrary.SetDllImportResolver 来自定义解析规则。

如果你的库面向standard2.0、standard2.1,那么 NativeLibrary.SetDllImportResolver 是用不了的。一个可以考虑的做法是手动改掉c/c++动态库的名称使其匹配默认规则,前提是库内部不会依赖文件名称;

另一个做法是使用kernel32.LoadLibrary、libdl.dlopen封装一个中间层。可参考 https://github.com/mellinoe/nativelibraryloader

 

相关资料:

本机库加载

SetDllImportResolver不支持.NET Standard

一个中间层nativelibraryloader

一个曾经踩坑的开源库,查看提交记录363a555d可以看到作者采用了两种方式

posted @   陈百川  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示