zipファイルからのアセンブリロード

開発しているとDLLが大量に出来て邪魔くさいことがあります。機能単位でディレクトリを分けてしまえば済む話ですが、zipあたりで圧縮して実行時にその中のアセンブリを取り出せれば、ちょっとだけ良さそうな気がします。ようはJavaのjarファイルです。ということでjarもどきをこさえてみました。

using System;
using System.IO;
using System.Collections;
using System.Reflection;
using ICSharpCode.SharpZipLib.Zip;

class App
{
  [STAThread]
  static void Main(string args) {
    if (args.Length != 2) {
      Console.WriteLine("usage app.exe exe zip");
      return;
    }
    AppDomain domain = AppDomain.CreateDomain("Main");

    // zipファイルをサーチパスに加える
    domain.SetData("ZipPath", args[1]);
    domain.AssemblyResolve += new ResolveEventHandler(OnAssemblyResolve);
    domain.ExecuteAssembly(args[0]);
  }

  public static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args) {
    Assembly asm = null;
    ZipFile zip = new ZipFile(AppDomain.CurrentDomain.GetData("ZipPath") as string);
    try {
      string files = args.Name.Split(new char {','});
      ZipEntry e = zip.GetEntry(files[0] + ".dll");
      byte dll = new byte [e.Size];
      zip.GetInputStream(e).Read(dll, 0, dll.Length);
      asm = Assembly.Load(dll);
    }
    finally {
      zip.Close();
    }
    return asm;
  }
}

で、こんな感じで実行。

$ app.exe main.exe dll.zip

main.exeが参照しているアセンブリはdll.zipの中にアーカイブされています。

アセンブリの参照解決出来ない場合にはAppDomainのAssemblyResolveイベントが呼ばれ、ここで自力で参照解決するチャンスがあります。AssemblyResolveイベントの戻り値はAssemblyとなっているので、何らかの方法でAssemblyオブジェクトが生成出来れば良いはずです。そこで例ではzipファイルから該当DLLのイメージをbyte[]に読み込んでAssemblyを生成しています。アセンブリ名を持ってくるのにSplitでぶった切って.DLLをくっつけているのはイマイチですが、使えそうな気がします。