LoginSignup
11
9

More than 5 years have passed since last update.

[C#]埋め込みDLLを敷居なく使う

Last updated at Posted at 2016-02-13

Rubyやろうと思ってたのにRedMineよりTracの方が気に入ったからという理由でPythonに走ったりしてるけど、わたしは元気です。
長続きしてしまっているSE仕事の合間にC#でツール作ったりしていたのですが、諸事情によりDLL埋め込みで作る機会があったので、備忘を兼ねて。

DLLを呼び出すだけならいろいろある

ちょこっと調べてみたらいろいろあった。

  • 普通に参照設定してusingで読み込む
  • DllImportで読み込む
  • 埋め込んで実行中のリソースから引っこ抜いてアセンブリとしてロード
  • 動的に読み込んでプラグインとして使う
  • ビルド後にマージして1つにまとめる

今回必要だったのは一番面倒そうな「埋め込んで実行中のリソースから引っこ抜いてアセンブリとしてロード」。
なんでかと言えば、2つ理由がある。
1つ目は、モジュール自体を設置、設定するのがベテランさんばかりな現場でないので、DLLと分けてあると忘れたり、バージョン違いで動かないケースがあるので埋め込む必要があったということ。
2つ目は、そのツールを作ってもあんまり特殊なことをすると引継ぎが面倒だし、保守できなくなるということ。
そして追加の要件として「C#プログラマが多いわけではない職場なので、あんまりややこしいソースにすると保守できない」ということである。
現場に合わせた保守性大事!

ということで、比較的平易に読める状態を保持しつつ、DLLを埋め込んで敷居なしに使える状態にする。

今回使う材料

今回は.NET2.0環境でZIP圧縮をしたいから、という理由だったので、Microsoft謹製のDotNetZipを使う。
ライセンスのことはあまり詳しくないのでよくわからないが、ざっくり調べてみたところ、「配布するなら元の著作権とかを表記しとくこと」「ビルドしたらそれも同じライセンス」「あとは好きに使え」っぽい。
というわけで、配布しないから好きに使う方向。

基本的な使い方

まずDotNetZipの基本的な使い方から。
参照で読み込んで、普通にファイル追加して、普通に圧縮ファイルを保存するなら以下のような感じ。

ZipArchive.cs
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#プログラマじゃないとしても普通のプログラマは読めるので、このレベルで書きたい。

普通の埋め込み呼び出し

だが、埋め込みリソースからの読み込みになると以下のような。

ZipArchive2.cs
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#プログラマ(私のことだ)にはいちいちこんな書き方すると読むのも辛いので、ラップして使う時を楽にする。

最終形(ラップされた読み込み)

ということでラップ用のクラスを用意する。

Lib.ZipFile.cs
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行修正するだけで行けちゃう!かんたん!

ZipArchive.cs
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関係のメソッドを使うようになるならラッパーに追加する必要があるけど、普通に圧縮するだけならこれで全部使えてしまいます。
ラッパー自体もパターンなので、追加もそんなに大変じゃない!
これなら読むのもらくちん!


可読性の高いソースは他人のためにも、数ヶ月後の自分のためにも、すごく大事なものだよね!

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9