我正在开发一个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 document,DllImportAttribute
在运行时内部调用LoadLibrary
或dlopen
将库加载到内存空间中。因此它遵循特定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的位置(例如,可以将其放入配置文件中),则最好的选择是使用LoadLibrary
或dlopen
自己显式加载每个DLL。这必须在第一次调用DllImport
函数之前完成。 DllImport
然后无需指定路径。
这样,您就可以确切地知道要获取哪个DLL,并可以按正确的顺序加载它们(如果有问题)。
如果由于某种原因您确实不想这样做,建议您创建一个类似MySetDllDirectory
的函数,该函数在Windows上调用SetDllDirectory
,在Linux上设置LD_LIBRARY_PATH
。这样,可以将更改隔离到单个模块中。
关于c# - 如何在Mono中设置dllimport的搜索路径?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43679863/