• Feeds

    Subscribe in a reader

  • Ads

Loading an assembly from a "working directory"at design time

Loading anarbitrary assembly into a design-time tool in order to do interesting thingswith it seems to be a somewhat common behavioral pattern for development tools.I know of several developer tools that have this general feature –Reflector is the canonical example. We have a couple of internal tools at workthat do the same sort of thing. They all have to solve the problem of loadingan assembly that lives outside of the application’s BaseDirectory.

 

Loading such anexternal assembly via Assembly.LoadFile() is straightforward, provided that theassembly you’re loading does not have dependencies on other assembliesthat must also be loaded. If those dependencies are not easily located by CLRAssembly Loader (either by living in the GAC or running app’s probingdirectories), you’ll get an exception unless you do a little bit of extrawork. You’d like Fusion to probe for dependencies in a “workingdirectory” – the directory from which you’re loading theassembly, but it won’t do that automatically. Fortunately, this work isminimal but obscure.

 

The solution isto subscribe to the AppDomain.ResolveEvent before you attempt to load theexternal assembly. This gets fired by the loader just before an exception getsthrow, and gives your app a last-second chance to try to find the assembly. Youcan then attempt to do an Assembly.LoadFile() from whatever directory you want.It’s very easy to use this event to implement a “working directory”concept. All you have to do is keep track of the directory from which youloaded the original assembly and use the ResolveEvent to probe there too.

 

Here’ssome example of a quick-and-dirty implementation that might live on some formsomewhere:

 

private void button1 Click(object sender, System.EventArgs e)
{
  FileDialog d = newOpenFileDialog();

  if( d.ShowDialog() ==DialogResult.OK )
  {

   string fileName =d.FileName;
    this.workingDirectory= Path.GetDirectoryName( fileName );
    Assembly asm = Assembly.LoadFile( fileName );

   DoInterestingThings( asm );
  }
}

 

private AssemblyCurrentDomain AssemblyResolve(objectsender, ResolveEventArgs args)
{
   //Args.Name is the assembly’s full name (“MyAssembly,Version=[etc]”)
   //We want just the part before the comma so we can derive the filename

  string fileBase =args.Name.Split(',')[0];

   if( workingDirectory!= null )

  {

     string fileName = fileBase +".dll";
     string workingPath= Path.Combine( workingDirectory, fileName );
     returnAssembly.LoadFile( workingPath );
   }

   return null
;
}

 

And,of course, the event handler wireup:

 

//Somewherein your initialization code

AppDomain.CurrentDomain.AssemblyResolve+= newResolveEventHandler(CurrentDomain AssemblyResolve);

 

There are stillways when this might fail, but it does handle the common cases. Of course, allthe standard caveats of Assembly.LoadFile() apply – no load context andwhatnot – but it works well for a dev tool.