如何在单声道中设置dllimport的搜索路径?

时间:2017-04-28 12:06:21

标签: c# .net mono pinvoke dllimport

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

我知道搜索路径可以在this post的Windows上通过SetDllDirectory()进行调整,并且在.NET Framework应用程序上进行测试时效果很好。 但是,它在基于mono 2.0的Unity中不起作用,它只是在运行时抛出DllNotFoundException,但是当我在DllImport中使用绝对路径或将dll复制到我的Unity项目中时它工作正常(我确定代码是相同的)

我尝试的下一种方式是环境变量,它在.NET和Mono上都不起作用,this post解释说CLR在进程执行期间从不刷新环境。

我尝试的第三种方法是在Windows上加载具有平台特定API的本机库,例如LoadLibrary(),在Linux上加载dlopen(),然后Dllimport可能会发现该库已加载相同的名称,然后它将使用加载库来查找函数指针,就像this post所做的那样。我得到了相同的结果。这个问题的首要答案是我们可以编写一个包装类,它使用特定于平台的API来显式加载库并获取函数指针,而不是聚焦Dllimport的方法,但这不是我想要的。

如果我的猜测正确,根据mono's documentDllImportAttribute在运行时内部调用LoadLibrarydlopen将库加载到内存空间。因此它遵循特定OS平台的搜索规则,例如windows:

  1. 加载应用程序的目录。
  2. 当前目录
  3. 系统目录。使用GetSystemDirectory()函数获取此目录的路径。
  4. 16位系统目录。
  5. Windows目录。使用GetWindowsDirectory()功能获取 这个目录的路径。
  6. PATH环境变量中列出的目录。
  7. 和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
    4. 顺便说一下,我也尝试在运行时设置LD_LIBRARY_PATH,但它不起作用,因为LD_LIBRARY_PATH只会在进程启动时解析一次,类似于PATH Windows上的环境变量。

      所以我的问题是:

      1. 为什么相同的代码在.NET Framework和Mono上的执行方式不同? Mono是否忽略了SetDllDirectory()对Windows的影响? DllImportAttribute在Mono中实际做了什么?
      2. 有没有办法在运行时调整Unity / Mono应用的搜索路径,只使用DllImport而不是LoadLibrary()dlopen()等平台特定的API?

1 个答案:

答案 0 :(得分:1)

不幸的是,答案是Linux上的Mono和Windows上的.Net之间的这种行为是不同的,所以你只需要处理它。

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

通过这种方式,您可以确切地知道您正在获取哪个DLL,并且可以按照正确的顺序加载它们,如果这是一个问题。

如果由于某种原因你真的不想这样做,我建议你创建一个像MySetDllDirectory这样的函数,在Windows上调用SetDllDirectory和Linux集LD_LIBRARY_PATH。通过这种方式,可以将更改隔离到单个模块中。