Assembly.LoadFrom如何解决非托管依赖性?

3
我对 Assembly.LoadFrom 的行为感到困惑。在我的应用程序中,我调用 Assembly.LoadFrom 来加载 .NET .exe 并使用 EntryPoint.Invoke 启动它(这种奇怪的方法对于在非 Windows 平台上构建 启动器 应用程序很有用)。
我原以为,由于 assemblyFile 在不同的文件夹中,它将无法找到与其位于同一文件夹中的某些 托管 .dll 依赖项。但是它起作用了;它没有失败...
当我调用 Assembly.LoadFrom(assemblyFile) 时,它似乎检查包含 assemblyFile 的文件夹,以查找 assemblyFile托管 依赖项。我没有预料到这一点。如果该程序集具有非托管依赖项(例如DllImport),它是否仍会搜索同一目录?这个行为是否特定于框架?

在Mono中,没有任何奇怪的行为,行为与原来相同。对于非托管依赖项...应用正常的平台规则(例如Windows的LoadLibrary())。老实说,我不确定这是否是特定于平台的,因此在Linux上使用Mono时,将应用正常的Linux搜索规则。 - Adriano Repetti
@Adriano 但 JIT 懒惰地解析 DllImport。当最终解析 DllImport("MySweetLibrary.dylib") 时,它会知道它来自遥远目录中的 assemblyFile,并搜索该目录吗?我猜它不会。 - Mr. Smith
2个回答

11

Assembly与加载非托管库无关。负责加载非托管库的是DllImport调用,它是惰性的(在第一次调用之前不会加载)。

在 .NET 中,DllImport 调用LoadLibrary函数(我不知道 Mono 在其他平台上的情况)。LoadLibary一组已知规则来解析它的依赖项:

  • 如果同名的 DLL 已经加载到内存中,系统会使用已经加载的 DLL,无论它在哪个目录中。系统不会搜索该 DLL。
  • 如果 DLL 在正在运行应用程序的 Windows 版本的已知 DLL 列表中,则系统使用其已知 DLL 的副本(以及已知 DLL 的依赖 DLL(如果有))。系统不会搜索该 DLL。有关当前系统上已知 DLL 的列表,请参见以下注册表键:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

如果这两个条件都不满足,并且启用了 SafeDllSearchMode(默认情况下为 XP SP2 及更高版本),则使用以下顺序:

  1. 应用程序加载的目录。
  2. 系统目录。使用GetSystemDirectory函数获取此目录的路径。
  3. 16位系统目录。没有获取该目录路径的函数,但会进行搜索。
  4. Windows目录。使用GetWindowsDirectory函数获取此目录的路径。
  5. 当前目录。
  6. 在PATH环境变量中列出的目录。请注意,这不包括由App Paths注册表键指定的每个应用程序路径。计算DLL搜索路径时,不使用App Paths键。

为了回答你的问题,当寻找非托管程序集时,并不会搜索托管程序集所在的目录,而只会搜索加载托管程序集的应用程序所在的目录。
但是,仍有希望。你可以调用 SetDLLDirectory 并添加托管程序集所在的文件夹,这样在查找非托管 DLL 时就会将其包含在搜索范围内,它将更改搜索顺序:
  1. 应用程序加载的目录。
  2. 由 lpPathName 参数指定的目录(在 SetDLLDirectory 调用中)。
  3. 系统目录。使用 GetSystemDirectory 函数获取此目录的路径。该目录的名称为 System32。
  4. 16 位系统目录。没有函数可以获取此目录的路径,但会进行搜索。该目录的名称为 System。
  5. Windows 目录。使用 GetWindowsDirectory 函数获取此目录的路径。
  6. 列在 PATH 环境变量中的目录。
如果您需要添加多个文件夹进行搜索,请参阅MSDN有关AddDllDirectory的文档,了解允许多个搜索目录所需的步骤。

4
没有任何.NET特定的配置会影响未管理的DLL的定位方式。无论CLR如何找到托管程序集,正常的操作系统搜索规则都会生效。在Windows上的底层操作系统调用是LoadLibrary(),这是一个winapi函数,它本身没有支持更改搜索规则。您可以指定完整的路径名来避免搜索,但在pinvoke中几乎从不实用。
如果这是一个问题,那么您将需要编写显式代码以帮助操作系统定位文件。常见的技术包括pinvoke SetDllDirectory(),使用Environment.SetEnvironmentVariable()修改PATH环境变量和通过分配Environment.CurrentDirectory来更改默认目录。

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