The woes of renaming assemblies for the uninitiated...

First off, read this Junfeng's blog post http://weblogs.asp.net/junfeng/archive/2004/01/29/64272.aspx as it will provide you with some background as to the problem.  However, we are going to examine a more explicit issue, and instead of using Assembly.Load we are going to use Assembly.LoadFrom to load the assembly in question.

In Junfeng's example, the second AppDomain couldn't find the executable because it had been renamed.  So the probing logic failed here.  However, if we had instead used LoadFrom then the file would have been loaded anyway.  Now, make a copy of say Hello1.exe and call it Hello2.exe.  When you try a LoadFrom on Hello2.exe you'll get an error saying the assembly has already been loaded.  This is actually quite hilarious because you have two files with different names, but the CLR is telling you they are the same assembly (really they are).  What happened is you failed to rename the assembly inside of it's manifest.  Let's take a look at some IL.

.assembly MetaFiler
{
  // --- The following custom attribute is added automatically, do not uncomment -------
  //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(bool,
  //                                                                                bool) = ( 01 00 00 01 00 00 )
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module MetaFiler.exe

For my particular application (I didn't use the Hello's, just some random app I've made) you'll see that the actual assembly name is MetaFiler.  The module, which has been ILDasm'ed is MetaFiler.exe.  The reason behind this is that a single assembly can be spread over multiple files.  This is why the file name really isn't all that important.  What is important is that somehow the CLR be provided with an assembly that contains the manifest information with a name matching the assembly name.

This brings me to the AssemblyResolve event on the AppDomain.  You see, if the CLR fails to load your assembly, you get a chance to try to find the assembly yourself.  This is kind of powerfuly, because this is where you hook-in for things like downloading the assemblies from the internet or reparing your application when a user deletes assemblies.  So in Junfeng's case the problem can be defined as follows:

1.  The user wants to rename your assembly.
2.  They rename it and the application still runs because of the default probing logic.
3.  Your application creates a second app domain which needs access to some types in your assembly.
4.  Your application fails because the second app domain can't load the needed assembly.

Solution? Explicitly handle the AssemblyResolve event and short-circuit the process of loading only your assembly into the new AppDomain.  This situation is extremely rare and only occurs when you have a second AppDomain in your application.  Normally renaming your assemblies has little or no impact.  AssemblyResolve has a boatload of other cool uses though.  Maybe one day I'll get around to talking about how we used it in the Terrarium (to this day one of the largest user submitted bugs is that they renamed assemblies so they could introduce more than one instance of something, normally a plant, and it fails to load saying same assembly.  I don't think they get it George...)

Published Sunday, February 01, 2004 3:30 AM by Justin Rogers

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required)