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

时间:2013-12-19 20:48:41

标签: c# .net dllimport

我对Assembly.LoadFrom的行为感到困惑。在我的应用程序中,我将Assembly.LoadFrom称为.NET .exe并使用EntryPoint.Invoke启动它(这种奇怪的方法对于在非Windows上构建启动程序应用程序非常有用平台)。

我原以为由于assemblyFile位于不同的文件夹中,因此无法找到与其位于同一文件夹中的某些托管 .dll依赖项。但它奏效了;它没有失败......

当我调用Assembly.LoadFrom(assemblyFile)时,它会检查包含assemblyFile的文件夹,以查找assemblyFile托管依赖项。我没想到这一点。如果该程序集具有非托管依赖项(例如DllImport)会发生什么情况,它是否仍会搜索同一目录?这个行为框架是否具体?

2 个答案:

答案 0 :(得分:8)

Assembly与加载未标记的库无关。非托管库的加载是DllImport调用是多么懒惰(在第一次调用之前不会加载)。

DllImport反过来(在.NET中,我不知道Mono在其他平台上做了什么)在Windows上调用LoadLibaryLoadLibarya known set of rules表示如何解决它的依赖关系:

  • 如果已在内存中加载了具有相同模块名称的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键。

  7. 因此,要回答您的问题,在查找非托管程序集时,不会搜索托管程序集所在的目录,只会搜索加载托管程序集的应用程序目录。

    然而,所有的希望都没有丢失,你可以调用SetDLLDirectory并添加托管程序集的文件夹,它会在搜索中包含它,在查找非托管DLL时,它会将搜索顺序更改为

    1. 加载应用程序的目录。
    2. lpPathName参数指定的目录(在SetDLLDirectory调用中)。
    3. 系统目录。使用GetSystemDirectory函数获取此目录的路径。该目录的名称是System32。
    4. 16位系统目录。没有函数可以获取此目录的路径,但会搜索它。该目录的名称是System。
    5. Windows目录。使用GetWindowsDirectory函数获取此目录的路径。
    6. PATH环境变量中列出的目录。
    7. 如果您需要添加多个文件夹进行搜索,请参阅AddDllDirectory上的MSDN文档,了解允许多个搜索目录所需的步骤。

答案 1 :(得分:4)

.NET特定配置都不会影响非托管DLL的定位方式。无论CLR如何找到托管程序集,正常的操作系统搜索规则都有效。 Windows上的底层OS调用是LoadLibrary(),这是一种不支持改变搜索规则本身的winapi函数。您可以指定完整路径名称以避免搜索,但这在pinvoke中几乎不可行。

如果这是一个问题,那么你必须编写显式代码来帮助操作系统找到文件。常见的技术是调整SetDllDirectory(),使用Environment.SetEnvironmentVariable()更改PATH环境变量,并通过分配Environment.CurrentDirectory

来更改默认目录。