Load contexts and Type.GetType

Type.GetType(AN,T) actually translates to Assembly.Load(AN).GetType(T). This actually can be the cause of many confusions. Often programmers end up in a situation where they have explicitly loaded the assembly, but Type.GetType is not able to find the type from the assembly they just loaded.

 

Let us look into the sample below. Let us do a Type.GetType for Foo and provide the path to the location of Foo in the config file as mentioned below.

 

Copy the source below to TypeSample.cs and the config to TypeSample.exe.config

using System;

using System.Reflection;

class DomSample {

    public static void Main(){

        try {

            Type ty1 = Type.GetType("Foo, Foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1faea1974f697f94");

            if (ty1 != null) {

          Console.WriteLine("V1 Type = {0}", ty1.Assembly.FullName.ToString());

            }

            else {

                Console.WriteLine("Unable to find type ...");

            }

        }

        catch (Exception e) {

            Console.WriteLine(e.ToString());

        }

    }

}

<configuration>

   <runtime>

      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

         <probing privatePath="v1" />

      </assemblyBinding>

   </runtime>

</configuration>

Make sure that you create a path v1 under APPBASE and create a Foo.cs which is compiled and signed to Foo.dll. Copy the following source to Foo.cs and comple it to Foo.dll and sign it with the key (see my article on signinig)

using System;

using System.Reflection;

[assembly: System.Reflection.AssemblyVersion("1.0.0.0")]

public class Foo {

    public void Bar() {

        Console.WriteLine("Version = {0}", (Assembly.GetExecutingAssembly()).ToString());

    }

}

If you run TypeSample.exe you will get the following output

V1 Type = Foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1faea1974f697f94

If you run the sample under ntsd you will notice that Foo.dll is loaded from directory v1 in the default AppDomain

Opened log file 'ntsd.log'

0:000> !dumpdomain

--------------------------------------

System Domain: 7a390b60

LowFrequencyHeap: 7a390b84

HighFrequencyHeap: 7a390be0

StubHeap: 7a390c3c

Stage: OPEN

Name: None

--------------------------------------

Shared Domain: 7a391138

LowFrequencyHeap: 7a39115c

HighFrequencyHeap: 7a3911b8

StubHeap: 7a391214

Stage: OPEN

Name: None

Assembly: 001e8828

--------------------------------------

Domain 1: 001b0890

LowFrequencyHeap: 001b08b4

HighFrequencyHeap: 001b0910

StubHeap: 001b096c

Stage: OPEN

SecurityDescriptor: 001b1a98

Name: DomSample.exe

Assembly: 001e8828 [C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]

ClassLoader: 001e88c0

SecurityDescriptor: 001e8738

  Module Name

790c2000 C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly: 001f95e0 [c:\blog\May2007\DomSample.exe]

ClassLoader: 001f9678

SecurityDescriptor: 001f94e0

  Module Name

00712c24 c:\blog\May2007\DomSample.exe

Assembly: 00208128 [c:\blog\May2007\v1\Foo.dll]

ClassLoader: 00208398

SecurityDescriptor: 00208298

  Module Name

007130d8 c:\blog\May2007\v1\Foo.dll

Now let us modify the code above to use LoadFrom and then try the Type.GetType.

Copy the code below to TypeSampleLoadFrom.cs and compile it to TypeSampleLoadFrom.exe. Let us not provide a config file this time such Type.GetType cannot find the assembly Foo when it tries to load it.

using System;

using System.Reflection;

class DomSample {

    public static void Main() {

        try {

            Assembly.LoadFrom("c:\\Blog\\May2007\\v2\\Foo.dll");

            Type ty1 = Type.GetType("Foo, Foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1faea1974f697f94");

            if(ty1 != null) {

                Console.WriteLine("V1 Type = {0}",ty1.Assembly.FullName.ToString());

            }

            else {

                Console.WriteLine("Unable to find type ...");

            }

        }

        catch(Exception e) {

            Console.WriteLine(e.ToString());

        }

    }

}

The above program yields the result "Unable to find type ...". If you watch the program under ntsd you will notice that Foo.dll is loaded from the LoadFrom, but the Type.GetType was not able to use the already loaded Foo.dll or find Foo.dll to load it to retreive the type information

0:001> !dumpdomain

--------------------------------------

System Domain: 7a390b60

LowFrequencyHeap: 7a390b84

HighFrequencyHeap: 7a390be0

StubHeap: 7a390c3c

Stage: OPEN

Name: None

--------------------------------------

Shared Domain: 7a391138

LowFrequencyHeap: 7a39115c

HighFrequencyHeap: 7a3911b8

StubHeap: 7a391214

Stage: OPEN

Name: None

Assembly: 002e86e8

--------------------------------------

Domain 1: 002b08c8

LowFrequencyHeap: 002b08ec

HighFrequencyHeap: 002b0948

StubHeap: 002b09a4

Stage: OPEN

Name: DomSample1.exe

Assembly: 002e86e8 [C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]

ClassLoader: 002e8780

SecurityDescriptor: 002f4ef8

  Module Name

790c2000 C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly: 002fa790 [c:\blog\May2007\DomSample1.exe]

ClassLoader: 002fa828

SecurityDescriptor: 002fa690

  Module Name

00102c24 c:\blog\May2007\DomSample1.exe

Assembly: 00302bc0 [c:\Blog\May2007\v2\Foo.dll]

ClassLoader: 003072b0

SecurityDescriptor: 00306b88

  Module Name

00103120 c:\Blog\May2007\v2\Foo.dll

Let us modify the above sample and add a config TypeSampleLoadFrom.exe.config as follows

<configuration>

   <runtime>

      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

         <probing privatePath="v1" />

      </assemblyBinding>

   </runtime>

</configuration>

You will get the output V1 Type = Foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1faea1974f697f94.

If you run the program under the debugger you will find that there are two Foo.dll loaded in the default AppDomain one loaded from the LoadFrom context and the other load by Type.GetType in the other context. I had specifically placed the same Foo.dll in different locations to make this happen. The Foo.dll loaded by Type.GetType was loaded from v1 directory and the Foo.dll loaded from the LoadFrom context was loaded from v2 directory. If you had both point to the same location, you will however see only one copy of the dll.

0:000> !dumpdomain

--------------------------------------

System Domain: 7a390b60

LowFrequencyHeap: 7a390b84

HighFrequencyHeap: 7a390be0

StubHeap: 7a390c3c

Stage: OPEN

Name: None

--------------------------------------

Shared Domain: 7a391138

LowFrequencyHeap: 7a39115c

HighFrequencyHeap: 7a3911b8

StubHeap: 7a391214

Stage: OPEN

Name: None

Assembly: 003386f8

--------------------------------------

Domain 1: 003008a0

LowFrequencyHeap: 003008c4

HighFrequencyHeap: 00300920

StubHeap: 0030097c

Stage: OPEN

SecurityDescriptor: 00301aa8

Name: DomSample1.exe

Assembly: 003386f8 [C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]

ClassLoader: 00338790

SecurityDescriptor: 00344ed0

  Module Name

790c2000 C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly: 0034a7c8 [c:\blog\May2007\DomSample1.exe]

ClassLoader: 0034a860

SecurityDescriptor: 0034a6c8

  Module Name

002d2c24 c:\blog\May2007\DomSample1.exe

Assembly: 00365b00 [c:\Blog\May2007\v2\Foo.dll]

ClassLoader: 00365998

SecurityDescriptor: 00358038

  Module Name

002d3120 c:\Blog\May2007\v2\Foo.dll

Assembly: 00366bd0 [c:\blog\May2007\v1\Foo.dll]

ClassLoader: 00366a58

SecurityDescriptor: 00365c48

  Module Name

002d3548 c:\blog\May2007\v1\Foo.dll