Concepts and Terminology


Before I get into the details of how to load assemblies into extensible applications, let me take a step back and outline some basic concepts and terminology. The topic of assembly loading is laden with new terms that are sometimes overloaded when they shouldn't be. To describe how to reference and load assemblies, it's best to agree on a consistent set of terminology. In the next few sections I describe the concepts and terminology that I use throughout the rest of this chapter. In particular, I define the following:

  • Strong versus weak assembly names

  • Early-bound versus late-bound references

  • Fully specified versus partially specified references

  • Version policy

Strong and Weak Assembly Names

A .NET Framework assembly is said to have either a weak name or a strong name. Throughout this chapter, I demonstrate how the factors you must consider when loading an assembly are different based on whether the assembly has a strong name or a weak name. For example, I'll show that the CLR uses different rules for locating an assembly when referring to it by its strong name rather than its weak name.

An assembly has a strong name if it has been signed with a cryptographic key pair using either a compiler or the sn.exe SDK tool. If an assembly has not been signed, it has a weak name. Structurally, assemblies with strong names are different from assemblies with weak names in two key ways:

  1. Assemblies with strong names have a digital signature embedded in the file containing the manifest.

  2. The name of strong-named assemblies contains the public key used to generate the signature.

This second characteristic is particularly important to the process of referencing and loading an assembly.

An assembly's identity consists of four parts:

  • Friendly name

  • Version

  • Public key

  • Culture

Assemblies with weak names have a friendly name, a version, and an optional culture. Assemblies with a strong name have a friendly name, a version, a public key, and a culture. You can see this difference in the portion of an assembly's metadata that records its name. In the following listing, I've used the ildasm.exe SDK tool to display a portion of the metadata for an assembly with a strong name. Note the presence of a public key in the name.

.assembly BoatRaceHostRuntime {   .publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00                 00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00                 07 D1 FA 57 C4 AE D9 F0 A3 2E 84 AA 0F AE FD 0D                 E9 E8 FD 6A EC 8F 87 FB 03 76 6C 83 4C 99 92 1E                 B2 3B E7 9A D9 D5 DC C1 DD 9A D2 36 13 21 02 90                 0B 72 3C F9 80 95 7F C4 E1 77 10 8F C6 07 77 4F                 29 E8 32 0E 92 EA 05 EC E4 E8 21 C0 A5 EF E8 F1                 64 5C 4C 0C 93 C1 AB 99 28 5D 62 2C AA 65 2C 1D                 FA D6 3D 74 5D 6F 2D E5 F1 7E 5E AF 0F C4 96 3D                 26 1C 8A 12 43 65 18 20 6D C0 93 34 4D 5A D2 93 )   .hash algorithm 0x00008004   .ver 1:0:0:0 }

In contrast, the following listing shows the metadata stored for an assembly with a weak name:

.assembly Utilities { .ver 1:0:0:0 }

Assemblies are given strong names when you intend them to be shared among several applications on the system. Shared code has much more stringent requirements than code that is private to only one application. These additional requirements are primarily related to security and versioning. For example, assemblies used by many applications require a robust approach to versioning to avoid the versioning conflicts associated with Win32 DLLs. New versions of the shared assembly must be able to be added to the system without breaking applications that depend on previous versions. A cryptographically strong name is required to ensure that a given assembly can't be altered by anyone other than the original assembly author.

For a more thorough description of assembly names, including more discussion on the motivation behind using strong names, see Chapters 2 and 3 in Applied Microsoft .NET Framework Programming (Microsoft Press, 2002) by Jeffrey Richter.

Early-Bound and Late-Bound References

Assemblies can be referenced in either an early-bound or a late-bound fashion. Early-bound references are recorded in metadata when an assembly is compiled. Late-bound references are specified on the fly using the APIs on such classes as System.Reflection.Assembly, System.Activator, or System.AppDomain.

Extensible applications are likely to use both early- and late-bound references. Assemblies that are part of the implementation of the extensible application are likely to be referenced using early binding. In contrast, the add-ins that are dynamically added to the application to extend it are loaded using late binding because the application doesn't know which add-ins it will load (and, hence, can't reference them) when the application is compiled.

Let's return to the boat race example introduced in Chapter 5 to illustrate more concretely how these two types of references are likely to be used in an extensible application. Assume that our boat race application is written entirely in managed code and consists of a main executable called boatracehost.exe and three utility DLLs called boatracehostruntime.dll, weather.dll, and sailconfigurations.dll. When boatracehost.exe is compiled, it statically references the utility DLLs using the /r compiler switch, like so:

C:\temp\BoatRaceHost>csc /target:winexe / r:BoatRaceHostRuntime.dll, SailConfigurations.dll,Weather.dll BoatRaceHost.cs

After compiling boatracehost.cs using this command line, the manifest for boatracehost.exe looks like this (produced with ildasm.exe):

.assembly BoatRaceHost {   .ver 1:0:0:0 } .assembly extern BoatRaceHostRuntime {   .publickeytoken = (4B 7D 48 01 D8 95 67 95)   .ver 1:0:0:0 } .assembly extern SailConfigurations {   .publickeytoken = (4B 7D 48 01 D8 95 67 95)   .ver 1:0:0:0 } .assembly extern Weather {   .publickeytoken = (4B 7D 48 01 D8 95 67 95)   .ver 1:0:0:0 } .assembly extern System.Windows.Forms {   .publickeytoken = (B7 7A 5C 56 19 34 E0 89)   .ver 1:0:5000:0 } .assembly extern System {   .publickeytoken = (B7 7A 5C 56 19 34 E0 89)   .ver 1:0:5000:0 } .assembly extern mscorlib {   .publickeytoken = (B7 7A 5C 56 19 34 E0 89)   .ver 1:0:5000:0 }   .assembly extern System.Drawing {   .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A)   .ver 1:0:5000:0 }

Each .assembly extern statement in the previous listing represents an early-bound reference to an assembly. Notice that in addition to the references to SailConfigurations, Weather, and BoatRaceHostRuntime, our application has early-bound references to the .NET Framework assemblies it uses, including System.Drawing and System.Windows.Forms.

To illustrate when late-bound references are used, let's assume our boatracehost application includes a menu command that enables users to add boats to the race dynamically. During a particular run of the application, the end user adds two boats to the race. These two boats are contained in the assemblies Alingi and TeamNZ. Both Alingi and TeamNZ are loaded dynamically; hence, late binding is used as shown in Figure 7-1.

Figure 7-1. Early- and late-bound assembly references in extensible applications


This chapter focuses primarily on late binding because the flexibility introduced by loading assemblies on the fly introduces several considerations unique to extensible applications. In particular, the fact that late-bound references can be partially specified introduces complexities you don't run into when all assembly references are early bound.

Fully Specified and Partially Specified References

As described, assemblies in .NET Framework are identified by a friendly name, a public key, a version number, and a culture. When referencing an assembly, you can specify all four of these fields or you can specify just the friendly namethe public key, version number, and culture are all optional. When all four fields are specified, the assembly reference is said to be fully specified. If anything less than all four fields is included, the reference is partially specified.

Partially specified references are unique to late binding. When a compiler records an earlybound reference to an assembly in metadata, it stores values for all four fields. These values can be null (as the value for the public key is when an assembly has a weak name), but they are values nonetheless. In contrast, the APIs that enable you to load an assembly dynamically allow you to omit values for the public key, version number, and culture fields. Referencing an assembly by only a portion of its name results in looser binding semantics than a fully specified reference does. For example, if you want to load an assembly by its friendly name only, without regard to version, you can simply omit a version number from your reference. If the CLR finds an assembly whose friendly name matches, it loads it regardless of which version it is. These looser binding semantics are useful in some scenarios. Microsoft ASP.NET, for example, uses partial binding and weakly named assemblies to implement loose version binding for assemblies stored in the bin subdirectory under the application's root directory. You might have noticed you can place any assembly in the bin directory and, as long as its name matches, it is loaded, regardless of version.

Although the flexibility allowed by partially specified assembly references is useful in scenarios like these, you should also be aware of its complexities. For example, the rules for which of the four fields must match are different depending on whether the assembly that matches the friendly name has a strong name or a weak name. I discuss these complexities in the section "Partially Specified Assembly References" later in this chapter.

Version Policy

As described, strong names are part of the infrastructure the CLR uses to provide a system that employs strict version checking to minimize inadvertent conflicts between different versions of an assembly. By default, the CLR loads the exact version of the assembly you specify in your reference. However, this version can be redirected to a different version through a series of statements called version policy. Version policy is useful in several scenarios. For example, a machine administrator can specify version policy that would prevent anyone on the machine from using a version of an assembly that is known to have security vulnerabilities or other serious bugs. In addition, an application can use version policy to load a version of a .NET Framework assembly different from the default.

These version policy statements are specified in Extensible Markup Language (XML) files and are most easily created with the .NET Framework Configuration tool. The following is an example of a version policy statement that causes version 6.0.0.0 of an assembly whose friendly name is Alingi to be loaded, regardless of which version was specified:

     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">        <dependentAssembly>          <assemblyIdentity name="Alingi" publicKeyToken="ae4cc5eda5032777" />            <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"              newVersion="6.0.0.0" />        </dependentAssembly>      </assemblyBinding>

Version policy can be specified at three different levels. First, the author of an application that uses shared assemblies can specify version policy in the application configuration file. Second, the publisher of a shared assembly can provide version policy in what is called a publisher policy assembly. Finally, the machine administrator can specify version policy through entries in the machine.config file. When the CLR resolves a reference to a strong-named assembly, it begins by looking in these three locations for version policy statements that might redirect the assembly reference to a different version. The three policy levels also form a hierarchy in that they are evaluated in sequence with the output of one level feeding into the next. For example, say an application configuration file contains a policy statement that redirects all references to an assembly from version 1.0.0.0 to version 2.0.0.0. If the policy statement in the application configuration file applies to the reference being resolved, the CLR next evaluates publisher policy looking for any policy statements that redirect version 2.0.0.0 of the assembly. Likewise, administrator policy is evaluated based on the outcome of the publisher policy phase.

This brief description of version policy gives you what you need to understand the concepts presented in this chapter. For more details on how version policy is specified and resolved, see Chapters 2 and 3 in Applied Microsoft .NET Framework Programming (Microsoft Press, 2002) by Jeffrey Richter.



    Customizing the Microsoft  .NET Framework Common Language Runtime
    Customizing the Microsoft .NET Framework Common Language Runtime
    ISBN: 735619883
    EAN: N/A
    Year: 2005
    Pages: 119

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net