.NET Assemblies, GAC, Versioning, Manifests and Deployment
A .NET application is composed of three primary entities: assemblies, modules
and types. An Assembly is the primary unit of
deployment. The individual files that make up a .NET application are call
modules. Types are the basic unit of
encapsulating data and behavior behind an interface composed of public fields,
properties, and methods.
Assemblies
An assembly is used by the .NET CLR
(Common Language Runtime) as the smallest unit for: deployment;
version control; security; type grouping and code reuse. Assemblies consists of
a manifest and one or more modules and/or files like HTML, XML, images, video
clips,...
An assembly can be thought of as a logical DLL and must contain a single
manifest and may optionally contain type meta data,
MSIL (Microsoft Intermediate Language) and
resources.
Assemblies come in 2 flavors: application private and
shared. Application private assemblies are used by
one application only. This is the default style of assembly. Such assemblies
must reside in the application folder.
Shared assemblies are meant to be used by more than one application. They must
have a globally unique name and must be defined in the
GAC (Global Assembly Cache). To learn more about viewing the GAC
click here.
Manifest (Assembly Meta Data)
The manifest makes an assembly self describing
and can be viewed with the IL Disassembler - Intermediate
Language Disassembler. The IL Disassembler (Ildasm.exe) is included
in the .NET Framework SDK and runs from a command line.
The manifest identifies the assembly, describes its security requirements, lists
other assemblies it depends on, and lists all of the types and resources
exposed by the assembly. If any of the resources exposed by the assembly are
localized, the mainfest contains the default culture
(language, currency, date/time format, etc.) the application will target.
The manifest will be discussed further later on.
Modules
A module is either a DLL or an EXE (Windows
Portable Executable - PE) file. It contains IL
(Intermediate Language), associated Meta Data and optionally the
assembly's manifest. IL is a platform independent way of representing the managed
code within an application. Before the managed code is executed the CLR
compiles the associated IL into native machine code.
Types
A type describes the encapsulation of data and an
associated set of behaviors. Types are scoped at the assembly level.
Reference types can be thought of as classes and value
types as structures (VB6 user-defined types).
Types have properties, methods and fields. Fields are
variables used to hold values. Properties are like
fields but with code behind them. Methods are the
public procedures that define the behavior of the class.
Versioning
The CLR supports versioning of shared assemblies via the GAC and allows
for side by side versioning and Automatic
QFEs (hotfixes). Side by side versioning means multiple versions of
the same component can be stored in the GAC at the same time. Automatic QFE
support means that if a compatible newer version of a component is available,
the CLR will use it.
Manifest - Revisited
The manifest contains several sections: Identity, Referenced
Assemblies, File List and Custom Attributes.
The first two sections are discussed subsequently.
Identity Section of the Manifest
The Identity section uniquely identifies an
assembly. To find it using the IL disassembler look for a .assembly
directive in the manifest with out the extern directive. Example:
.assembly Microsoft.VisualBasic
The CLR appends .dll to the name (ex. Microsoft.VisualBasic) to find the
actual file containing the assembly.
The version directive specifies the version of the assembly in major version :
minor version : build : revision format:
.assembly Microsoft.VisualBasic
.ver 7:0:0:0
Assemblies with the same name but different versions (major and minor version
numbers) are treated as completely different assemblies by the CLR.
The Identity section optionally contains a strong name
for private assemblies. Strong names are required for shared assemblies. A
strong name uses public/private key encryption to uniquely identify the
assembly and distinguish between assemblies with the same name.
A public key is generated by the assembly author using the SN tool (sn.exe)
included in the .NET Framework SDK. The public key is stored in the manifest
and a signature of the file containing the assembly's manifest is stored in the
resultant PE file. The CLR uses these two signatures to resolve type references
to ensure the correct assembly is loaded at runtime. The public key is stored
using this directive:
.publickeytoken = (xxxxx)
Optionally contained in the Identity section is the culture which defines
the country and language the assembly is targeted for. The .locale directive
is used for this purpose. Culture-neutral assemblies
can be used for any culture.
Referenced Assemblies Section of the Manifest
The Referenced Assemblies section holds
information about all assemblies referenced by your assembly. The .assembly
directive accomplishes this:
.assembly extern
.publickeytoken = (xx xx xx)
.ver 1:0:241:0
As stated before, the CLR appends .DLL to the name to find the physical file. If
the referenced assembly has a strong name, the .publickeytoken will
contain a hash of the referenced assemblies public key. Version and culture
directives are used for externally referenced assemblies as described above.
Deployment
Recall, assemblies can be application private or shared.
Application private assemblies must reside in the application folder and do not
need a strong name. They only need a name and version in the Identity section
of the manifest. If a strong name exists the CLR checks the strong name of the
assembly and referenced assemblies to see if they match.
Shared assemblies are used by many applications and must have a globally unique
name based on their strong name. Their information is stored in the GAC which
is typically the Assembly folder in the Windows
directory.
To learn more about viewing the GAC, click here.
Deployment
Applications can be deployed in an isolated fashion termed application
isolation where the application is self contained and independent.
All modules of an application are managed by the application. If a component is
used by another application, even if it's the same version, that application
has its own copy. Thus each application can be installed and removed
independently. .NET provides for this using application private assemblies.
Side by side execution is when multiple versions of
the same assembly can execute at the same time on the same PC or within the
same process. Again, the CLR allows for this. Recall, a version number has four
parts. The major and minor parts determine if a component version is compatible
with a prior version. If they differ, they are not compatible. If only the
build and revision numbers differ, the components are compatible. This is
called Quick Fix Engineering (QFE).
Unless told otherwise, .NET uses the default versioning policy
to decide what version of a referenced assembly to use. If the referenced
assembly doesn't have a strong name it is assumed to be application private and
to reside in the application folder. The CLR will load it regardless of whether
its version matches what's in the calling assembly's manifest. Thus, version
numbers of application private assemblies are not checked. If the referenced
assembly is not found in the application folder, an error occurs.
Quick Fix Engineering
If a referenced assembly has a strong name the load process is as follows:
-
Assembly configuration files (discussed later) are examined to see what version
of a referenced assembly to load.
-
The CLR checks if the assembly was loaded in a previous call. If so, it is
used.
-
If the assembly isn't loaded the GAC is queried for a match and if found that's
what's loaded.
-
If a configuration file has a codebase entry for the assembly the file
specified by this entry is used.
-
If none of the above exist, the CLR looks for the referenced assembly starting
in the application folder.
-
If still not found, the CLR asks the Windows Installer service if it has
the assembly. If so, the assembly is installed and this is the assembly used.
This is call install on demand.
-
If the assembly still cannot be found, an error is raised.
Just because a referenced assembly has a strong name doesn't mean it has to be
deployed to the GAC. A developer can install a known good version with the
application. The GAC is checked to see if it has an assembly with a higher build.revision
number. This allows deployment of an updated assembly without having to
re-install or rebuild the application. This is known as Automatic
Quick Fix Engineering Policy.
Configuration Files
Three configuration files can alter the above policy. The application
configuration file which resides in the application folder and has
the same name as the application file with .config appended to it (Ex:
myExe.exe.config) is, as the name implies, application specific.
The machine configuration file named
machine.config and located in the
\Config folder overrides all application configuration
file(s) on a machine. Lastly the security configuration file
allows granting/denying access to resources by an assembly.
Configuration files are XML files used to override the default policy used by
the CLR. They have a <startup> section which specifies the version
of the CLR to use for the application. This is because different versions of
the .NET runtime can run side by side on a machine. Click
here for more info.
The <runtime> section has a few key elements
that can specify the version of an assembly to use either all the time or to
replace a specific version of the assembly.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas=microsoft-com:asm.v1">
<dependentAassembly>
<assemblyIdentity name="AssemblyName"
publickeytoken="b8838383d8s88"
culture="en-us"/>
<bindingRedirect oldVersion="*"
newVersion="2.0.34.0"/>
</dependentAassembly>
</assemblyBinding>
</runtime>
</configuration>
When the CLR resolves the reference to AssemblyName it loads version
2.0.34.0 instead of the version stated in the manifest. Instead of replacing
all versions with 2.0.34.0 the oldVersion element can specify the exact
version to replace.
The exact location of the assembly to load can be specified with the
<codeBase> element. Thus, on demand
downloading can be employed to distribute an application and have an
externally referenced assembly downloaded the first time it is used.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas=microsoft-com:asm.v1">
<dependentAassembly>
<assemblyIdentity name="AssemblyName"
publickeytoken="b8838383d8s88"
culture="en-us"/>
<codeBase version="2.0.34.0"
href="http://www.thescarms.com/mydll.dll"/>
</dependentAassembly>
</assemblyBinding>
</runtime>
</configuration>
An application configuration file can also specify the search path for the CLR
to use. By default the CLR looks in the application folder and not sub folders.
This can be changed by specifying a relative path or list of relative
folders separated by semi-colons.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas=microsoft-com:asm.v1">
<probing privatePath="myfolder"/>
</assemblyBinding>
</runtime>
</configuration>
The CLR will look in the application's folder, all specified sub folders and a
sub folder with the same name as the assembly name. Further, it a culture is
used, the CLR will look in a culture specific sub folder of each of the
directories it looks in. For example, if an assembly named "AssemblyName"
resides in "C:\MyApp" and the culture is "en" the CLR will look in:
C:\MyApp
C:\MyApp\en
C:\MyApp\en\AssemblyName
C:\MyApp\myfolder
C:\MyApp\myfolder\en\AssemblyName
|