运行时如何定位程序集

注释

本文特定于 .NET Framework。 它不适用于 .NET 的较新版本实现,包括 .NET 6 及更高版本。

若要成功部署 .NET Framework 应用程序,必须了解公共语言运行时如何定位并绑定到构成应用程序的程序集。 默认情况下,运行时尝试与应用程序的生成程序集的确切版本绑定。 可以通过配置文件设置覆盖此默认行为。

公共语言运行时在尝试查找程序集并解析程序集引用时执行许多步骤。 以下各节将介绍每个步骤。 描述运行时如何查找程序集时,通常使用术语“探测”;它指一套用于根据名称和区域性查找程序集的试探法。

注释

可以使用 程序集绑定日志查看器(Fuslogvw.exe)在日志文件中查看绑定信息,该查看器包含在 Windows SDK 中。

启动绑定

当运行时尝试解析对另一个程序集的引用时,将开始查找程序集并绑定到程序集的过程。 此引用可以是静态引用,也可以是动态引用。 编译器在生成时记录程序集清单元数据中的静态引用。 调用各种方法(例如 Assembly.Load)时会即时生成动态引用。

引用程序集的首选方法是使用完整引用,包括程序集名称、版本、区域性和公钥令牌(如果存在)。 运行时使用此信息查找程序集,请按照本节后面所述的步骤作。 无论引用是针对静态程序集还是动态程序集,运行时都使用相同的解析过程。

还可通过仅向调用方法提供程序集的部分信息(例如,仅指定程序集名称)动态引用程序集。 在这种情况下,只会搜索应用程序目录来查找程序集,并且不会进行其他检查。 通过使用任一方式加载程序集( Assembly.LoadAppDomain.Load),可执行部分引用。

最后,可以使用类似Assembly.Load的方法进行动态引用,仅提供部分信息;然后在应用程序配置文件中使用<元素来限定引用。 此元素允许你在应用程序配置文件中提供完整的引用信息(名称、版本、区域性和公钥令牌(如果适用),而不是在代码中提供。 如果您想要为应用程序目录外的程序集进行完全限定引用,或者想要引用全局程序集缓存中的程序集,同时希望在配置文件中指定完整引用以便更加方便,而不是在您的代码中进行指定,那就可以使用此技术。

注释

这种类型的部分引用不应与多个应用程序之间共享的程序集一起使用。 由于每个应用程序而不是每个程序集应用配置设置,因此使用此类型部分引用的共享程序集要求每个使用共享程序集的应用程序在其配置文件中具有限定信息。

运行时使用以下步骤解析程序集引用:

  1. 通过检查适用的配置文件(包括应用程序配置文件、发布者策略文件和计算机配置文件)来确定正确的程序集版本。 如果配置文件位于远程计算机上,则运行时必须首先找到并下载应用程序配置文件。

  2. 检查程序集名称是否已绑定到之前 ,如果是,则使用以前加载的程序集。 如果上一个加载程序集的请求失败,请求将立即失败,而不会尝试加载程序集。

    注释

    程序集绑定失败的缓存是 .NET Framework 版本 2.0 中的新增功能。

  3. 检查全局程序集缓存。 如果找到程序集,运行时将使用此程序集。

  4. 使用以下步骤探测程序集

    1. 如果配置和发布服务器策略不影响原始引用,并且绑定请求是使用 Assembly.LoadFrom 该方法创建的,则运行时会检查位置提示。

    2. 如果在配置文件中找到代码库,运行时将仅检查此位置。 如果此探测失败,运行时将确定绑定请求失败,并且不会发生其他探测。

    3. 使用 探测章节中所述的试探法探测程序集。 如果在探测后找不到程序集,运行时会请求 Windows Installer 提供程序集。 这充当按需安装功能。

      注释

      没有版本检查没有强名称的程序集,也没有运行时在全局程序集缓存中检查没有强名称的程序集。

步骤 1:检查配置文件

基于三个 XML 文件,可以在不同级别配置程序集绑定行为:

  • 应用程序配置文件。

  • 发布者策略文件。

  • 计算机配置文件。

这些文件遵循相同的语法,并提供特定程序集的绑定重定向、代码位置和绑定模式等信息。 每个配置文件可以包含一个 <用于重定向绑定进程的 assemblyBinding> 元素 <assemblyBinding> 元素的子元素包括 <dependentAssembly> 元素 <dependentAssembly> 元素的子元素包括 <assemblyIdentity> 元素<bindingRedirect> 元素以及 <codeBase> 元素

注释

可以在三个配置文件中找到配置信息;并非所有元素在所有配置文件中都有效。 例如,绑定模式和专用路径信息只能位于应用程序配置文件中。 有关每个文件中包含的信息的完整列表,请参阅 使用配置文件配置应用

应用程序配置文件

首先,公共语言运行时检查应用程序配置文件是否存在重写调用程序集清单中存储的版本信息的相关信息。 应用程序配置文件可以随应用程序一起部署,但应用程序运行时并不需要该文件。 通常,此文件的检索几乎是即时的,但在应用程序基础位于远程计算机上(如基于 Web 的方案)的情况下,必须下载配置文件。

对于客户端可执行文件,应用程序配置文件与应用程序的可执行文件位于同一目录中,并且具有与具有 .config 扩展名的可执行文件相同的基名称。 例如,C:\Program Files\Myapp\Myapp.exe 的配置文件是 C:\Program Files\Myapp\Myapp.exe.config。在基于浏览器的场景中,HTML 文件必须使用 <链接> 元素来显式地指向配置文件。

以下代码提供了应用程序配置文件的简单示例。 此示例将TextWriterTraceListener添加到Listeners集合中,以便将调试信息记录到文件。

<configuration>
   <system.diagnostics>
      <trace useGlobalLock="false" autoflush="true" indentsize="0">
         <listeners>
            <add name="myListener" type="System.Diagnostics.TextWriterTraceListener, system version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" initializeData="c:\myListener.log" />
         </listeners>
      </trace>
   </system.diagnostics>
</configuration>

发布者策略文件

其次,运行时检查发布者策略文件(如果存在)。 发布者策略文件由组件发布者作为补丁或更新分发到共享组件。 这些文件包含共享组件的发布服务器所发布的的兼容性信息,其中此组件将程序集引用定向到新版本。 与应用程序和计算机配置文件不同,发布服务器策略文件包含在其自己的程序集中,这些程序集必须安装在全局程序集缓存中。

下面是发布服务器策略配置文件的示例:

<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

            <dependentAssembly>
                <assemblyIdentity name="asm6" publicKeyToken="c0305c36380ba429" />
                <bindingRedirect oldVersion="3.0.0.0" newVersion="2.0.0.0"/>
            </dependentAssembly>

        </assemblyBinding>
    </runtime>
</configuration>

若要创建程序集,可以将 Al.exe(程序集链接器) 工具与以下命令一起使用:

Al.exe /link:asm6.exe.config /out:policy.3.0.asm6.dll /keyfile: compatkey.dat /v:3.0.0.0

compatkey.dat 是一个强名称密钥文件。 此命令创建一个强名称程序集,您可以将其放置在全局程序集缓存 (GAC) 中。

注释

发布者策略会影响使用共享组件的所有应用程序。

发布者策略配置文件替代来自应用程序(即程序集清单或应用程序配置文件)的版本信息。 如果应用程序配置文件中没有语句来重定向程序集清单中指定的版本,发布者策略文件将替代程序集清单中指定的版本。 但是,如果应用程序配置文件中有重定向语句,发布者策略将替代该版本,而不是清单中指定的版本。

更新共享组件时,将使用发布者策略文件,并且应使用该组件的所有应用程序选取共享组件的新版本。 除非应用程序配置文件强制实施安全模式,否则发布者策略文件中的设置将替代应用程序配置文件中的设置。

安全模式

发布者策略文件通常作为服务包或程序更新的一部分显式安装。 如果升级后的共享组件出现问题,则可以使用安全模式忽略发布服务器策略文件中的替代。 安全模式由 <publisherPolicy apply=“yes|no”/> 元素确定,仅位于应用程序配置文件中。 它指定是否应从绑定过程中删除发布者策略配置信息。

可以为整个应用程序或所选程序集设置安全模式。 也就是说,可以关闭构成应用程序的所有程序集的策略,也可以为某些程序集打开该策略,但不能对其他程序集启用。 若要选择性地将发布者策略应用于构成应用程序的程序集,请设置 <publisherPolicy apply=no/>,并指定要受影响的程序集,使用 < 元素。 若要将发布策略应用于构成应用程序的所有程序集,请设置 <publisherPolicy apply=no/>,并确保没有依赖程序集元素。 有关配置的详细信息,请参阅 使用配置文件配置应用

计算机配置文件

第三,运行时检查计算机配置文件。 此文件称为 Machine.config,位于安装运行时的根目录的 Config 子目录中的本地计算机上。 管理员可以使用此文件来指定计算机本地的程序集绑定限制。 计算机配置文件中的设置优先于所有其他配置设置;但是,这并不意味着所有配置设置都应放入此文件中。 由管理员策略文件确定的版本是最终版本,无法重写。 Machine.config 文件中指定的替代会影响所有应用程序。 有关配置文件的详细信息,请参阅 使用配置文件配置应用

步骤 2:检查以前引用的程序集

如果之前调用中还请求了所请求的程序集,公共语言运行时将使用已加载的程序集。 命名构成应用程序的程序集时,这可能会产生影响。 有关命名程序集的详细信息,请参阅 程序集名称

如果以前对程序集的请求失败,则程序集的后续请求会立即失败,而不会尝试加载程序集。 从 .NET Framework 版本 2.0 开始,程序集绑定失败将被缓存,并且缓存的信息用于确定是否尝试加载程序集。

注释

若要还原到 .NET Framework 版本 1.0 和 1.1 的行为(不缓存绑定失败),请在配置文件中包含<disableCachingBindingFailures>元素

步骤 3:检查全局程序集缓存

对于强名称程序集,通过查看全局程序集缓存继续执行绑定进程。 全局程序集缓存存储计算机上多个应用程序可以使用的程序集。 全局程序集缓存中的所有程序集都必须具有强名称。

步骤 4:通过代码库或探测查找程序集

使用调用程序集的引用和配置文件中的信息确定正确的程序集版本后,公共语言运行时会在全局程序集缓存中检查(仅适用于强名称程序集),然后尝试查找该程序集。 查找程序集的过程涉及以下步骤:

  1. 如果在应用程序配置文件中找到 codeBase< 元素,运行时将检查指定的位置。> 如果找到匹配项,则使用此程序集且不执行探测。 如果在那里未找到程序集,绑定请求将失败。

  2. 然后,运行时将使用本节后面指定的规则探测引用的程序集。

注释

如果目录中有多个版本的程序集,并且想要引用该程序集的特定版本,则必须使用 <codeBase> 元素而不是privatePath探测<元素的属性>。 如果使用 <探测> 元素,运行时将在第一次发现与所引用的简单程序集名匹配的程序集时停止探测,无论它是否是正确的匹配项。 如果此匹配项正确,则使用此程序集。 如果不匹配,探测将停止并绑定失败。

通过代码库查找程序集

可以在配置文件中使用 <codeBase> 元素提供代码库信息。 在运行时尝试探测所引用的程序集之前,此代码库总是会被检查。 如果包含最终版本重定向的发布者策略文件也包含 <codeBase> 元素,则 <codeBase> 元素是所使用的元素。 例如,如果应用程序配置文件指定 <codeBase> 元素,并且重写应用程序信息的发布者策略文件也指定 <codeBase> 元素, <则使用发布服务器策略文件中的 codeBase> 元素。

如果在<codeBase>元素指定的位置找不到任何匹配项,绑定请求将失败,并且不会采取进一步的步骤。 如果运行时确定程序集与调用程序集的条件匹配,则它使用该程序集。 加载给定 <codeBase> 元素指定的文件时,运行时会检查以确保名称、版本、区域性和公钥与调用程序集的引用匹配。

注释

在应用程序根目录之外引用的程序集必须具有强名称,并且必须安装在全局程序集缓存中或使用 <codeBase> 元素指定。

通过探测查找程序集

如果应用程序配置文件中没有 <codeBase> 元素,则运行时会使用四个条件探测程序集:

  • 应用程序基,这是执行应用程序的根位置。

  • 区域性,即被引用的程序集的区域性属性。

  • 名称,即被引用的程序集的名称。

  • > 元素的 特性,这是根位置下用户定义的子目录列表。 可以使用应用程序域的属性在应用程序配置文件和托管代码 AppDomainSetup.PrivateBinPath 中指定此位置。 在托管代码中指定时,首先会检索托管代码 privatePath,然后是应用程序配置文件中指定的路径。

探测应用程序基和区域性目录

运行时始终在应用程序的基目录中开始探测,它可以是计算机上的 URL 或应用程序的根目录。 如果在应用程序基础中找不到引用的程序集,并且未提供任何区域性信息,则运行时会搜索具有程序集名称的任何子目录。 查看的目录包括:

  • [应用程序基] / [程序集名称].dll

  • [application base] / [assembly name] / [assembly name].dll

如果指定了被引用程序集的区域性信息,则只探测以下目录:

  • [application base] / [culture] / [assembly name].dll

  • [application base] / [culture] / [assembly name] / [assembly name].dll

使用 privatePath 属性进行探测

除区域性子目录和为被引用程序集指定的子目录外,运行时还探测使用 > 元素的 特性指定的目录。 使用 privatePath 特性指定的目录必须是应用程序的根目录的子目录。 根据引用的程序集请求中是否包含区域性信息,探测的目录会所有不同。

运行时在首次找到一个与引用的简单程序集名称相匹配的程序集时便会停止查找,无论该匹配项是否正确。 如果此匹配项正确,则使用此程序集。 如果不匹配,探测将停止并绑定失败。

如果包括区域性,则探测以下目录:

  • [application base] / [binpath] / [culture] / [assembly name].dll

  • [application base] / [binpath] / [culture] / [assembly name] / [assembly name].dll

如果不包括区域性信息,则探测以下目录:

  • [应用程序根目录] / [二进制路径] / [程序集名称].dll

  • [application base] / [binpath] / [assembly name] / [assembly name].dll

探测示例

给定以下信息:

  • 引用的程序集名称:myAssembly

  • 应用程序根目录: http://www.code.microsoft.com

  • 配置文件中的 <probing> 元素指定:bin

  • 区域性:de

运行时探测以下 URL:

  • http://www.code.microsoft.com/de/myAssembly.dll

  • http://www.code.microsoft.com/de/myAssembly/myAssembly.dll

  • http://www.code.microsoft.com/bin/de/myAssembly.dll

  • http://www.code.microsoft.com/bin/de/myAssembly/myAssembly.dll

具有相同名称的多个程序集

以下示例演示如何配置具有相同名称的多个程序集。

<dependentAssembly>
   <assemblyIdentity name="Server" publicKeyToken="c0305c36380ba429" />
   <codeBase version="1.0.0.0" href="v1/Server.dll" />
   <codeBase version="2.0.0.0" href="v2/Server.dll" />
</dependentAssembly>

探测的其他位置

还可以使用当前绑定上下文确定程序集位置。 通常情况下,这种情况会发生在使用 Assembly.LoadFrom 方法和 COM 互操作方案中。 如果程序集使用 LoadFrom 该方法引用另一个程序集,则调用程序集的位置被视为有关查找所引用程序集的位置的提示。 如果找到匹配项,则加载该程序集。 如果未找到匹配项,运行时将按照其搜索语义继续进行搜索,然后查询 Windows Installer 来提供程序集。 如果未提供与绑定请求匹配的程序集,则会引发异常。 如果引用了类型,则在TypeLoadException托管代码中出现此异常;如果找不到正在加载的程序集,则在FileNotFoundException中出现此异常。

例如,如果 Assembly1 引用 Assembly2,并且 Assembly1 是从http://www.code.microsoft.com/utils下载的,那么该位置被视为有关查找 Assembly2.dll的提示。 然后运行时会在 http://www.code.microsoft.com/utils/Assembly2.dllhttp://www.code.microsoft.com/utils/Assembly2/Assembly2.dll 中探测程序集。 如果未在任一位置找到 Assembly2,运行时将查询 Windows Installer。

另请参阅