free hit counter script
plans.theFrankes.com Socialize: Stumble It! Digg It! del.icio.us! reddit! Google bookmark!
Email this pageEmail this page Feed index Feed Index
Skip Navigation Links
Home
Recording StudioExpand Recording Studio
Alibre DesignExpand Alibre Design
CodeExpand Code
WoodworkingExpand Woodworking
Et CeteraExpand Et Cetera
Skip Navigation LinksHome : Code : Programming Tutorials : Understanding .Net Assemblies & References
Help me go to college!


New Discussion Forums!

Join the brand new discussion forums today and help the community grow by contributing your questions, comments, ideas, and expertise! Join now!








Google Search

Understanding .Net Assemblies & References
An introduction to the nuances of .Net assemblies and references for anyone who's be bewildered by their intricacies.

All images, text and code is ©1995-2008 by Alex Franke.
All rights reserved.
Published: Jan 6, 2006
Updated: Oct 28, 2006

In this article:

Assembly Manifest

Every .Net assembly contains a manifest that contains descriptive information about the assembly, including the assembly identity information (name, etc, see below), what files make up the assembly, what types or functionality can be called from a different assembly, and what other assemblies it depends upon. You can view the assembly manifest by opening it with the ildasm tool. Run ildasm.exe from the .Net Command Line.

The assembly manifest doesn’t store the paths used when you add a reference through the IDE – it only stores information about the assemblies being referenced.

Excellent advanced .Net references - Click to buy
Professional .NET Framework 2.0 (Programmer to Programmer)
$26.39
Code Complete: A Practical Handbook of Software Construction
$31.49
.NET Framework Standard Library Annotated Reference, Volume 1: Base Class Library and Extended Numerics  Library (Microsoft .NET Development Series)
.NET Windows Forms in a Nutshell
$34.16
The Dark Art of C# Programming: .Net Core Language (Series)
$19.95
.NET Framework Solutions: In Search of the Lost Win32 API
$50.99
Inside Microsoft  .NET IL Assembler
Professional Visual Studio 2005
$31.49

IDE References

In order to write code that uses the functionality of a different component, the development environment needs to know what that component is capable of doing and how to call its functionality. In Visual Studio, you make your application aware of other components by “adding references” to them. When a reference to an assembly is added, the IDE will inspect the manifest of the referenced assembly to see what it can do and how it should be used. You can add File, Project, COM, .Net, Web, and dynamic references to Visual Studio projects. I’ll discuss File and Project references in more detail, and the others below, plus a bit about VB6 references as they relate to .Net.

In this context, references are for the IDE and build process, not for application execution.

Project References: When you use a project reference (preferred, if at all possible), then you’re telling a project that it depends upon the output of another project in the same solution. When the solution builds, the IDE builds the dependencies first, then finds the output DLLs in the output paths associated with the current configuration. (If you’re in Debug configuration, it’ll find that output file in the referenced project’s Debug bin, and if you’re in Release configuration, it’ll find it in its Release bin.) Once found, the file is copied into the obj directory of the application, then to the bin directory when the build is complete. (The obj directory is basically a temporary working folder that the IDE uses during builds.) This is all pretty handy, and that’s why it’s preferred.

If a dependency can’t be built, the IDE will (by default) keep going down its list of builds and you’ll probably get an error when the referencing assembly tries to build and the file can’t be found. Project references can only be exchanged among projects in the same solution. (Projects, by the way, are perfectly happy existing in more than one solution at a time.)

File References: When you use a file reference, you actually specify the exact path that the IDE will use to find the referenced assembly so it can examine its manifest and learn how it works. If the referenced file is not in the GAC and not a framework component, then the Copy Local property of the reference will default to True, meaning that during build, the IDE will follow the reference path, fetch the file, and copy it into the obj and bin directories so it can be found and used at run time (see below). This can have unfortunate side effects, though, if more than one version of the same file name is referenced along the dependency chain. (The file system knows nothing about assembly names or assembly versions and only considers file names when copying a file from one place to another.) For this reason, it’s a good idea to disable "Copy Local" in these cases. For example, if

  • MyApp references MyUtilDll (v 2), and
  • MyApp references MyBllDll (v 2), and
  • MyUtilDll (v 2) references MyBllDll (v 1)
...then Copy Local will do more harm than good because one of the MyBllDll DLLs will get copied over another when the solution is built.

Other reference types: Here’s a brief overview of the other types of references:

  • COM – A reference is made to a COM interop component that’s installed in the registry.
  • .Net – A reference is made to a specific subset of GAC assemblies whose locations have been listed in the system registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders. Otherwise, like a file reference.
  • Web – A proxy class is generated from information in the WSDL and is added to the project. No other files are required.
  • Dynamic – A reference is made at run-time using a method like Assembly.Load(), which causes the referenced assembly to be loaded on the fly.

VB6 References: Because VB6 knew nothing about .Net when it was born, it wouldn’t know what to do with an Assembly if it were given one. Because of this, Microsoft created a way for .Net assemblies to masquerade as COM objects and a tool that generates a type library from an assembly (tlbexp.exe). A type library is the ActiveX equivalent of an assembly manifest – it contains lots of interface information but no code. Once the functionality of an ActiveX object is discovered using a type library, it’s up to the consuming application to actually find the file that contains the code. It does this by way of the system registry. First it looks up the fully qualified name of the class being instantiated (e.g. MyCompany.MyTechnology.MyClass) in H_KEY_CLASSES_ROOT. It finds that class’ ID (CLSID, a GUID), and then looks up that class ID in H_KEY_CLASSES_ROOT/CLSID. From there it inspects that key’s InprocServer32 key.

The default value of this key contains the path of the application that will be run. In the case of .Net assemblies, this is always mscoree.dll, which serves as a shim, managing the differences between .Net and COM. The other properties of this key tell mscoree.dll how to find the actual .Net assembly that should be treated as a COM component. This may (but not necessarily) include a codebase path to the assembly.

Assembly Name and Identity

An assembly's identity can be strong or weak (full or partial). The file name of the assembly is completely independent and has no impact on the identity of the assembly (except when installing the assembly into the GAC, in which case the names must match). Assembly identities are included with the metadata in the assembly's manifest. Strong names include the assembly name, version, culture, and public key token. Weak names don’t include a public key token.

Assembly References (Dependencies)

Now we need to change the context of the word “reference”. In this context we’re not concerned with the physical location of the file for development and build purposes, but rather the identity of the assembly that the executing assembly depends upon. Recall that the manifest of the running assembly includes full or partial information about the other assemblies it depends upon. It got this information by following the IDE reference paths described above and inspecting the manifests of the dependent assemblies.

These assembly references are resolved and "bound" when the application is run. To do this, the runtime goes through the process of finding the actual assemblies, as described here.

The runtime starts with the dependent assembly name information that was included in the running assembly's manifest, and it then sets out to determine the appropriate assembly version to bind.

Binding Redirects (or Version Redirects) & *.config Files

First, the runtime checks the configuration files ([app].config, publisher policy or the machine.config, in this order) for any elements that might impact the version of assembly that should be bound. This example basically tells the app, "If your manifest says you need assembly 1.5.42.1 (or anything in the "oldVersion" range), then load version 2.0.0.0 instead." This instruction could be in any of the configuration files. The assembly must also be strongly named to be redirected in this manner.

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <dependentAssembly>
            <assemblyIdentity name="someAssembly"
                publicKeyToken="8cb93028ef92ac93"
                culture="neutral" />
            <bindingRedirect
                oldVersion="1.0.0.0-1.65535.65535.65535"
                newVersion="2.0.0.0"/>
        </dependentAssembly>
    </assemblyBinding>

The runtime first checks the [app].config file, which (if present) should be in the application's directory. Next it checks the publisher policy file, which is essentially a different assembly installed in the GAC that affects all applications that use the shared assembly. Any changes in the publisher policy file override those in the application manifest and in the [app].config file. Finally, it checks the machine.config file, which overrides all other settings and basically has the final say. If none of the files overrides the executing assembly’s manifest, then the runtime looks for the assembly in the manifest.

To Load or Not To Load

Now the runtime has the name of the assembly to load, strong or weak. If an assembly of the same name has already been loaded, it will use the pre-loaded assembly instead of loading a new copy of it. If the names are weak, then version numbers will not be compared, which could cause problems. For example, if the weakly-named assembly MyDal.dll (version 1.0) was previously loaded because some other dependency needed to run some code in it, and your code next needed to run some code in MyDal.dll (version 2.0), then the 2.0 version would never be loaded; Because of the weak names, the runtime will see that assembly "MyDal" had already been loaded and will try to use that instead.

The GAC

If an assembly of that name had not already been loaded and if it is strongly named, the runtime will check the GAC for the assembly. If found it will be loaded from the GAC. The GAC is only for strongly named assemblies and is structured in such a way as to not rely on filenames. Many different versions of the same assembly (same filename) can coexist happily in the GAC.

The actual physical location of the files in the GAC is in %windir%\assembly (for example, c:\windows\assembly). With version 2.0 of the .Net framework, the GAC was extended to support different processor architectures. From a command window, you can browse the various processor architecture versions of the GAC, which are organized first by the architecture directory (listed below), then name, and finally version with a key token.

The GAC in %windir%\assembly is organized like this:

  • GAC - The original GAC, for .Net verion 1.0 and 1.1 assemblies (no processor architecture)
  • GAC_32 - 32 bit only assemblies
  • GAC_64 - 64 bit only assemblies (only on 64 bit machines)
  • GAC_MSIL - Portable assemblies, which are compiled with ProcessorArchitecture MSIL and can run on any platform.

That said, it's not advisable to poke around in the GAC because its implemention could change over time. In fact, you can only browse the GAC from a command window because if Windows Explorer recognizes that it's been pointed to the GAC, it will show you the GAC interface GUI instead of the typical file browser.

codeBase Path

If the assembly is still not found, the runtime inspects the element (within ) of the three configuration files mentioned above, each file overriding the other as before. If the assembly is strongly named, the codebase could point anywhere, including, say "http://www.myCompany.com/somepath". If it is not strongly named, the path must be inside and relative to the application directory, and any redirected version information is ignored. In this example, the assembly at "somePath\someAssembly.dll", if not strongly named, will be loaded regardless of any version redirection.

    <codeBase version="2.0.0.0" href="somePath\someAssembly.dll" />

If the element exists for the assembly and it is not found in the specified location, the runtime will give up and cause an exception.

Probing

If there's no element and the assembly hasn't been found, the runtime starts "probing". It starts by searching for the assembly in the same location as the application.

    [application root]/([culture, if specified]/)[assembly name].dll
    [application root]/([culture, if specified]/)[assembly name]/[assembly name].dll

The runtime can also be instructed to search for the required assembly by probing other specific directories, using the element in the config file, or at runtime by calling AppDomain.AppendPrivatePath(). Paths specified at runtime are checked before those specified in the config files. In this example, the element (which is in ) instructs the runtime to probe two additional paths for the assembly:

    <probing privatePath="subdir1;subdir1\sub-subdir" />

These paths are probed in the same manner as before, testing first the path (with culture information if specified), then the path (with culture information if specified) and any subdirectory with the assembly name.


Copyright 2008 by Alex Franke.
All rights reserved. Email: alex at thefrankes dot com
This page and all site content, including downloadable plans, tutorials, images and code are
copyright 1987-2008 by Alex Franke unless otherwise noted. All rights reserved.
Plans may not be used for commercial purposes without express written permission.