【发布时间】:2017-04-28 12:06:20
【问题描述】:
我正在开发一个 Unity 应用程序,它需要从安装应用程序的外部动态加载本机库,由于某种原因,我无法在编译之前将绝对路径设置为 DllImport(例如读取 . txt 在运行时并加载它),我不想使用平台特定的 API,例如 Windows 上的 LoadLibrary() 或 Linux 上的 dlopen,因为它不方便。而且我已经挣扎了好几天了。
我知道搜索路径可以通过SetDllDirectory()在Windows上从this post进行调整,并且在.NET Framework应用程序上测试时效果很好。
但是,它在基于 mono 2.0 的 Unity 中不起作用,它只是在运行时抛出 DllNotFoundException,但是当我在 DllImport 中使用绝对路径或将 dll 复制到我的 Unity 项目中时它工作正常(我确定代码相同)
我尝试的下一个方法是环境变量,它不适用于 .NET 和 Mono,this post 解释说 CLR 在进程执行期间从不刷新环境。
我尝试的第三种方法是使用特定于平台的API加载本机库,例如Windows上的LoadLibrary()和Linux上的dlopen(),然后Dllimport可能会发现已经加载了同名的库,然后它将使用加载库来查找函数指针,就像this post 所做的那样。我得到了同样的结果。该问题的最佳答案是我们可以编写一个包装类,它使用特定于平台的 API 来显式加载库并获取函数指针,而不是专注于 Dllimport 的方法,但这不是我想要的。
如果我的猜测是正确的,根据mono's document,DllImportAttribute 在运行时内部调用LoadLibrary 或dlopen 以将库加载到内存空间中。所以它遵循特定操作系统平台的搜索规则,例如windows:
- 加载应用程序的目录。
- 当前目录
- 系统目录。使用
GetSystemDirectory()函数获取该目录的路径。 - 16 位系统目录。
- Windows 目录。使用
GetWindowsDirectory()函数获取 此目录的路径。 - PATH 环境变量中列出的目录。
和 Linux:
- 用户
LD_LIBRARY_PATH中以冒号分隔的目录列表 环境变量。这是一种常用的方式来让原生 由 CLI 程序找到的共享库。 -
/etc/ld.so.cache中缓存的库列表。/etc/ld.so.cache通过编辑/etc/ld.so.conf并运行ldconfig(8)创建。 编辑/etc/ld.so.conf是搜索附加信息的首选方式 目录,而不是使用LD_LIBRARY_PATH,因为这样更 安全(木马库更难进入/etc/ld.so.cache而不是将其插入LD_LIBRARY_PATH)。 -
/lib,然后是/usr/lib。
顺便说一句,我也尝试在运行时设置LD_LIBRARY_PATH,但它不起作用,因为LD_LIBRARY_PATH只会在进程启动时被解析一次,这类似于Windows上的PATH环境变量。
所以我的问题是:
- 为什么相同的代码在 .NET Framework 和 Mono 上执行不同? Mono 是否只是忽略了
SetDllDirectory()在 Windows 上的影响?DllImportAttribute在 Mono 中实际上做了什么? - 有没有办法在运行时调整 Unity/Mono 应用程序的搜索路径,只使用
DllImport而不是平台特定的 API,例如LoadLibrary()和dlopen()?
【问题讨论】:
标签: c# .net mono pinvoke dllimport