Monday, April 24, 2006

Embedded Assembly's

Wouldn’t it be nice if all those required DLL’s could just be embedded inside the exe and referenced dynamically?
Turns out, it’s a pretty easy task. Just takes a bit of reflection

The first step is to handle the ‘AssemblyResolve’ event, this way you can help the AppDomain resolve the correct assembly prior to looking for any external assemblies. This has to occur in a static constructor.

static Program()
{
  AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}


Now you can add the DLL’s required to the project (not as references, but as files), then set the ‘Build Action’ to ‘Embedded Resource’.

OK, so once this above event fires, we need to get an array of embedded resource names, loop through them, do a little reflection, then load the requested type =]

string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();

foreach (string resource in resources)
{
  string baseName = resource.Substring(0, resource.LastIndexOf('.'));
  ResourceManager resourceManager = new ResourceManager(baseName, Assembly.GetExecutingAssembly());


We then need to grab the enumerator of ResourceManager to help determine if we are loading an assembly as opposed to an image etc. We assume a byte[] is what we are looking for.

ResourceSet resourceSet = resourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
IDictionaryEnumerator enumerator = resourceSet.GetEnumerator();

while (enumerator.MoveNext())
{
  object obj = enumerator.Value;
  if (obj is byte[])
  {
    try
    {
      Assembly assembly = Assembly.Load((byte[])obj);


From here, we can do the final test to make sure we have the requested assembly.

if (args.Name == assembly.GetName().FullName) return assembly;

The beauty is, if we don’t resolve it manually and return a null, the AppDomain will continue probing and it may be resolved somewhere else, or throw an exception as a last resort.

Here’s the full source code

static Program()
{
  AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
  string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
  foreach (string resource in resources)
  {
    string baseName = resource.Substring(0, resource.LastIndexOf('.'));
    ResourceManager resourceManager = new ResourceManager(baseName, Assembly.GetExecutingAssembly());
    ResourceSet resourceSet = resourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
    IDictionaryEnumerator enumerator = resourceSet.GetEnumerator();
    while (enumerator.MoveNext())
    {
      object obj = enumerator.Value;
      if (obj is byte[])
      {
        try
        {
            Assembly assembly = Assembly.Load((byte[])obj);
            if (args.Name == assembly.GetName().FullName) return assembly;
        }
        catch { }
      }
    }
  }
  return null;
}

Comments on "Embedded Assembly's"

 

Blogger B said ... (6:18 AM) : 

The AssemblyResolve event is not getting called for me.
It's odd, because when I compiled yesterday, everything worked beautifully, but today, no such luck.

I throw an exeption in the first line of the handler, jsut to make sure it's really not being called, and I never see it.

Any ideas why the handler wouldn't be called?

Thanks!

 

Anonymous Anonymous said ... (8:11 AM) : 

Whats going on with this blog -- bring it back!

 

post a comment