Introduction
NetZ is a fantastic tool for merging your executable and dependent assemblies into a single, zipped, executable. Using NetZ makes distribution of an application very easy, and it provides a layer of protection from programs like Reflector without having to buy into an obfuscation approach. However, a NetZ application will not be able to resolve dynamically loaded assemblies, such as business rule plug-ins, extenders, or assemblies referenced in MyXaml-based applications.
The Assembly Resolver
I wrote this small static class to sneak into NetZ's assembly loading mechanism so that a dynamically loaded assembly can be acquired from the NetZ assembly collection. This library can be added to your application, and used during development when you don't need the resolver. When you are ready to build your NetZ executable, the resolver will automatically figure out that the application is being run from the NetZ loader and will take the appropriate action.
The first step is to detect NetZ, which uses a bit of Reflection to see if the NetzStarter
class is in the executable:
protected static void CheckForNetZ()
{
string appName = AppDomain.CurrentDomain.FriendlyName;
appName = StringHelpers.LeftOfRightmostOf(appName, '.');
Trace.WriteLine(appName);
Type t = Type.GetType("netz.NetzStarter, " + appName);
if (t != null)
{
Trace.WriteLine("Found NetZ. Hooking assembly resolver.");
netzAssemblyLoader = t.GetMethod("GetAssemblyByName",
BindingFlags.Static | BindingFlags.NonPublic);
AppDomain.CurrentDomain.AssemblyResolve +=
new ResolveEventHandler(OnAssemblyResolve);
}
}
I put in some trace messages so you can see what the application is doing in Debug mode.
The next step is to resolve the assembly:
private static Assembly OnAssemblyResolve(object sender,
ResolveEventArgs args)
{
Assembly assy = null;
string assyName = args.Name;
if (assyName.Split(',').Length == 1)
{
assyName = qualifiedNameMap[assyName.ToLower()];
}
Trace.WriteLine("(NetZResolver) Attempting to resolve: " + assyName);
assy = (Assembly)netzAssemblyLoader.Invoke(null, new object[] { assyName });
return assy;
}
Usage
Using the class is very simple. For example, here are the calls to resolve the MyXaml.WinForms
extension, which is dynamically loaded:
NetZResolver.Initialize();
NetZResolver.RegisterShortName("MyXaml.WinForms", "MyXaml.WinForms,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
Note that the fully qualified assembly name is required, including the version. This means that if you update an assembly version number, you will need to also update the fully qualified name. This is only necessary if the resolver is given a short name. If you have dynamic assemblies that are being loaded by only the short name, then you will have to register the short name and the fully qualified assembly name with the resolver, as the above example illustrates.
Conclusion
Using this class, you shouldn't have any problems resolving dynamically loaded assemblies. Please note that I will not answer any NetZ support questions--NetZ is well documented, and if you have any problems, please contact the author of NetZ. Feel free to change the namespaces for the two source files to more suit your needs, but please keep the copyright in the source code.
History
- 21 July, 2006: Initial post.