Markus bugged me this morning about a problem with an import of a couple of assemblies using Html Help Builder. Html Help Builder can import assemblies and quickly create full documentation from the assemblies to HTML, CHM and Word. It supports .NET 2.0 and can deal with generic types although there was a small bug with one of the template sets that caused the generic parameters not to render properly.
While working on this Markus sent me his files to check out the problem and it turns out that I couldn’t even get the assemblies to load locally. Markus (or rather one of his employees Mike) was complaining about the import failing and sure enough once I had the files here locally I saw the same failures.
It turns out the failure is due to some sort of referencing problem in the assembly for one of the dependent assemblies and Html Help Builder is choking while trying to get the types loaded. I use code like this to get the initial list of types in the specified assembly:
public int GetAllObjects(bool DontParseMethods)
{
this.lError = false;
Assembly assembly;
try
{
// assembly = Assembly.LoadFrom(this.cFilename);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve +=
new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(this.cFilename);
}
catch (Exception ex)
{
this.lError = true;
this.cErrorMsg = "Unable to load assembly.\r\n\r\nRaw Error:\r\n" + ex.Message;
return 0;
}
try
{
this.aXObjects = assembly.GetTypes();
}
catch(Exception ex)
{
this.lError = true;
this.cErrorMsg = "Unable to load types from assembly.Typically this is caused by missing dependent assemblies or assembly version mismatches.\r\n\r\nRaw Error:\r\n" + ex.Message;
return 0;
}
… go on parsing
}
(excuse the bad naming conventions – this code is ancient and was one of my first .NET endeavours <s>)
The code is failing on the call to GetTypes() which returns an collection of types that are contained in the assembly. And the aXObjects collection ends up null with an exception thrown into the exception block.
Both Markus and Mike insisted that the assemblies are fine, but I kept getting loader exceptions on one of the related assemblies. Finally I created a WinForms project and loaded the assemblies in a page to see if that work and sure enough that code bombed *at runtime* trying to load the same assembly with the exact same error message. Ok, so the failure makes sense. Garbage in, garbage out...
Reflector and the VS.NET Assembly browser can though
However I noticed that if I’m in VS.NET and click on the assembly I can get the assembly browser to show me the assembly and all of its types and details. Same with Reflector which can also browse and display the assembly types, and member signatures and even the decompiled code.
So what are they doing that I’m not? <g> How can you get a list of types in the assemblies without using GetTypes()? Are these tools going straight to the IL or the manifest to get the data out bypassing Reflection altogether?
ReflectionOnly Assembly parsing – hopeful but No Cigar
In the meantime, while I was looking this over, I noticed that .NET 2.0 has a new feature IsReflectionOnly (here’s some more detailed info on this), which is supposed to be more lightweight and doesn’t actually require loading the types.
Originally I simply did:
assembly = Assembly.LoadFrom(this.cFilename);
but ended up changing this to ReflectionOnly loading in hopes that this might resolve the issue with the type loading:
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve +=
new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(this.cFilename);
You then need to implement a fairly generic resolver method like so:
Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
return System.Reflection.Assembly.ReflectionOnlyLoad(args.Name);
}
This delegate is fired whenever a dependent type is loaded and it's meant to give your chance to do any fix up to ensure that the assembly can be loaded properly. The code above is pretty generic and it works fine, but it looks like implementing this handler is required - without it any loading of dependent assemblies will fail. I suspect the .NET team wanted to make it explicit that external types need to be loaded since ReflectionOnly gives the impression of being a low impact operation.
Testing with a number of my fairly complex framework assemblies I didn’t see any functionality problems but I’ll have to do some more testing. I suspect performance should be improved but it’s hard to tell – I don’t have assemblies that are large enough to make enough of a difference.
So anybody know if there’s a different way to parse types through the framework that I might be missing?
Other Posts you might also like