«前の日記(2006-01-11 [水]) 最新 次の日記(2006-02-01 [水])» 編集
RSS: href="http://endoh-namazu.tierra.ne.jp/diary/index.rdf"


半期 四半期 全カテゴリ

新・なまず日記


2006-01-31 [火]

_ [AHK]窓使いの憂鬱のプラグインをAutoHotkeyで利用する ー 基本編

TAGA nayutaさんの窓使いの憂鬱(以下、mayu)は、キーバインド変更ソフトです。アクティブなウインドウの種類に応じたキー設定の変更や、キーバインド以外も様々な機能が実現できるなど、AutoHotkey(以下、AHK)と非常に似通った目的と機能を持つソフトです。なまずもかつて、mayuを使おうか、それともAHKを使おうかと、悩んだことがあります。キー定義のスクリプトの書き方が、よりプラグラミング言語っぽく見えた、ということで、なまずはAHKの方を選んだのですが。

さて、mayuでは、標準で用意されたもの以外の機能を実現するために、プラグインと呼ばれる外部プログラムをコールできる、という仕組みを持っています。すでに多数の優秀なプラグインが開発されているのですが、その中でもなまずが特に興味を引かれたのが、tamanegi(?)さんのmigemo-isearchです。これは、IEやSleipnirなどのIEのエンジンを用いたwebブラウザ上で、インクリメントサーチ を実現するものです。しかも、migemoを使って、日本語文字列までも、ローマ字でインクリメントサーチできるのです。emacs(isearchのルーツ)ファンであり、Sleipnirファンであり、しかもキーボード派のなまずとしては、まさにツボというか、急所というか、性感帯というか、あぁン、というツールなのです。

なにがなんでも使ってみたい、と思ったものの、いまさらmayuに乗り換えるのも大変だし、mayuとAHKの共存は難しそうだし、どうしたものかと調べてみましたら、なんと、mayuのプラグインはきわめて単純なDll関数のコールであり、AHKのDllCall()を使ってサクっと呼び出せることがわかりました。

そこで、mayuのプラグインをAHKから呼び出すための、ちょっとしたライブラリを書きました。これを使ってmigemo-isearchを呼び出すことで、AHKの環境下に於いても、ブラウザのインクリメンタルサーチが可能となります。いや、migemo-iseach、本当にいいですよ。まさにあぁンです。

と、いうことで、今回、基本編として、mayuのプラグイン呼び出しの機能説明と、AHKからmayuプラグインを呼び出すためのライブラリの紹介をいたします。さらに次回、実践編として、migemo-isearchをAHKから呼び出す具体的事例を紹介します。



では、mayuのプラグイン呼び出し、&PlugInの仕様を説明します。もちろん、基本的には、原典のマニュアルにあたっていただいた方がいいのですが、ここでは、AHKから呼び出すために必要になりそうなことを中心に、取り上げたいと思います。


  • &PlugIn( DLLNAME, FUNCNAME, FUNCPARAM, runAsThread )

    • DLLNAME
      Dllのファイル名を指定します。mayuでは、mayu.exeの下のpluginフォルダの中に、Dllが存在することが必要になるのですが、AHKでは、ここは別にmayuに合わせることはないでしょう。むしろ、AHKのDllCall()のファイル検索の仕様に合わせた方が使い勝手が良さそうです。


    • FUNCNAME
      Dll内の関数を指定します。mayuは、ここで与えられた文字列を使って、以下の順番で、Dll内の関数を探し、最初に見つかった関数をコールします。
      1. void WINAPI
        mayuFUNCNAMEW(const wchar_t *FUNCPARAM);
      2. void WINAPI
        mayuFUNCNAMEA(const char *FUNCPARAM);

      3. void WINAPI
        mayuFUNCNAME(const char *FUNCPARAM);
      4. void WINAPI
        FUNCNAME(const char *FUNCPARAM);
      これは、例えば、FUNCNAMEAbc として、Dll内に、mayuAbcWという関数と、mayuAbcAという関数があった場合、最初に見つかったmayuAbcWが必ずコールされる、ということを意味します。ですから、mayuと同じ仕様でAHKで動作させようと思ったら、この関数検索の順番は非常に重要になります。


    • FUNCPARAM
      Dll関数に渡す引数(文字列)を指定します。基本的には、文字列は、ASCII(あるいはShift-JIS?)のようですが、上記の例で言えば、mayuAbcW()のように、最後が"W"の関数をコールする場合、文字列は、ワイドキャラ、つまりUNICODEであることが必要のようです。


    • RunAsThread
      この引数がtrueだと、mayuは、今スクリプトを処理しているスレッドとは別のスレッドを生成し、新しいスレッドのなかで、Dll関数をコールします。
      ここは大事なところなので、後述します。

RunAsThreadの目的は、コールしたDll関数から戻らなくても、次のキー入力を受け付けて、スクリプトを動作させたい、ということにあるようです。この機能のおかげで、mayuのプラグイン関数は、スクリプトの動作のために、早く関数から抜け出さなくちゃ、ということを考える必要がなくなります。例えば、migemo-isearchの内部関数"mayuie_isearch_fmRi()"は、コールすると、自分でウインドウを生成して、その中で、キー入力待ちを始めます。そして、そのウインドウが消去されない限り、関数から抜け出ることはありません。

この機能の必要性をもう少し説明するために、具体的な事例をあげたいと思います。emacsでポピュラーなインクリメントサーチのやり方は、Cntrl-sでインクリメンタルサーチを開始して、その状態でさらにCntrl-sを押すと、次の検索候補にジャンプする、というものです。

上記の機能を、migemo-isearchでは、IEがアクティブの時、"mayuie_isearch_fmRi()"をコールすることで、インクリメントサーチの開始、その時生成されたウインドウ(クラス名"migemo-iseach")に0x8001のメッセージを送ることで、次の検索候補にジャンプ、という構成で実現しています。

上記の機能を、AHKで実装してみましょう。以下のような感じです。

$^s::
  SetTitleMatchMode,2

if(WinActive("Microsoft Internet Explorer")) DllCall("migemo-isearch\mayuie_isearch_fmRi","Str","") else if(WinExist("ahk_class migemo-isearch")) PostMessage, 0x8001, 0, 0, , ahk_class migemo-isearch else Send, ^s return

残念ながら、このコードはうまく動きません。なぜかというと、DllCall()で"mayuie_isearch_fmRi"をコールすると、独自にウインドウを生成し、キー入力待ちを始めてしまうので、関数の中に入ったっきり、出てこなくなってしまうのです。つまり、AHKにとっては、Cntrl-sで生成されたホットキースレッドが、いつまでたっても終わらない、という状態になります。

この状態で、もう一度Cntrl-sを押しても、AHKはそれを無視します。同じホットキースレッドが二重に起動することを、(デフォルトの)AHKは許さないからです。つまり、次の検索候補にジャンプするためにCntrl-sを押しても、それでスクリプトを動作させることは不可能、ということです。

ではどうしたらよいのでしょうか。AHKでは、汎用的なスレッドを生成させることはできませんが、タイマ割り込みで、別のタイマスレッドを生成させることはできます。つまり、以下のコードなら、うまく動く、ということです。

$^s::
  SetTitleMatchMode,2

if(WinActive("Microsoft Internet Explorer")) SetTimer, Timer, 0 else if(WinExist("ahk_class migemo-isearch")) PostMessage, 0x8001, 0, 0, , ahk_class migemo-isearch else Send, ^s return
Timer: DllCall("migemo-isearch\mayuie_isearch_fmRi","Str","") return

このコードでは、"mayuie_isearch_fmRi()"をコールする代わりに、タイマスレッド"Timer"を起動しています。この直後起動された"Timer"スレッドの中で、"mayuie_isearch_fmRi()"をコールします。前述したとおり、この関数は独自ウインドウが消去されない限り、中に入ったまま出てきませんから、"Timer"スレッドは、いつまでたっても終わりません。しかし、Cntrl-sのホットキースレッドの方は、タイマを起動するだけですから、あっと言う間に終了します。つまり、次の検索候補にジャンプするための次のCntrl-sを処理することができる、というわけです。

ちょっとややこしかったですね。ご安心ください。なにも考えずにmayuのプラグインを利用できるよう、ライブラリを書きました。以下のファイルをダウンドードして、解凍して出てきたmayu_plugin.ahkをインクルードしてお使いください。

mayu_plugin_v10000.zip

以下に、関数仕様を説明します。


  • result := MYP_PlugIn( dllName [, funcName, funcParam, runAsThread, force ] )

    引数:
    • dllName
      プラグインのDLL名。窓使いの憂鬱とは異なり、Dllは、AutoHotkeyのDllCallの時のサーチフォルダのどこか(システムフォルダなどPATHが通っているディレクトリか、A_WorkingDir)に置く。また、Dllのフルパスを指定しても良い。
    • funcName
      Dll内の関数名(の一部)。省略した場合は空文字列となる。窓使いの憂鬱と同様に、以下の順番で、Dllの関数をコールする。
      1. void WINAPI
        mayufuncNameW(const wchar_t *FUNCPARAM);
      2. void WINAPI
        mayufuncNameA(const char *FUNCPARAM);

      3. void WINAPI
        mayufuncName(const char *FUNCPARAM);
      4. void WINAPI
        funcName(const char *FUNCPARAM);
    • funcParam
      プラグインをコールするときの引数。省略した場合は空文字列。
      Dll内の関数が、
      mayufuncNameW()
      であった場合は、内部で、UNICODEに変換してから、プラグインをコールする。
    • runAsThread
      trueの場合、タイマスレッドを生成し、そのスレッドの中で、プラグインをコールする。プラグインから処理が帰ってこなくても、次のホットキー割り込みを発生させることができる。
      ただし、窓使いの憂鬱とは異なり、同時にひとつのスレッドしか生成できない。窓使いの憂鬱プラグインをrunAsThread = trueで実施中、もうひとつ、窓使いの憂鬱プラグインをrunAsThread = trueで実施しようとしても、無視される。
      省略時はfalse。
    • force
      窓使いの憂鬱は、funcName に、接頭語(mayu)や接尾語(W)などをつけて、上記の4通りのパターンで、Dll内の関数を検索する。当関数も同じ検索を行う。しかし、あらかじめ関数名がわかっている場合は、いちいち検索するのは効率が悪い。
      そこで、force をfalse(省略時もfalse)以外の値にすると、funcName で指定した関数がDll内にあるものとして、決めうちでコールを行う。
      force を"W"にした場合は、funcParam をUNICODEに変換してからプラグインをコールする。"W"とfalse以外の値であったら、funcParam をそのまま使ってコールする。

    戻り値:
    • result
      コール成功はtrue、失敗はfalse。ただし、runAsThread がtrueだった場合は、常にtrueとなる。
    動作:
    • 上記の通り


上記のライブラリを使って、以下のmayuプラグインの動作を確認しました。

今回は、mayuのプラグインをAHKで呼ぶために、mayuプラグインの呼び出し方と、ライブラリを紹介しました。
次回は、migemo-iseachをAHKで使う具体的な事例をご紹介します。また、プラグインのコールだとか、PostMessageだとかのややこしいことを意識しないでも使えるよう、簡単なライブラリ(そんな大げさなものではなくて、皮関数、いわゆる、ラッパ(wrapper)というんですか?)も紹介します。といっても、種明かしは今回で全部してしまったので、もうわかった、という方は、今からバンバン使ってみてください。素晴らしいですよmigemo-iseachは。あぁンですよ。では、次回までごきげんよう。




追伸(全然関係ないけれど)
実はこの間、お友達に誘われて、ミクシィにアカウントを作りました。なかなかおもしろいです。
「なまず日記」では、「書く以上はそれなりの質と量がないと」とか思って(こんなブログでも、なまずはイッパイイッパイなんですよ!)、あまり更新できませんが、ミクシィでは、馴れ合いというか、マッタリとした雰囲気で、テキトーなことを書いています。ミクシィにアカウントをお持ちでしたら、もしよかったら、探してみてください。

本日のTrackBacks(全1件) []
_ Click here:Click here (2008-04-13 [日] 14:08)

The government recommends we get so many vitamins and minerals in our daily diet to be healthy and fit. However, many of our food choices don’ t contain sufficient amounts of them, so we have to make do with what we have. You could take vitamin supplements,..