我正在开发一个Unity应用程序,该应用程序需要从安装该应用程序的外部动态加载本机库,由于某种原因,我无法在编译前将绝对路径设置为DllImport(例如,在运行时读取.txt中的库路径)并加载它),并且我不想使用平台特定的API,例如Windows上的LoadLibrary()或Linux上的dlopen,因为它很不方便。我已经挣扎了好几天。

我知道可以通过Windows上的SetDllDirectory()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 documentDllImportAttribute在运行时内部调用LoadLibrarydlopen将库加载到内存空间中。因此它遵循特定OS平台(例如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是否会忽略Windows上SetDllDirectory()的影响? DllImportAttribute在Mono中实际上做什么?
是否有任何方法可以在运行时调整Unity / Mono应用程序的搜索路径,仅使用DllImport而非平台特定的API(例如LoadLibrary()dlopen())即可?

最佳答案

不幸的是,答案是Linux上的Mono和Windows上的.Net之间的行为是不同的,因此您将不得不处理该问题。

如果您知道每个DLL的位置(例如,可以将其放入配置文件中),则最好的选择是使用LoadLibrarydlopen自己显式加载每个DLL。这必须在第一次调用DllImport函数之前完成。 DllImport然后无需指定路径。

这样,您就可以确切地知道要获取哪个DLL,并可以按正确的顺序加载它们(如果有问题)。

如果由于某种原因您确实不想这样做,建议您创建一个类似MySetDllDirectory的函数,该函数在Windows上调用SetDllDirectory,在Linux上设置LD_LIBRARY_PATH。这样,可以将更改隔离到单个模块中。

关于c# - 如何在Mono中设置dllimport的搜索路径?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43679863/

10-12 19:54