Susie Plug-inの作り方のメモ

このページも作成が全然進んでいません。 実際のところどうなんだって気になる人がいるかもしれないですが、 Mint Plug-inでよければ一応ソースを置いています。使う方のサンプルも付けてみました。

長らく注意書を忘れていましたがこのページで扱うPlug-inは00IN Plug-in(画像展開用)です。 00AM Plug-in(アーカイブPlug-in)は作ったことがないので割愛します。


目次
  1. Susieとのインタフェースを理解する
  2. VC++の場合のヘッダファイル(他の言語はわかりません)
  3. 各SPIのサンプル
    1. GetPluginInfoのサンプルコード
    2. IsSupportedのサンプルコード
    3. GetPictureInfoのサンプルコード
    4. GetPictureのサンプルコード
    5. GetPreviewのサンプルコード
    6. もろもろの下請けルーチン
  1. Q&A ありがちな失敗について
    1. Plug-inがうまく認識されない
    2. 内蔵DOCが表示されない
    3. 書庫内ファイルがうまく読めない
    4. Macバイナリの付いたファイルが読めない
    5. Plug-in設定ダイアログで日本語しかでない
    6. VC++で作成したPlug-inがWin32s環境で動作しない
    7. GetPictureで不正なBITMAPINFOヘッダが返却される
    8. デバッグ中にINT 3Hで止まる
  2. Plug-in利用のサンプル

  1. Susieとのインタフェースを理解する
  2.  以下の記述は経験的に得られたもので必ずしも正確であるとは限らないし、 間違っていることも多いと思うし、将来にわたってそうとは限りません。 と、一応逃げを打っておきます。
     またSusie以外のSusie Plug-inを使うアプリケーションでは利用の仕方が違っている 可能性が大きいです。 例えばPlug-inと拡張子の関連付けをハードコーディングしているアプリケーションもあります。 私の壁紙チェンジャーではPlug-inのサポートしているファイルタイプを見ているので、 "JPEG"とか"JPE"となっているJPEGファイルは壁紙の対象にはなりません。

     Susieと各画像ファイル用のPlug-inとの間のインタフェース(SPI)を簡単に説明します。

    1. int GetPluginInfo(int infono, LPSTR buf, int buflen)

       サポートしているPlug-inの種類(Import/Export/Archive)や対応している 代表的なファイルタイプ(*.JPGとか*.GIFとか)などの情報を取得します。 使用する事になっているPlug-inに対して、 Susieは起動時にこの関数をコールします。 [ファイル]-[開く]ダイアログのファイルタイプとフィルター、 [設定]ダイアログの拡張子関連付けで利用します。

    2. int IsSupported(LPSTR filename, DWORD dw)

       Susieは指定されたファイルに対してIsSupportedを呼び出します。 GetPluginInfoのファイルタイプは(多分)利用されません。 つまりJpeg Plug-inに対して"Sample.pic"というファイルが渡される事もあり得るということです。
       どれか一つのPlug-inが成功を返却すると他のPlug-inは呼び出されません。 またその後GetPictureInfoが失敗しても再度他のPlug-inに対してIsSupportedを呼び出すことはありません。 ですからこの関数の返す値は重要です。

       表示対象となっている画像がファイルの場合には、 メモリーインタフェースで呼び出されます。 この関数が実際にファイルインタフェースで呼び出されることがあるかどうかは調査していません。  

    3. int GetPictureInfo(LPSTR buf,long len,unsigned int flag,struct PictureInfo *lpInfo)

       SusieではIsSupportedで成功を返却するとその直後に呼び出されますが、 Susie Plug-inを使用するアプリケーションの中でIsSupportedを呼び出さないで GetPictureInfoを呼び出すものがあります。 このためGetPictureInfoは必ずしもサポートしている画像形式がbufに指定されると 考えてはいけません。

       bufで指定された画像に内蔵ドキュメントがない時にはlpInfoのhInfoはNULLにします。

       Susie(Ver0.41)ではIsSupportedが成功を返すがGetPictureInfoが失敗を返すという状況があってもGetPictureを呼び出します。 おそらくGetPictureInfoの戻り値のチェックをしていないか、 またはGetPictureInfoが失敗を返した場合には単にコメントがないという風に扱っているのだと思われます。

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

       pHBInfoはBITMAPINFO構造体を格納した領域へのハンドルです。 またこの領域にはパレットも含みます。 pHBmはピクセルデータ(と言って分かります?)が格納された領域へのハンドルです。

       bufで指定された画像ファイルのアスペクト比が1:1でない時にはpHBInfoのBITMAPINFO 構造体中のbmiHeaderのbiXPelsPerMeterとbiYPelsPerMeterを使ってアスペクト比を 指定します。 これによりpHBmのデータを実際に編集せずに済みます。 アスペクト比の補正はSusieが表示の際に行ないます。

    5. int GetPreview( LPSTR buf,long len,unsigned int flag, HANDLE *pHBInfo,HANDLE *pHBm, FARPROC lpPrgressCallback,long lData)

       カタログ表示の時に呼び出されます。それ以外はGetPictureと同じ。

       この関数が存在しないか、サポートされていない場合には、 GetPictureが使用されます。

    6. int ConfigurationDlg(HWND parent,int fnc)
    7.  Plug-in自身によるaboutダイアログ表示、設定ダイアログの表示を行ないます。 Susie Ver0.40以降よりサポートされています。 この関数は最新のPlug-in仕様のrev4には含まれておらず、 ドキュメントは今のところSusieの掲示板に書かれたものだけです。

       parentは親ウィンドウのHWNDです。 fncは機能コードであり現在定義されているコードは次のものです。

       Plug-inのaboutダイアログを実装していなければ、SusieはGetPluginInfoで返却する Plug-in名を表示します。

    以下、続かないかもしれない。

  3. VC++の場合のヘッダファイル
  4. SUSIE配布ファイルの中にあるSPI_API.TXTからPlug-inのヘッダファイルを作成します。 これは機械的(手動で)に変換可能なのでがんばって書き換えます。

     注意点としては構造体のアライメントの問題があります。 PictureInfo構造体のメンバはshortのメンバがあり、 VC++などのデフォルトのアライメントが8バイトのコンパイラで使用するとhInfoの オフセットがSusieが期待しているものとずれてしまいます。 これは内蔵DOCが表示されないという現象となってあらわれるので 分かりやすいです。

    -- 問題の構造体 --
    typedef struct SpiPictureInfo {
        long left,top;			/* 画像を展開する位置 */
        long width;			/* 画像の幅(pixel) */
        long height;			/* 画像の高さ(pixel) */
        WORD x_density;			/* 画素の水平方向密度 */
        WORD y_density;			/* 画素の垂直方向密度 */
        short colorDepth;		/* 画素当たりのbit数 */
        HLOCAL hInfo;			/* 画像内のテキスト情報 */
    } SpiPictureInfo;
    

     コンパイルオプションで対策を打つのは美しくないのでヘッダファイルで処理します。 といいつつ、この対策はコンパイラに依存するので美しくないです。 実際にはヘッダファイルを参考していただくと分かるでしょう。





  1. Q&A ありがちな失敗について
  2.  Plug-in制作中で私がはまった点について書いておきます。 他にも解決方法があるかもしれませんが、私の場合の解決法を書いておきます。 ちなみに以下の項目はおぼろげな記憶とバグ修正履歴から再構成してお送りしてます。


    1. Plug-inがうまく認識されない(という現象だったかな?)

       Plug-inの各APIはWin3.1で言うところのPASCAL呼び出し、 Win32で言うところの__stdcallです。 Win32でもWindows.hでは

        #define PASCAL __stdcall

      となっているので、そのままでPASCALって書いてあげればOKです。 これが違っていると関数コールから戻った際にスタックがおかしくなるので 訳の分からない事になります。 また何故か関数名のエクスポートが__declspec(dllexport)ではうまくいかないことがあります。 おそらく序数の付く形式ではうまくいかないアプリケーションが存在するのでしょう。 そういうアプリケーションのためにWin3.1の頃と同じようにDEFファイルも書いてあげます。 私は面倒なので最低限しか書いてません(書かなくても良い様な気がするのですが、書いてあげるとうまくいきます)。

        Mint Plug-inでの例
        LIBRARY IFMINT.SPI
        DESCRIPTION 'Susie Plugin. support mint title[HoshihanaKyoko-III] format'

        EXPORTS
          GetPluginInfo
          IsSupported
          GetPictureInfo
          GetPicture
          GetPreview

      という感じです。Susie ver0.40以降で利用できる設定ダイアログを実装する場合には
          ConfigurationDlg

      も追加してあげます。

    2. 内蔵DOCが表示されない

       これはヘッダファイルのところでも書いたことです。 api_spi.txtからヘッダファイルを作った時にPictureInfo構造体メンバのオフセットがアライメントの関係で本来の位置とずれてしまったからです。

      対策はPictureInfo構造体の前後を次のようにします。


        VC++の場合
        #include <pshpack1.h>
        struct PictureInfo ......
        #include <poppack.h>

        BC++の場合(未確認)
        #pragma option -a-
        struct PictureInfo ......
        #pragma option -a.


    3. 書庫内ファイルがうまく読めない

       メモリインタフェースとファイルインタフェースのどちらも実装しましたか?。

    4. Macバイナリの付いたファイルが読めない

       GetPictureInfo/GetPicture/GetPreviewの各関数で ファイルインタフェースであった場合にファイルオフセットを きちんと扱っていますか?。

      こんなのはまるの私だけ・・・だよね。

    5. Plug-in設定ダイアログで日本語しかでない

       Susieではメニューなどの言語設定ができます(設定ダイアログの[ウィンドウ]の画面)。 この設定の結果は以下のレジストリに反映されます。


        HKEY_CURRENT_USER:Software/takechin/Susie/Resource
          Japanese : REG_DWORD : 英語(0) / 日本語(1)
            この値が存在しない時は1(日本語)とする

      ダイアログは日本語と英語をそれぞれ用意しておいて上記レジストリの値を読んで切り替えてあげます。 J6I Plug-in Ver 0.03ではこのことに気がついてなくて英語のリソースが存在しません。

    6. VC++で作成したPlug-inがWin32s環境で動作しない

       もしかしてSusie起動時に以下のようなメッセージが表示されませんか?。


        MSVCRT40.DLL for Win32
        Error: This version of MSVCRT40.DLL is not compatible
        with Win32s

       これはMSVCRT40.DLLがWin32sとWin95/WinNTで違うものだからです。 Win32s用のMSVCRT40.DLLをVC++4.xのCDROMからコピーしなおすか、 VC++ Pro以上であれば使用するライブラリをスタティックリンクする事で 回避できます。 しかしWin32s用のMSVCRT40.DLLはWin32s 1.30cの配布パッケージには含まれていないのでスタティックリンクする方がよいでしょう。

       その他の原因としてPlug-inがWin32sでサポートされていないWin32 APIを使っていませんか?。 例えばTLS(Thread Local Storage)MutexなどはWin32sではサポートされていません。

       またVC++4.2以上では正式にはWin32sはサポートされない事になっています。 しかしライブラリをDLLを使った時には上記のようなメッセージボックスを表示しますが、 スタティックリンクした場合にはWin32sかどうかを意識していません。 よってVC++4.2以上でもスタティックリンクする事で動作の可能性があります。 ちなみにMint Plug-in程度であればVC++4.2でもWin32sで動作していました。

    7. GetPictureで不正なBITMAPINFOヘッダが返却される

       Plug-in制作よりは利用の時にはまるかもしれません。
       spi_api.txtのGetPictureの説明のところと関数ヘッダの定義のところで、 pHBInfoとpHBmの並び順が違うのでついプログラムを書く時に間違ってしまいます。 私が使っているヘッダではこの点は普通の順に並び変えています。

      壁紙チェンジャーを作った時にはまって3時間ぐらい悩んだのは内緒です(^^;)。

    8. デバッグ中にINT 3Hで止まる

       デバッグ中に見知らぬコードの中でINT 3Hで止まってしまう事があります。 具体的には各Plug-in->LocalFree->RtlFreeHeap->.....->DbgBreakPoint のような呼び出しです。

      私が少し追いかけたところでは、LocalFreeの中で呼ばれるルーチンのチェックコードに引っ掛かっているのは確認できました。 LocalFreeはSetLastError(ERROR_INVALID_HANDLE)を呼んでエラーコードを設定していることと、この現象が発生しないPlug-inがあることから、 該当のPlug-inかPlug-inが使っているライブラリの問題のような気がします。 lhasad.spi/ifgif.spi/ifpi.spi/ifpic.spiなどで発生します。

      私はどうしようもないので(VC++で)"SPACE"と"F5"を連打してきりぬけています。

  3. Plug-in利用のサンプル
  4. Plug-inを使うサンプルコードを公開します。 詳しい使い方をここに書いた方が良いのですが、 取り合えず同梱のドキュメントを参考にしてください。 メールを出すまでもないような質問であれば 掲示板 の方にでも書いてもらってもよいです。

    一応断っておくと奇麗なソースじゃありません。 それとOCXとかいう高級なものでもありません。 単なるwrapperです。

    以下の点を修正しています(1997/10/31)

    1. GetPictureInfoで取得されたコメント領域のメモリを解放していない
    2. GetPictureで取得されたBITMAPINFOへのメモリを解放していない
    3. GetPictureで取得されたビットマップデータ(phBm)へのメモリを解放していない
    4. 00IN Plug-inの時にGetPictureInfo/GetPicture/GetPreview を用意していないPlug-inをロードすると SpiFile::LoadFile()がエラーを返却する。
    5. SPIANY.EXEにいろいろオプション追加(したのでSPIANY2.EXEは削除)
    6. -mオプション指定時にバグってました。