miauのブログ

はてなダイアリー「miauの避難所」をはてなブログに移行しました

D言語でひとりWikiのプラグインを作ってみる

はてなダイアリーの下書きに ひとりWiki を使っているんですが、

[http://www2u.biglobe.ne.jp/~MAS/soft.html#htwiki:title=ひとりWiki]

のような はてな記法 に対応していないので、ひとり Wiki で書いた下書きをアップロードする前にリンクの置換が必要で、結構面倒でした。

それで以前「はてな記法でもプラグイン使えるようにしてもらえませんか?」的な要望を出していたんですが、2.0 でこの対応をしていただきまして。

[プラグイン名:引数]

という形式で対応していただいているので、上記の例であれば「http」というプラグインを作ってしまえば、期待通りの動作をさせることができますし、その他のはてな記法プラグインさえ書けば対応できる部分も多くなるわけで。

この機能をようやく試せましたので、そのお話・・・なんですが、要約すると「うまく作れなかったのでヘルプ!」です。

(2011-02-10 追記)問題は解決したので、タイトル変更しました(元タイトル:「D言語でひとりWikiプラグイン作ろうとする→失敗」)

ひとり Wikiプラグインについて

に使い方やサンプルが載っています。

サンプルは Delphi で書かれていますが、C/C++ 互換の DLL であればどんな言語で作ってもいいはずで。今回 D 言語で書こうと思ったのは、

  • C/C++ と互換のある DLL が作成できる
  • 無料で気軽に使える
  • それなりに高レベルな書き方ができる

という条件を満たす言語を言語が他に思いつかなかったためです。

D 言語の基本的な部分

私もそんなに使い慣れてないので、ツッコミあればお願いします。

入門

あたりにまとまっているので、この辺見ながらコーディングしてました。

マニュアル

2.0 のマニュアルがどこにあるかわからなくて焦っていたことがあるんですが、

から左上の「D 2.0 →」をクリックすれば OK みたいです。

環境構築

から D 2.0 compiler の .zip を落として、C:\dmd2 として解凍→C:\dmd2\windows\bin にパスを通す、という感じでコンパイラを導入しました。インストーラはサイズが大きい(.zip が 10 MB くらいなのに対して 100 MB くらいある)ですし、D 1.0 がインストールされて、そちらの dmd が PATH で先に来たりして面倒なので。

dmc は入れたほうがいいらしいんですが、導入の利点がよくわかってないので、もっと使いこなしてから考えようと思ってます。(IDE 等もまだ入れてません。)

(2011-02-10 追記)D 2.0 では dmc は基本的に不要らしいです。詳細は id:repeatedly のコメント参照で。

あと、今回は Windows API を使ったりするので、

を参考に、

cd /d C:\dmd2\src\phobos
svn export http://svn.dsource.org/projects/bindings/trunk/win32

とやって Bindings for the Windows API を取得しました。

Tips

これまでは D 言語使うのはプログラミングコンテスト系のときだけだったので、SciTE で編集する場合は SciTEUser.properties に

# D
command.go.$(file.patterns.d)=C:\dmd2\windows\bin\dmd.exe -run $(FileNameExt)

のように設定して F5 で実行できるようにしていました。(dmd -run じゃなくて rdmd でもよかったような・・・)

まあ今回みたいに依存ファイルやライブラリが増えてくると、この方法では実行できませんので、参考程度に。

DLL を作ってみる→うまく変換できない・・・

のサンプルを動かしてみるとあっさり動いたので、じゃあこの mydll.dll をベースにひとり Wikiプラグインを作ってみようとすると・・・なんだかエラーになってうまくいきません。

結構ハマったんですが、ポイントは「C/C++ 等から呼び出すときは、関数を extern (Windows) で宣言する必要がある」ことみたいで。以下のサイトを見ると一発で解決しました。感謝。

書き方さえわかれば、あとは関数を増やしていくだけなので、ひとりWikiプラグインテスターで .dll の読み込みが成功するところまではたどり着いて。

じゃあ実際に変換をかけてみると・・・テスターがエラーも吐かずに落ちちゃいました。OllyDbg でも追ってみたんですが、正常に動作しているプラグインとの違いはつかめなくて、ちょっとお手上げな感じでした。

現時点での完成品

とりあえずソースを gist に置いておきます。

作成した .dll もいちおう。

このあたり詳しい方はアドバイスいただけると助かります・・・。

(2011-02-10 追記)

ひとりWiki掲示板で

プラグインが上手く行かない件ですが、手元にD言語の処理系がないので
ただの予想ですけど、PluginInlineの引数の型はchar[]ではなくchar*に
なると思います。あるいは使えるならLPCSTRあたりでしょうか。

とアドバイスいただいたので、char を char* に変えて試したところ、ちゃんと動作するようになりました。

関数の中で引数の値は使ってないし、OllyDbg で追うと EAX としてちゃんと値は返せているので、引数が原因ではないと踏んでたんですが・・・D 言語だと char にした場合特殊な処理が入ってるんですかね・・・うーん。

なにはともあれ、これでちゃんとプラグインが作れそうです。完成したらまたエントリ書きます。

(2011-02-10 追記2)

引数を char* にしたものと char[] にしたもので .dll を比較してみたんですが、差があったのは 1 byte だけで、どこが違ったかというと PluginInline() からの戻り方でした。

char* を 7 つ並べたバージョンは


RETN 1C
になっているのに対して、char[] を 7 つ並べたバージョンは

RETN 38
になっており、

  • char* 版
    • 0x1c = 28 = 4 * 7 = (sizeof(char*)) * 7
  • char[] 版
    • 0x38 = 56 = 8 * 7 = (sizeof(size_t) + sizeof(void*)) * 7

という感じで、こちらも id:repeatedly の解説のとおりの数字ですね。

引数は生成される機械語に影響しないと思い込んでいたんですが、これは __cdecl の話のようで。今回は extern (Windows) を指定しているので、C でいう __stdcall の動作になるので、

にあるようにスタックの後始末は呼び出された側で行う必要がある(=引数が処理に影響を与える)みたいですね。

知識が半端だと無駄にハマるいい例でした。ちゃんと勉強しないとなぁ・・・。