I’m often being asked to resolve various problems in which the .NET Assembly loader does not find the right assembly to load and therefore doesn’t load it at all.

While this is not hard core debugging that you would usually expect using WinDbg, CorDbg, MDbg and others, this is a very common case that with a little more information about how .NET works and one simple tool, can be easily resolved in both development and production environments.

Did you see the error “File or assembly name XXX, or one of its dependencies, was not found” error (replace XXX with the relevant assembly name) before?

That’s exactly the error I’m talking about.

Before we will go into common problems and how to resolve them, let us first understand how the .NET assembly loader actually works and how and where it searches for the assembly we want to load.

Strongly Signed Assembly
A strongly signed assembly is an assembly that was signed using a key pair during compilation (or after it).
The key pair is generated using the “sn.exe” utility and can be unique per assembly, per product or per publisher (depending on your needs).

Strong signing an assembly gives the assembly a unique identifer which is composed out of the following parameters:

  1. Assembly Name – the file name of the assembly minus the “.dll” suffix.
  2. Assembly Version – The version of the assembly as defined in the AssemblyInfo file. It can contain a fixed number which the developer / build machine will advance manually or have the “*” sign at the end so each build will increment it automatically.
  3. Culture – The deafult value for this parameter in most assembly is “neutral” meaning, that this assembly is not culture specific. It is mainly used to create assembly (or satellite assemblies) that are culture specific such as a specific assembly which has resource string that contains German text instead of English text. In which case, the culture will be “de”.
  4. Public Key Token – A token generate from the public key that you have in your key pair. It is used to verify the signature of the assembly to check that it was not tempered with after it was signed.

All of these parameters together create, what is referred to as, a “Fully Qualified Name” of a .NET assembly and looks like this:

MyAssembly, Version=1.0.2.3, Culture=neutral, PublicKeyToken=b77a5c561934d069

A fully qualified name for a specific type will look like this:

MyType, MyAssembly, Version=1.0.2.3, Culture=neutral, PublicKeyToken=b77a5c561934d069

If there is a program that needs to load some type and create it dynamically and the assembly of that type is strongly signed you can use the following piece of code to load and create that specific type given its fully qualified name:

string typeFullQualifiedName = “fully qualified name of type here”;
Type type = Type.GetType(typeFullQualifiedName);
if (type != null) {
object obj = Activator.CreateInstance(type);
}

This piece of code will try to load the relevant assembly (we will talk about the .NET assembly loader logic next) and if it succeeds, it will get the Type object which will allow us to create an instance of that type.

If you don’t have a full qualified name of the type, you can always use System.Reflection.Assembly.Load, give it the full path (or relative path) to the assembly and call assembly.GetType with the type you want to get and continue from there on.

.NET Assembly Loader – The real story

The first thing the .NET assembly loader checks is whether the assembly is strongly signed. If it is, it will start its search in the Global Assembly Cache (a.k.a the GAC. You can read more about it here).

The loader will search for a policy file named in the format of:
policy.AssemblyMajorVersion.AssembyMinorVersion.AssemblyName

For example:
Policy.1.2.MyAssembly

If such a file exists it will look inside of it if the version of the assembly that we are trying to load matches the version/versions range written in the policy file. If it does, it will try to load the assembly with the version specified there. If no such policy file exists, it will try to load the assembly from the GAC.

If it will fail to find it in the GAC, it will start to serach in the system’s search path.
In web applications it will also include the application’s Bin directory in the search path.

You can manully add folders to an AppDomain’s search path by using the “AppendPrivatePath” method.

So, what should I do if it can’t find the assembly?
The best method I’ve found to check if there is a policy problem or a path/version problem is to use FileMon from SysInternals.

FileMon tracks all activities that relates to accessing files in your operating system including read, write, access denieds on files and so on.

I would suggest adding a filter with the name (or partial name) of your application and start the application.

It will start showing you all file access that your appliction does. When you’ll receive the assembly load error, search for “NOT FOUND” or “ACCESS DENIED”.

If you have policies for that specified assembly you will see that the application accessed the policy file and directly after it will try to load the assembly with the version specified in the policy file.

If this is not a strongly signed assembly or the file is not in the GAC you will see all the places the application searched for to load the file.

Summary
This is a very easy trick that saves long hours of debugging, specifically in a production or semi-production environment.

If you are using the various abilities of .NET for side-by-side versioning, policy files and the GAC, do mind that you WILL run into these simple problems that consumes a lot of time and effort.

If this post helped you, comment on this post or just Email me. I would like to know that I’m not the only one fighting with the GAC and policy files 🙂

  • Chris Melinn

    Eran,

    I used to get random “Access is Denied” errors to various DLLs. I finally tracked the problem down to the Indexing Service. By simply stopping the Indexing Service, I stopped getting the error.

    It doesn’t sound like the type of error you are facing, but I wanted to post it for anyone else dealing with this headache.

  • I’m experiencing this with a VB.Net Win Forms application. I’ve tried referencing the a .dll multiple ways in my application (the project, a network dll, and one in my GAC).

    I publish the application and get the “could not load assembly” error on the client. The weird part is that it calls the same dll multiple times before finally failing with a “path not found” error (I see this by using ProcMon). I have created a strong typed name on the client and put the library in the GAC. The fully qualified name in the error message matches the information in the GAC.

    I also tried disabling the Indexing Service.

    Lost…

  • Clint, What is the code you use to load the assembly? There are various ways to dynamically load an assembly in .NET. I might be able to help if I could see part of the code.