Reflection From a DLL and Serialization

Last week, we finished up our main series on reflection.  For most people, what we covered is all they will ever need.  However, there are a few other things I’ve needed to know due mainly to the fact that I was using reflection from a DLL instead of an EXE

When I wrote VSS.NET, which was my first .NET application, allow Visual SourceSafe to be used over the Internet, I wanted to implement the SCC api.  This is the API that allows you to use VSS from the IDE.  For some reason that I can’t remember now and isn’t important to this discussion, I needed to use reflection from the DLL that the SCC interface was calling.  This posed two problems.

Reflection From a DLL
First, because the DLLs were being called from an unmanaged DLL that might be called from ANY EXE (VB6, Visual Studio 6, Visual Studio 2002, etc) I needed some way of loading the reflected DLLs from where the main DLL was located, or I needed to go through the trouble of putting the managed DLLs in the GAC.  I opted to load them based on the directory the unmanaged DLL was in.  For this, you’ll need to use Assembly.LoadFrom() instead of Assembly.Load().

That was easy enough to fix.  But, the second issue I ran into took me quite a while to track down.

Using Serialization and Reflection from a DLL
You see, my main DLL was serializing the configuration settings to a file and needed to deserialize that information when the main object was created.  Buried deep in the documentation is the fact that when you use Serialization, .NET will look for the DLLs and objects based on the location of the EXE, even if you’ve already loaded the DLLs into memory.

This means that if the DLLs you are reflecting are in a different directory, you’ll never be able to deserialize the object from the file!

There is a solution.  .NET provides an event handler off the current Appdomain that you can access via, System.AppDomain.CurrentDomain.AssemblyResolve.  The AssemblyResolve event fires when .NET can’t find the proper DLL for an assembly it is trying to load.

So, all you need to do is provide a function that loads the DLL from the right location, and returns the Assembly object.

If you’ve already loaded the assembly, all you have to do is find the assembly in the current app domain and return it.  So, my event handler looks like this:

public static Assembly MyResolver(Object sender,ResolveEventArgs args)
{
    // Get all of the loaded assemblies
    Assembly[] Assemblies = AppDomain.CurrentDomain.GetAssemblies();
    // Get the name of the assembly we need to return
    String name = args.Name.ToUpper().Substring(0,args.Name.IndexOf(","));
    // If it is this assembly, just return this assembly
    if(args.Name.StartsWith("VSS_NET,"))
        return Assembly.GetExecutingAssembly();
    // Otherwise, find the assembly in the list of
    // assemblies.
    foreach(Assembly asm in Assemblies)
    {
        if(asm.FullName.ToUpper().StartsWith(name))
            return asm;
    }
    // if we still haven't found it, use load from and load
    // from the same directry this dll is in.
    String currentLocation = Assembly.GetExecutingAssembly().Location;
    Assembly newAsm =
      Assembly.LoadFrom(currentLocation.ToUpper().Replace("VSS_NET",name));
    return newAsm;
}

 

All that is left is to wire up the event handler prior to deserializing:

System.AppDomain domain = System.AppDomain.CurrentDomain;
domain.AssemblyResolve += new ResolveEventHandler(VSSController.MyResolver);

Related Post

One Response to “Reflection From a DLL and Serialization”

Leave a Reply

Comment Policy:

  • You must verify your comment by responding to the automated email that is sent to your email address. Unverified comments will never show.Leave a good comment that adds to the conversation and I'll leave your link in.
  • Leave me pure spam and I'll delete it.
  • Leave a general comment and I'll remove the link but keep the comment.

Notify me of followup comments via e-mail

Bear