Rubyやろうと思ってたのにRedMineよりTracの方が気に入ったからという理由でPythonに走ったりしてるけど、わたしは元気です。
長続きしてしまっているSE仕事の合間にC#でツール作ったりしていたのですが、諸事情によりDLL埋め込みで作る機会があったので、備忘を兼ねて。
DLLを呼び出すだけならいろいろある
ちょこっと調べてみたらいろいろあった。
- 普通に参照設定してusingで読み込む
- DllImportで読み込む
- 埋め込んで実行中のリソースから引っこ抜いてアセンブリとしてロード
- 動的に読み込んでプラグインとして使う
- ビルド後にマージして1つにまとめる
今回必要だったのは一番面倒そうな「埋め込んで実行中のリソースから引っこ抜いてアセンブリとしてロード」。
なんでかと言えば、2つ理由がある。
1つ目は、モジュール自体を設置、設定するのがベテランさんばかりな現場でないので、DLLと分けてあると忘れたり、バージョン違いで動かないケースがあるので埋め込む必要があったということ。
2つ目は、そのツールを作ってもあんまり特殊なことをすると引継ぎが面倒だし、保守できなくなるということ。
そして追加の要件として「C#プログラマが多いわけではない職場なので、あんまりややこしいソースにすると保守できない」ということである。
現場に合わせた保守性大事!
ということで、比較的平易に読める状態を保持しつつ、DLLを埋め込んで敷居なしに使える状態にする。
今回使う材料
今回は.NET2.0環境でZIP圧縮をしたいから、という理由だったので、Microsoft謹製のDotNetZipを使う。
ライセンスのことはあまり詳しくないのでよくわからないが、ざっくり調べてみたところ、「配布するなら元の著作権とかを表記しとくこと」「ビルドしたらそれも同じライセンス」「あとは好きに使え」っぽい。
というわけで、配布しないから好きに使う方向。
基本的な使い方
まずDotNetZipの基本的な使い方から。
参照で読み込んで、普通にファイル追加して、普通に圧縮ファイルを保存するなら以下のような感じ。
using Ionic.Zip;
namespace ZipWrapper
{
class ZipArchive
{
public void ArchiveFiles()
{
ZipFile zip = new ZipFile(Encoding.GetEncoding(932));
zip.AddFile(@"c:\test\text1.txt", "");
zip.AddFile(@"c:\test\text2.txt", "");
zip.AddFile(@"c:\test\img\img.png", "img");
zip.Save(@"c:\test.zip");
}
}
}
このレベルならC#プログラマじゃないとしても普通のプログラマは読めるので、このレベルで書きたい。
普通の埋め込み呼び出し
だが、埋め込みリソースからの読み込みになると以下のような。
using System.IO;
using System.Reflection;
namespace ZipWrapper
{
class ZipArchive
{
public void ArchiveFiles()
{
Assembly exasm = Assembly.GetExecutingAssembly();
Stream dllstrm = exasm.GetManifestResourceStream("ZipWrapper.Lib.Ionic.Zip.dll");
int dlllen = (int)dllstrm.Length;
BinaryReader dllbr = new BinaryReader(dllstrm);
Assembly zipasm = Assembly.Load(dllbr.ReadBytes(dlllen));
Type ZipFile = zipasm.GetType("Ionic.Zip.ZipFile");
// これがインスタンス生成
object _zip = Activator.CreateInstance(ZipFile, new object[] { Encoding.GetEncoding(932) });
MethodInfo AddFile = ZipFile.GetMethod("AddFile", new Type[] { typeof(string), typeof(string) });
// この辺がファイル追加してるところ
AddFile.Invoke(_zip, new object[] { @"c:\test\text1.txt", "" });
AddFile.Invoke(_zip, new object[] { @"c:\test\text2.txt", "" });
AddFile.Invoke(_zip, new object[] { @"c:\test\img\img.png", "img" });
MethodInfo Save = ZipFile.GetMethod("Save", new Type[] { typeof(string) });
// この辺で保存
Save.Invoke(_zip, new object[] { @"c:\test.zip" });
}
}
}
めんどくさいわー!
ちゃぶ台投げるわー!
日頃手抜きに慣れたC#プログラマ(私のことだ)にはいちいちこんな書き方すると読むのも辛いので、ラップして使う時を楽にする。
最終形(ラップされた読み込み)
ということでラップ用のクラスを用意する。
using System.IO;
using System.Reflection;
namespace ZipWrapper.Lib
{
class ZipFile
{
object _zip; //インスタンス用
Type _zipfile;
public ZipFile(Encoding encoding)
{
Assembly exasm = Assembly.GetExecutingAssembly();
Stream dllstrm = exasm.GetManifestResourceStream("ZipWrapper.Lib.Ionic.Zip.dll");
int dlllen = (int)dllstrm.Length;
BinaryReader dllbr = new BinaryReader(dllstrm);
Assembly zipasm = Assembly.Load(dllbr.ReadBytes(dlllen));
_zipfile = zipasm.GetType("Ionic.Zip.ZipFile");
_zip = Activator.CreateInstance(_zipfile, new object[] { encoding });
}
/// <summary>
/// アーカイブにファイルを追加
/// </summary>
/// <param name="filename"></param>
/// <param name="directoryPathInArchive"></param>
public void AddFile(string filename, string directoryPathInArchive)
{
MethodInfo addfile = this._zipfile.GetMethod("AddFile", new Type[] { typeof(string), typeof(string) });
addfile.Invoke(this._zip, new object[] { filename, directoryPathInArchive });
}
/// <summary>
/// アーカイブを保存
/// </summary>
/// <param name="filename"></param>
public void Save(string filename)
{
MethodInfo save = this._zipfile.GetMethod("Save", new Type[] { typeof(string) });
save.Invoke(this._zip, new object[] { filename });
}
}
}
これがあれば最初のソースに1行修正するだけで行けちゃう!かんたん!
using ZipWrapper.Lib;
namespace ZipWrapper
{
class ZipArchive
{
public void ArchiveFiles()
{
ZipFile zip = new ZipFile(Encoding.GetEncoding(932));
zip.AddFile(@"c:\test\text1.txt", "");
zip.AddFile(@"c:\test\text2.txt", "");
zip.AddFile(@"c:\test\img\img.png", "img");
zip.Save(@"c:\test.zip");
}
}
}
他のZIP関係のメソッドを使うようになるならラッパーに追加する必要があるけど、普通に圧縮するだけならこれで全部使えてしまいます。
ラッパー自体もパターンなので、追加もそんなに大変じゃない!
これなら読むのもらくちん!
可読性の高いソースは他人のためにも、数ヶ月後の自分のためにも、すごく大事なものだよね!