SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

動的PInvokeによる統合アーカイバ仕様DLLを使用した書庫の展開と作成

統合アーカイバDLLのAPIをマネージコードから呼び出す


  • このエントリーをはてなブックマークに追加

本稿では、統合アーカイバプロジェクトAPI仕様に準拠したDLLを使って、アーカイブを展開/作成する方法について説明します。さらに、DLL名と関数名を実行時に指定してアンマネージDLL関数を呼び出す、動的PInvokeの方法についても説明します。

  • このエントリーをはてなブックマークに追加

完成画像
完成画像

はじめに

 この記事では、俗に言う「統合アーカイバプロジェクトAPI仕様に準拠したDLL」(以下「統合アーカイバDLL」)を使って書庫(アーカイブ)を展開(解凍)、作成(圧縮)する方法について説明します。さらに、DLL名と関数名を実行時に指定してアンマネージDLL関数を呼び出す、動的PInvokeの方法について説明します。最終的にはこれらを活用して、DLLの情報をXMLファイルに記述することにより対応DLLを増やすことができる、展開、圧縮専用アーカイバ「.NET Archiver」を作成します。

注意事項

 この記事は、以下の統合アーカイバDLLのみを考慮して書かれています。この記事で「すべての統合アーカイバDLL」とあれば、以下のDLLすべてという意味です。また、DLLのバージョンが明記されていない場合は、以下に示したバージョンのDLLであるものとします。

統合アーカイバDLL一覧
DLL名バージョン著作権者または製作者(敬称略)配布先
UNLHA32.DLL2.02eMiccohttp://www2.nsknet.or.jp/~micco/micindex.html
UNZIP32.DLL5.42shoda T.http://www.csdinc.co.jp/archiver/lib/unzip32.html
ZIP32J.DLL0.37吉岡恒夫http://openlab.ring.gr.jp/tsuneo/zip32j/
7-ZIP32.DLL4.31.00.03秋田稔http://akky.cjb.net/
CAB32.DLL0.98宮内邦昭http://www.lightship.co.jp/cab/cab32.html
TAR32.DLL2.28吉岡恒夫http://openlab.ring.gr.jp/tsuneo/tar32/
UNRAR32.dll0.10亀井哲弥http://www.csdinc.co.jp/archiver/
UNARJ32.DLL0.63aMiccohttp://www2.nsknet.or.jp/~micco/micindex.html
BGA32.dll0.37木村利靖http://www.csdinc.co.jp/archiver/
YZ1.DLL0.27K.INABAhttp://www.kmonos.net/
UNGCA32.DLL0.11b天野晃治http://www6.plala.or.jp/amasoft/
UNIMP32.dll0.17HyperBeathttp://www.csdinc.co.jp/archiver/
UNHKI32.dll0.02Silkyhttp://sirk.tn.st/
UNACEV2J.DLL0.05Niiyama(HEROPA)http://www31.ocn.ne.jp/~heropa/

 また、記事内で「Unbel32.dll」(バージョン:0.45、著作権者:島田啓史)に触れた部分もありますが、これは対象外とさせていただきます。私が試した限りでは、正常な動作が確認できなかったためです。

前提知識

 この記事はプログラミング初心者を対象としていないため、基本的な事柄については説明しません。不明な点はMSDNなどで調べてください。

 またこの記事では、統合アーカイバDLLの使い方についてもある程度の説明はしていますが、各DLLを使用する前には必ずDLLと同梱されているドキュメントをお読みいただき、ライセンスや使い方などを確認してください。

必要な環境

 サンプルはMicrosoft Visual Studio 2005で作成されています。実行には、.NET Framework 2.0が必要です。また、すべてのサンプルを実行するためには、「Unlha32.dll」がインストールされていることが必要です。

 なお、.NET Framework 1.1以前における方法に関しては、『遅延バインディングによりアンマネージDLL関数を呼び出す』を参考にしてください。

DllImportAttribute属性を使用したPInvoke

 「Unlha32.dll」などの統合アーカイバDLLはアンマネージDLLですので、マネージコードからDLL内のAPIを呼び出すには、工夫が必要です。それが、PInvoke(Platform Invoke、P/Invoke、プラットフォーム呼び出し)です。

 「Unlha32.dll」のヘッダファイル「UNLHA32.H」によれば、「Unlha32.dll」のAPIであるUnlhaの関数プロトタイプは、次のようなものです。

int WINAPI _export Unlha(const HWND _hwnd, LPCSTR _szCmdLine,
    LPSTR _szOutput, const DWORD _dwSize);

 このUnlha関数をマネージコードから呼び出すためには、例えば次のようなメソッドを宣言します。

C#の場合
[DllImport("unlha32.dll",
    EntryPoint = "Unlha",
    CharSet = CharSet.Ansi,
    ExactSpelling = true)]
private extern static int Unlha(
    IntPtr hwnd,
    [MarshalAs(UnmanagedType.LPStr)]string szCmdLine,
    [MarshalAs(UnmanagedType.LPStr)]StringBuilder szOutput,
    uint dwSize);
VB.NETの場合
<DllImport("unlha32.dll", _
    EntryPoint:="Unlha", _
    CharSet:=CharSet.Ansi, _
    ExactSpelling:=True)> _
Private Shared Function Unlha( _
    ByVal _hwnd As IntPtr, _
    <MarshalAs(UnmanagedType.LPStr)> ByVal _szCmdLine As String, _
    <MarshalAs(UnmanagedType.LPStr)> ByVal _szOutput As StringBuilder, _
    ByVal _dwSize As UInteger) As Integer
End Function
補足
 説明のために、必要の無いDllImportAttributeのフィールドや、パラメータへの属性もあえて指定していますが、仮にこれらすべてを削除したとしても、問題なく動作するでしょう。また、VB.NETではDeclareステートメントを使う方法もありますが、DllImportAttributeより指定できる設定が少ない点以外は、ほとんど同じです。両者の違いについて詳しくは、『Should I use Declare or DllImport in VB.NET? What's the difference?』をご覧ください。

 なぜこのようになるのかについては、MSDNの『アンマネージコードとの相互運用』以下(特に、『アンマネージDLL関数の処理』や『プラットフォーム呼び出しによるデータのマーシャリング』)で詳しく説明されています。この記事はDllImportAttributeの解説が目的ではありませんが、差し当たり必要な知識を簡単に説明しておきます。

 まずはじめの悩みどころは、アンマネージ関数のパラメータや戻り値のデータ型をマネージコードではどの型に置き換えたらよいのかという点でしょう。これについては、MSDNの『プラットフォーム呼び出しのデータ型』が参考になります。これをそのまま適用すれば、特に問題は無いでしょう。

 ところで、_szCmdLineパラメータがString型なのに対して、_szOutputStringBuilderとなっています。これは、_szCmdLineが文字列を渡すだけなのに対して、_szOutputは文字列がバッファにコピーされるためです。つまり、MSDNの『文字列のマーシャリング』の『Buffersのサンプル』と同じです。

 次に、DllImportAttributeのフィールドについて説明しましょう。これは、MSDNでは、『マネージコードでのプロトタイプの作成』で詳しく解説されています。

 まず、EntryPointフィールドで呼び出すDLL関数の名前(エントリポイント)を指定します。上記の例のように、エントリポイントの名前とメソッドの名前が同じ場合は、指定する必要はありません。通常はメソッドの名前をエントリポイントと別の名前にしたい場合に使用します。

 CharSetフィールドでは、文字列パラメータのマーシャリング方法と、関数の検索法をCharSet.AnsiCharSet.UnicodeCharSet.Autoのいずれかで指定します。上の例ではCharSet.Ansiを指定していますが、これはUnlha関数の文字列パラメータにAnsi文字列として渡すためです(これらの選択に関しては、『プラットフォーム呼び出しのデータ型』をご覧ください)。デフォルトでCharSetCharSet.Ansiです。

補足
 ちなみに、もしCharSetCharSet.Autoにしてしまったら、どうなるでしょうか? Windows 98系のプラットフォームではANSI形式となるため問題ありませんが、Windows NT系のプラットフォームではUnicode形式となってしまうため、正しく文字列をDLL関数に渡すことができなくなるでしょう(ただし、MarshalAsAttributeが正しく設定されている場合は、問題ないようです)。

 上の例では、ExactSpellingフィールドをtrueにしています。ExactSpellingフィールドがfalseの場合は、「Unlha」という名前の関数が見つからなかった時に「UnlhaA」という名前の関数を検索しますが、ここではその必要がないためです。なお、ExactSpellingフィールドのデフォルトはCharSetCharSet.AnsiならばVB.NETがtrue、C#およびC++がfalseです。詳しくは、MSDNの『DllImportAttribute.ExactSpellingフィールド』にあります。

 さて残るは文字列のパラメータに付いたMarshalAsAttribute属性です。これは、型をアンマネージコードにどのようにマーシャリングするかを指定するものです。上記の例では、文字列はANSI型にマーシャリングするため、UnmanagedType.LPStrが指定されています。詳しくは、MSDNの『文字列に対する既定のマーシャリング』の『プラットフォーム呼び出しで使用される文字列』などをご覧ください。

 確かではありませんが、MarshalAsAttributeを指定しなくても、DllImportAttributeのCharSetフィールドと矛盾がなければ、問題はないようです。

 ごく簡単な説明でしたが、これで圧縮、展開に必要なAPIを呼び出すための予備知識は整ったものとします。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
「Unlha32.dll」を使用した書庫の展開

修正履歴

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

どぼん!(ドボン!)

DOBON.NET内で.NET Frameworkの機能を紹介したWebサイト.NET Tipsやメールマガジン「.NETプログラミング研究」の発行人。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/444 2006/07/24 20:04

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング