Infosys Microsoft Alliance and Solutions blog

« How many Threads have I got? | Main | Session 0 and 1 in Vista »

Inconsistent Behavior between Assembly.Load overloads

Recently while working on a project, I came across an interesting behavior of Assembly.Load method and its different overloads. I specifically tried with Assembly.Load(AssemblyName) and Assembly.Load(string) overloads.

My testing showed that the overload that accepted a string depended on the value of Environment.CurrentDirectory. Usually when using Assembly.Load method, one would expect the assembly to be loaded from the application's root folder.

In my case, I had a DLL (lets call it ClassLibrary1.dll) which I was trying to load via reflection. Since it was in the application's root folder and folder itself not known (will depend on Installation), I had decided to use Assembly.Load method. I could have used the Application.StartupPath to resolve the path to the assembly and then loaded it, but I decided to go ahead with Assembly.Load. The code looked like

    Assembly al = Assembly.Load("ClassLibrary1");
    Type t = al.GetType("ClassLibrary1.Class1");
    /*...further logic...*/

This worked fine till I had a need to introduce a OpenFileDialog before the call to the Assemly.Load method for some other requirement. The call to that caused the Environment.CurrentDirectory to be set to the path from where the file was loaded and the Assembly.Load call started to fail.

Alternatives exist and I could either use OpenFileDialog.RestoreDirectory option or set the Environment.CurrentDirectory to Application.StartupPath before calling the Assembly.Load method. However the interesting point is that if I used the other overload of Assembly.Load method, things worked fine without these alternatives.

    AssemblyName asmName = new AssemblyName();
    asmName.Name = "ClassLibrary1";
    asmName.Version = new Version(1,0,0,0);
    Assembly al = Assembly.Load(asmName);
    Type t = al.GetType("ClassLibrary1.Class1");
    /*...further logic...*/

The ClassLibrary1.dll wasn't strong named, but making it strong named didn't change this behavior. The setting of Version in the above code snippet is optional and it works fine without setting this also.

This clearly meant that the Assembly.Load(string) overload did something different than Assembly.Load(AssemblyName) and you can very well see this by using Lutz Roeder's Reflector. When you go the string overload method route, there is an extra call to an internal AssemblyName.nInit method, which isn't called when we use the AssemblyName overload route. This call looks like below (as shown by Reflector)

    if (assemblyRef.nInit(out assembly, forIntrospection, true) == -2146234297)
        return assembly;

When we use the string overload, it internally does creates the AssemblyName instance and assigns to the Name property, but due to this additional call to nInit method, the behavior seems to be different. I haven't yet figured out what really happens because this will require exploring the source code and understanding the AssemblyNameNative:Init method in assemblyname.cpp

Hence when using the Assembly.Load methods be careful about the Path from where you expect the files to get loaded and which overload you are using. Note that Activator.CreateInstance(string assemblyName, string typeName) internally uses the Assembly.Load(string) overload and hence it suffers from the same problem that i just described.


Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter