2003.03/20 更新

Susieプラグインの制作方法

[Susieプラグインとは?] / [プラグインの作成]
['00IN'API関数の解説] / ['00AM'API関数の解説]
[エラーコード] / [プラグインのデバッグ方法]
[Susieプラグインを利用するには] / [サンプルソース]
[参考リンク] / [更新履歴]


■Susieプラグインとは?
 「Susie」というのは画像ビューアの名前です。(フリーウェアで、Susieの部屋からダウンロードすることが出来ます。)
 Susie 本体が対応している画像形式は BMP くらいなのですが、Susieプラグインと呼ばれるファイルを追加することによって対応形式を増やすことができるようになっています。

 Susieプラグインの正体は DLL(ダイナミックリンクライブラリ) です(拡張子は".spi"に変更されていますがDLLです)。Susieプラグインの関数を呼び出して、JPEGなどのメジャーな画像形式から聞いた事も無いマイナーな画像形式であろうと(その形式に対応しているプラグインであれば)DIBに変換して表示する、という仕組みです。

 また、SusieプラグインにはJPEGプラグインのようなファイル入力用のプラグイン('00IN'プラグイン)だけではなく、LZHファイルやZIPファイルのようなアーカイブファイルを展開するプラグイン('00AM'プラグイン)もあります。


 Susieプラグインの仕様は公開されていて、新しいプラグインを作ることや、Susieプラグインを利用したアプリケーションを作ることが可能になっています。

 すでに多種多様のSusieプラグインが存在しているので、自作のアプリケーションをSusieプラグインに対応させることで対応形式に頭を悩ますことは無くなるでしょう。万一必要なプラグインが無い場合は自分で作成しましょう、ということでSusieプラグインの作成について解説していきます。


■プラグインの作成
 Susieプラグインの仕様書はSusieの作者たけちん氏作の本家プラグインライブラリの中にあります(SPI_API.TXT)。詳細はこちらを参照して下さい。

 Susieプラグインは拡張子がSPIであること以外は普通のDLLなので、作成手順は普通のDLLと変わりません。詳しくは使用している開発ツールのヘルプを調べてください。


・Visual C++6.0の場合
 まずプロジェクトを作成します。「新規作成」で「Win32 Dynamic-Link Library」を選択、作成するDLLの種類は「空のDLLプロジェクト」にします。
 あとはプロジェクトにソースファイルとDEFファイルを追加してビルドするだけです。
※プロジェクトの設定で、「C/C++」タブの「コード生成」カテゴリの使用するランタイムライブラリを「マルチスレッド(DLL)」に設定すると、DLLのサイズを小さくすることができます(ただし実行には「MSVCRT.DLL」が必要になります)。


・C++Builder4の場合
 まず「新規作成」でプロジェクトを作成します。VCLを使用する場合は「ダイナミックリンクライブラリ」を選択し、VCLを使用しない場合は「コンソールウィザード」を選択し実行形式を「DLL」にします。
 あとはプロジェクトにソースファイルを追加してメイクするだけです。


・C++Builder5の場合
 まずプロジェクトを作成します。「新規作成」で「DLLウィザード」を選択します。必要に応じて設定してかまいませんが、「マルチスレッド」には必ずチェックを入れます。あとはプロジェクトにソースファイルを追加してメイクするだけです。

・Borland C++ Compiler 5.5の場合
 「Borland C++ Compiler 5.5」でSusie Plug-inを作るをご覧下さい。

・Delphi 6の場合
 DelphiでSusie Plug-in


■'00IN'API関数の解説
00INサンプルソースは BMP のプラグインです。全く実用性はありませんが、理解し易いかと思います。
 また、サンプルを元に新しくプラグインを作成する時は 〜Ex 関数を書きかえるだけで済むように定型的な処理をまとめています。


GetPluginInfo(int infono, LPSTR buf, int buflen);
 プラグインに関する情報を得る関数です。
 プラグインは引数 infono に応じたプラグインの情報を buf に書き込み、書き込んだ文字数を返します。


 書き込む文字列に終端NULL を入れるのかどうかというのは仕様には明記されていないのですが、入れておいた方が問題が無いでしょう。
 たけちん氏の作成したプラグインの動作を調べて見たところ、必ず終端NULL が入ります(ただし終端NULL は書き込んだ文字数に入れない)。

 サンプルを元にする場合はプラグインの情報(pluginfo[])を書きかえるだけで済みます。


IsSupported(LPSTR filename, DWORD dw);
 dwの上位ワードが0ならばファイルハンドル(CreateFile 等が返す値)で、そうでなければファイル先頭から2Kバイト読み込んだバッファへのポインタになっています。これを調べて、対応している画像フォーマットであれば非0を返します。

 filename は拡張子を判断材料にしたり、複数のファイルで構成されている場合のためにあります。

 Susieの場合は、誤って非0(対応している)を返してしまうと他のプラグインにそのファイルを判断する機会がまわらず、対応しているプラグインがあったとしても結果は「未知の形式」のファイルとなってしまいます。できるだけ厳重にチェックしなければいけません。


 サンプルでは IsSupportedEx という関数を作成してファイル入力かメモリ入力かの違いを吸収しています。サンプルを元にする場合は IsSupportedEx を書き換えてください。


・Susie の場合はメモリで渡される(上位ワードが非0)。
・他のアプリケーションではファイルハンドル(上位ワードが 0)の場合もある。


GetPictureInfo(LPSTR buf, long len, unsigned int flag, struct PictureInfo *lpInfo);
 画像ファイルに関する情報を得る関数です。

 flag を見てファイル入力かメモリ入力なのかを区別し、指定されたデータに対する画像情報を返却します。基本的に Susie の場合は書庫内のファイルはメモリ入力で、その他のファイルはファイルで渡されます。

 正常終了なら 0、それ以外はエラーコードを返します。


 Susieの場合、IsSupportedで非0(対応している)を返すと次に GetPictureInfo、GetPicture の順に関数が呼び出されます。しかし他のアプリケーションではこの順番で関数が呼ばれるとは限らないため(IsSupported を一切使わず、いきなり GetPicture 等を呼び出すアプリケーションが存在する)、プラグインの対応している形式が渡されるとは限りません。これは GetPicture、GetPreview においても同様です。


 サンプルでは GetPictureInfoEx という関数を作成してファイル入力かメモリ入力かの違いを吸収し、IsSupportedEx で対応形式をチェックしています。サンプルを元にする場合は GetPictureInfoEx を書き換えてください。

・PictureInfo 構造体の hInfo は、仕様には「Globalメモリーのハンドルが格納される」と書かれていますが、HLOCAL hInfoで間違いないようです。
 たけちん氏の作成した内蔵ドキュメントを使用するプラグイン(ifjpeg.spi ver0.24)は Global* API を使用していません。


GetPicture(LPSTR buf, long len,unsigned int flag,
    HANDLE *pHBInfo, HANDLE *pHBm,
    FARPROC lpPrgressCallback, long lData);

 画像をDIBに変換する関数です。

 プラグインはLocalAllocによって必要なメモリーを確保し、そのハンドルを返します。

 BITMAPINFO構造体を*pHBInfoに、ビットマップデータを*pHBmに入れます。


 サンプルではGetPictureExという関数を作成してファイル入力かメモリ入力かの違いを吸収し、IsSupportedExで対応形式をチェックしています。サンプルを元にする場合はGetPictureExを書き換えてください。

 Susieは16ビットや32ビットDIBに対応していない、というのが定説です(注1)。Susie以外のアプリケーションではこれらに対応しているものもありますが、16ビットや32ビットの場合は24ビットに変換した方がいいでしょう。
 RLEやトップダウンDIBの対応は確認していませんが、何も問題が無いようにBI_RGBでボトムアップのDIBにした方がいいでしょう。

注1:詳しくはSusieプラグイン対応アプリケーションの挙動を参照



GetPreview(LPSTR buf,long len,unsigned int flag,
    HANDLE *pHBInfo, HANDLE *pHBm,
    FARPROC lpPrgressCallback, long lData);
 プレビューやカタログ等で用いる縮小された画像を作成する関数で、基本的にGetPictureと同じです。
 JPEGのように、アルゴリズムの関係で縮小したサイズでは高速に展開出来る場合にこの関数をインプリメントします。


 サンプルでは単に「その機能はインプリメントしていない」エラーコードを返しています。


ConfigurationDlg(HWND parent, int fnc);
 プラグインの設定ダイアログを表示する関数です。

 正常終了なら0、それ以外はエラーコードを返します。
 ダイアログからプラグイン固有の設定を行うことができます。

 この関数は必要が無ければ実装しなくても問題ありません。
 Susieの場合は、関数を実装しただけもプラグインの設定ボタンが有効になります(「その機能はインプリメントしていない」エラーコードを返すだけであっても)。


 この関数はサンプルでは実装していません。


■'00AM'API関数の解説
古い00AMサンプルソースはLZH(lh0のみ)のプラグインです。
lh0はファイルを無圧縮で連結するアーカイブ形式で、
ファイル1のファイル情報ヘッダ、ファイル1、
ファイル2のファイル情報ヘッダ、ファイル2、
ファイル3のファイル情報ヘッダ、ファイル3、
・・・
となっています。

新しい00AMサンプルソースではアーカイブ情報をキャッシュするようにしてみました。
また、こちらも 〜Ex 関数を書きかえるだけで済むようにしました。


GetPluginInfo(int infono, LPSTR buf, int buflen);
IsSupported(LPSTR filename, DWORD dw);
ConfigurationDlg(HWND parent, int fnc);
 '00IN'の関数を参照して下さい。


GetArchiveInfo(LPSTR buf, long len, unsigned int flag, HLOCAL *lphInf);
 アーカイブ内の全てのファイルの情報を取得する関数です。

 timestamp等の情報がアーカイブに無い場合は0に設定します。


 サンプルではメモリ入力に対応していません。


GetFileInfo(LPSTR buf, long len, LPSTR filename, unsigned int flag, fileInfo *lpInfo);
 アーカイブ内の指定したファイルの情報を取得する関数です。

 GetArchiveInfoがアーカイブ内の全てのファイルの情報を返すのに対して、こちらは指定されたファイルの情報を返します。また、GetArchiveInfoよりもflagの追加情報が増えています。


 サンプルではflagの追加情報の扱いが仕様に完全には従っていませんので注意してください。「ファイル名の大文字小文字を区別する」かどうかのフラグを無視しています。


GetFile(LPSTR src, long len,
    LPSTR dest, unsigned int flag,
    FARPROC prgressCallback, long lData);
 アーカイブ内のファイルを取得する関数です。

 プラグインはLocalAllocによって必要なメモリーを確保し、そのハンドルを返します。

 ファイル入力の場合のsrcはアーカイブファイル名のことで、lenにはGetArchiveInfoやGetFileInfoで返したfileInfo構造体のpositionメンバの値が使われています。つまりGetFileでは、このアーカイブファイルのここの位置にあるファイルをくれ、という情報しか渡されないのです。このためpositionメンバの値はファイルを取り出すのに都合の良い位置(ファイル情報へのオフセットなど)を入れておく必要があります。


 サンプルではファイルへの出力をサポートしていません。


■エラーコード
Susieプラグインのエラーコードは以下の通りです。
エラーコード 意味
-1      その機能はインプリメントされていない
 0      正常終了
 1      コールバック関数が非0を返したので展開を中止した
 2      未知のフォーマット
 3      データが壊れている
 4      メモリーが確保出来ない
 5      メモリーエラー
 6      ファイルリードエラー
 7      窓が開けない※
 8      内部エラー
 9      書き込みエラー※
10      ファイル終端※
※7、9、10は仕様にはありませんが(仕様では7は予約になっています)、Susieで使用されているエラーコードです。


■プラグインのデバッグ
 関係のない他のプラグインは外して行いましょう。Susieは本体(Susie.exe)のフォルダにあるプラグインが使われますから、作成中のプラグインのフォルダにSusie.exeを置くのが良いでしょう。

 Visual C++6.0やC++Builder4ではDLLのホストアプリケーションを指定してデバッグできるので特に問題はないと思われます。
 (Visual C++の以前のバージョンは知りませんが、C++Builderはバージョン3からホストアプリケーションを指定することが出来ます)

 アプリケーションによってプラグインの扱いに細かい違いがあります(Susieプラグイン対応アプリケーションの挙動)。
 拙作のTEST SPIをホストにすると様々な条件で試すことができて便利です。


■Susieプラグインを利用するには
Susieプラグインの利用方法


■サンプルソース
自由に改変して使用して下さい。
'00IN'BMPプラグインのソース
OLD'00AM'LZHプラグインのソース
NEW'00AM'LZHプラグインのソース
※'00AM'プラグインサンプルの注意点(OLD, NEW どちらも)
アーカイブ内のファイル情報で compsize にLZHの書庫ヘッダにある圧縮後のサイズをそのまま使用してしまっています。
LZHの書庫ヘッダに書かれている圧縮後のサイズは圧縮したデータのサイズで、書庫ヘッダのサイズは含まれていません。
Susieプラグインとしてはこの場合は compsize に「LZHの書庫ヘッダ+圧縮データのサイズ」を使用するべきです。


■参考リンク
ここよりも次のページが参考になるでしょう。

Susieの部屋
Susieの作者のページです。
Susieプラグインの仕様書は「Susie 32bit plug-in library」に含まれています。

kana's Home Page
Susie Plug-in関連の工作室にPlug-inの作成についての注意事項があります。
ここだけでプラグイン作成の情報が十分に得られます。

でれつきのページ
てくてく講座にSusieプラグインの作り方があります。

wai-emu's Home Page
Susie Plug-inについてのプログラミングメモがあります。

とぅいんくる☆は〜つ
Susie Plug-in を利用するプログラムを作成するときに気を付けた方がいい点につい てがあります。


■更新履歴
2000.11/14
・更新記録を付けるようした。
・サンプルソース spi00in.c のバグ修正。
 GetPictureEx関数
 NULL なら LocalFree、ではなく NULL でなければ LocalFree の間違い...
・参考リンクを別ページから統合しました。

2000.11/15
・サンプルソース spi00in.c のバグ修正。
 GetPictureInfoEx関数
 GlobalLockしたあとGlobalUnlockしていない...

2000.12/05
・PictureInfo 構造体の hInfo はずーっと HGLOBAL だと思ってた...
・サンプルソース spi00in.c を修正
 Global* を Local* に修正。

2001.09/21
・サンプルソース追加。
 00INは差し替え。00AMはバグってそうなので前のを残しておく。
・Delphiでプラグインを作るページへのリンク。
・Susieプラグイン対応アプリケーションの挙動ページへのリンク。
・デバッグのホストにTEST SPIをすすめてみる。
・プラグインの利用は別ページに。
・デッドリンク削除。

2001.09/23
・00AMサンプルソース更新。
 キャッシュ関係の問題点を修正。

2001.09/26
・00IN, 00AMサンプルソース更新。

2003.03/20
・00AMサンプルソースの注意点。

戻る