如何在Mono中设置Dllimport的搜索路径?

4
我正在开发一个 Unity 应用程序,需要动态从安装位置之外加载本地库。由于某种原因,我不能在编译前设置 DllImport 的绝对路径(如在运行时读取库路径的 .txt),也不想使用特定于平台的 API,例如 Windows 上的 LoadLibrary() 或 Linux 上的 dlopen ,因为这样很麻烦。我已经为此奋斗了几天。
我知道可以通过 Windows 上的 SetDllDirectory() 调整搜索路径,来自 此帖子,当在 .NET Framework 应用程序上进行测试时,它运行良好。 但是在基于 mono 2.0 的 Unity 中却不起作用,它只会在运行时抛出 DllNotFoundException ,但是当我在 DllImport 中使用绝对路径或将 dll 复制到我的 Unity 项目中时(我确定代码相同),它能够正常工作。
我尝试的下一步方法是使用环境变量,在 .NET 和 Mono 上都不起作用,此帖子 解释说 CLR 在进程执行期间从不刷新环境。
我尝试的第三种方法是首先使用特定于平台的 API(例如在 Windows 上使用 LoadLibrary() 或在 Linux 上使用 dlopen())加载本地库,然后 Dllimport 可能会发现同名库已经被加载,然后它将使用加载库来查找函数指针,就像此帖子所述。我得到了相同的结果。该问题的最佳答案说我们可以编写一个包装器类,该类使用特定于平台的 API 显式地加载库并获取函数指针,而不是专注于 Dllimport 的方法,但这不是我想要的。
如果我的猜测正确,则根据 mono 文档 DllImportAttribute 在运行时内部调用 LoadLibrary dlopen 来将库加载到内存空间中。因此,它遵循特定于操作系统平台的搜索规则,例如 Windows:
  1. 应用程序加载的目录。
  2. 当前目录
  3. 系统目录。使用 GetSystemDirectory() 函数获取此目录的路径。
  4. 16 位系统目录。
  5. Windows 目录。使用 GetWindowsDirectory() 函数获取此目录的路径。
  6. 列在 PATH 环境变量中的目录。
而 Linux:
  1. 用户的LD_LIBRARY_PATH环境变量中用冒号分隔的目录列表。这是一种常用的方法,允许CLI程序找到本地共享库。
  2. /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更困难)。
  3. /lib,然后是/usr/lib

顺便说一下,我也尝试在运行时设置LD_LIBRARY_PATH,但它不起作用,因为LD_LIBRARY_PATH只会在进程启动时解析一次,这类似于Windows上的PATH环境变量。

所以我的问题是:

  1. 为什么相同的代码在.NET Framework和Mono上表现不同?Mono是否在Windows上忽略了SetDllDirectory()的效果?DllImportAttribute在Mono中实际上是做什么的?
  2. 是否有任何方法可以在运行时调整Unity/Mono应用程序的搜索路径,只使用DllImport而不是平台特定的API,如LoadLibrary()dlopen()
1个回答

1
很不幸,这种行为在Linux上的Mono和Windows上的.Net之间是不同的,所以你只能处理这个问题。
最好的选择是,如果你知道每个DLL的位置(例如,你可以将其放入配置文件中),则自己明确地加载每个DLL使用LoadLibrarydlopen。必须在第一次调用DllImport函数之前完成此操作。DllImport然后无需指定路径。
这样,你就知道你得到了哪个DLL,并且可以按照正确的顺序加载它们,如果这是一个问题的话。
如果出于某种原因你真的不想这样做,我建议你创建一个类似于MySetDllDirectory的函数,在Windows上调用SetDllDirectory,在Linux上设置LD_LIBRARY_PATH。通过这种方式,更改可以隔离到单个模块。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接