高見知英のかいはつにっし(β)

高見知英のアプリケーション開発日誌 のほか、地域活動などの活動報告ブログ。

メモリマップドファイル

id:Hiekichi:20050223 の件(今朝のは書いた日付が間違いでしたようですので、日付が修正されています)で、オンラインの問題をオフラインで解決するという、インターネット関係ではあまり好ましくないことをやってしまいました。
実はあちらの方、わたしの学校の先生ですので、あまり不確かなことを言いたくなかったんです。あの直後、学校に出かけなければなりませんでしたし。

 ええと、こちらで結果を言っていいのか分かりませんが…。まあ、あちらにかかれているとおりの状況が起こっていたのです。で、状況がなんだかフックプロシージャ内の処理で躓いてるように感じましたから、そうじゃないのかなと思っていたのです。
 実際学校に着いたら、ソースコードをプリントアウトして待っていてくれていました。思った通り、フックプロシージャ内でSetWindowPosなどをやっていたようですので、それを「メインEXEでやるように修正したら直ると思います」と助言した…というのが、あらすじです。


 他サイトで聞きかじったことそのままなので悪いですが、フック用のDLLは、すべてのEXEの中にロードされます。@nifty:@homepage:エラーに付属しているツール、プロセスビューア等でみると分かると思いますが、実際にすべてのプログラムに、そのDLLがロードされてます。ですので、フックプロシージャで行われることは、すべてのプログラムプロセスで行われると言うことになります。
フックプロシージャというのは、基本的にウィンドウプロシージャ並み(フックの種類によりますが)に頻繁に呼ばれますので、そこで時間のかかるような処理をやると、あっという間に固まってしまう…というわけです。
 そこで、基本的には、フックプロシージャは、メインEXEにPostMessageで、フックするべき処理が起こった旨を通知する(SendMessageだと処理が終わるまでプロセスが止められますので、同じことになってしまいます)だけにとどめておくのです。
 しかし、メインEXEとは、先の通りプロセスが違うことになりますから、ウィンドウハンドルを知りたくても保存した変数などはありません。そこで、メモリマップドファイルという仕組みが必要になるわけです。
メモリマップドファイルとは、Windowsに備わっている仕組みで、まあ、早い話がメモリ上に一意な名前の付いた、全プロセスの共有メモリを作ってしまうのです。そこで、そのなかにメインEXEのウィンドウハンドルなどを保存しておけば、万事うまくいく…というわけです。
とはいえ、PostMessageでメインEXEにデータを送ってしまっては、メインEXEがそのフックメッセージに応答してリアクションをとるくらいしかできません。だって、メインEXEがリアクションをとったときには、もうすでにフックしたデータは処理されているのですから…。まあ、リアクション以上のことといえば、Piro.ccマウ筋の、マウスジェスチャーのような、あまりリアルタイム性を問われないアクションくらいです。
あくまで短い処理であれば何でもいいんですから、WH_KEYBOARDでフックしたプロシージャで、キーコードを差し替えてしまうとかくらいならば、許されるのでしょう。やったことないので、あくまで憶測ですが。


 ――というのが真相です。わたしもそのつもりで早く行ったけど、トラブルでちょっと遅刻しかけてしまったし、あんまりちゃんと説明できなかったのが、今から思うと悔しいです。メインEXEにリアクションをとらせなければいけないことと、それにはメモリマップドファイルを使う必要があるとしか言わなかったなあ…。
先生、こちらを読んでくださるとうれしいのですが…。


 ああそうそう。先の通り、フックDLLはすべてのプロセスにロードされますので、"極力軽く"ということも忘れてはいけません。
変数を多数覚えておく必要があればメモリマップドファイルに納めておくべきだし(というかそうしなきゃ変数をとっておく意味がないかも知れませんが)、関数を使う必要があっても、できれば外部呼び出しですませましょう。先生が使っているのはC++とのことですし、たしかC++は簡単に共有メモリが作れる仕組みがあったような記憶があります。それで済めばそれでいいのかも知れません(とはいえたしか、BorlandのBCB Tipsページでは、古いWindowsの仕様だとか書かれていたので、結局メモリマップドファイル? …ってことになるんでしょうか)。

 メモリマップドファイル自体は、プロセス越えの通信手段ではかなり優秀だとの噂ですので、ほかでも使えます。たしかTerapadか何かで使われてた気がする…。変更通知には何を使ってるのかな、SendMessageとセットで使うのかな…。Terapadを使ってないので分かりませんが。