ウィンドウメッセージを取得してみる ACT-1

Windowsプログラミングには欠かせないウィンドウメッセージ処理――これは現在のHSPの標準機能では実現できませんでした。しかし、Win32 APIの提供するさまざまな機能には、このメッセージを取得できないと意味をなさないものが多々あります。逆を言えば、メッセージを取得することによって機能を格段に拡張することができるようになる、ということです。そういった機能拡張のためにも、ウィンドウメッセージ取得拡張プラグインhsgetmsg.dllを用いてウィンドウメッセージを取得してみたいを思います。今回は、そのための前準備です。

ウィンドウメッセージ

ウィンドウメッセージ(以下、単にメッセージと呼ぶ)は、Windowsがアプリケーションの所有するウィンドウに対して送る通知のことです。Windowsは、ユーザーからマウス入力やキー入力を受け取ると、アプリケーションに対してメッセージという形で通知するのです。アプリケーションはメッセージを受け取ることで、いろいろなことを知ることができますし、そのメッセージに対していろいろ処理をすることができます。

ウィンドウがメッセージが受け取るのは、そのウィンドウに対してメッセージを発生させるものがあるからです。それらがメッセージを発生させることを「メッセージを送る(送信する)」あるいは「メッセージをポストする」といいます。(「送信」と「ポスト」の詳しい違いは後述。)

ウィンドウに送られるメッセージにはいくつかの種類があります。

どんなメッセージがどんな意味を持つかは、Windowsによって決められています。メッセージの種類は全部で1000種類近くあるといわれます。

メッセージの実体は、あらかじめ決められている整数値です。しかし、プログラミングにおいてこれらの値(メッセージコード)をそのまま取り扱ったのでは分かりづらいので、C/C++などのプログラミング言語では、“WM_”などで始まる定数名として定義されています。すなわち

#define WM_CREATE     0x0001

などというようにして、メッセージコードに対応した名前がつけられているのです。(C/C++における#defineはHSPのものと同じ働きをします。)

以下に、代表されるメッセージのほんの一部を紹介しましょう。

定数名 コード 意味
WM_CREATE 0x0001 ウィンドウが作成された
WM_DESTROY 0x0002 ウィンドウが破棄された
WM_PAINT 0x000F ウィンドウを再描画する必要がある
WM_SIZE 0x0005 ウィンドウサイズが変更された
WM_LBUTTONDOWN 0x0201 マウスの左ボタンが押された
WM_LBUTTONUP 0x0202 マウスの左ボタンが離された
WM_LBUTTONDBLCLK 0x0203 マウスの左ボタンがダブルクリックされた
WM_RBUTTONUP 0x0204 マウスの右ボタンが押された
WM_RBUTTONUP 0x0205 マウスの右ボタンが離された
WM_RBUTTONDBLCLK 0x0206 マウスの右ボタンがダブルクリックされた
WM_KEYDOWN 0x0100 キーが押された
WM_KEYUP 0x0101 キーが離された
WM_CHAR 0x0102 文字が入力された

ウィンドウプロシージャとサブクラス化

さて、ウィンドウにメッセージが送られたとき、Windowsは『ウィンドウプロシージャ』と呼ばれるコールバック関数(Windowsから呼び出される、アプリケーションによって定義された関数)を呼び出します。ウィンドウプロシージャとは、アプリケーション自身の持つメッセージ応対用のプログラムコードのことで、ここではメッセージに対してのさまざまな処理が行なわれます。

ウィンドウプロシージャでは、マウスクリックされた、キー入力された、ボタンが押されたなど、さまざまなメッセージに対してそれに対応する処理を行ないます。HSPの内部にもこのウィンドウプロシージャは存在していて、いろいろな処理をしているのです。例えば、タイトルバーをドラッグすることでウィンドウを移動したり、ウィンドウ境界をドラッグしてウィンドウサイズを変更したりできます。ウィンドウが前面に出れば自動的に再描画されますし、×ボタンを押せば終了(あるいはonexit命令で指定したラベルのスクリプトを実行)します。他にも、ウィンドウ上でマウスが動いたときにはシステム変数mousex, mouseyの値が変わりますし、コンボボックスやリストボックスの選択項目を変更すると割り当てられた変数の値が自動的に変更されます。これらのことは、すべてウィンドウプロシージャで行なわれているのです。(実際には、ウィンドウ移動やサイズ変更などのようにどのアプリケーションにも備わっている機能はWindowsが提供しているもので、ウィンドウプロシージャの中からさらに『デフォルトウィンドウプロシージャ』と呼ばれる、Windowsが提供する関数を呼び出すことで実現されています。)


さて、変な言い方になるかもしれませんが、HSPでは標準でメッセージを取得できないのは何故でしょうか?

言うまでもなく、標準機能にそんな機能がつけられていないからです。これは突き詰めて言えば、HSPのウィンドウプロシージャに、送られてきたメッセージを変数などに保存しておくためのプログラムコードが存在しないから、ということになります。

では、もしも、そのようなプログラムコードを今あるHSPのウィンドウプロシージャに入れてしまうことができたらどうでしょうか?

じつは、拡張プラグインを用いるとそれを実現することができてしまうのです。詳しく言うと、拡張プラグインのDLLの中に新しいウィンドウプロシージャのコードを含めておくのです。ここで実行されるのは、送られてきたメッセージを変数に保存しておくことと、HSPの実行ファイルに含まれるもとのウィンドウプロシージャを呼び出すことだけです。そして、メッセージが送られてきたときにDLL内のコードが実行されるようにウィンドウプロシージャを置き換えてしまいます。こうしてしまえば、実際にメッセージが送られてきたとき、新しいウィンドウプロシージャ内でそのメッセージを変数に保存し、もともとのウィンドウプロシージャが呼び出されて本来の処理が実行される、というわけです。このように、ウィンドウプロシージャを置き換えてしまうことを『ウィンドウのサブクラス化』といいます。

hsgetmsg.dllは、まさにウィンドウをサブクラス化することによってメッセージ取得を実現しています。ただ、上で述べたようにメッセージをそのまま変数に代入しているのではありません。まず、あらかじめどのメッセージを取得するのかを設定しておいて、そのメッセージが送られたときにだけ取得するようにしています。これは、すべてのメッセージを取得していたのでは、HSPの速度ではメッセージの取りこぼしが激しくなり、ほとんど目的のメッセージが取得できなくなる可能性があるためです。また、スクリプトの実行速度そのものも激減してしまいます。したがって、必要なメッセージのみ取得しています。

また、設定したメッセージが送られたらそれをそのまま変数に代入するのではなく、いったんDLL内部でメモリを確保してそこにストックしておき、スクリプト内で取得命令を実行するたびに送られた順に変数に代入する、という手段をとっています。何故なら、もしも直接変数に代入する方式をとると、連続して送られてくる2つのメッセージをともに設定してあった場合に、後のメッセージに上書きされて、先に送られてきたメッセージが取得できなくなってしまう、という現象が起こってしまうためです。


今回はウィンドウメッセージの仕組みと、メッセージを取得するhsgetmsg.dllの概要についてだけを述べました。次回はhsgetmsg.dllの各命令の説明と、そのスクリプトなどを紹介したいと思います。