メッセージ取得



メッセージとは、ウィンドウに「こういう操作がされた」と通知されるものです。(ウィンドウが作成された時とかキーが押された時とか) 一般的なwindowsアプリケーションは、ウィンドウへのメッセージを取得してそれに応じた処理をします。
しかし、HSPは特殊な型でメッセージによる処理をする言語ではありません。一般的なメッセージ型の場合はメッセージを受け取るまで停止しています。そしてメッセージを受け取ると処理する。 メッセージが送られてこなければ永遠に停止したままなのです。これはCPUにも負担をかけず、ツールなどには効率的なのですが、リアルタイムで動作するゲームなどには向いていません。
一方HSPの場合、リアルタイムに動作するようになっていて、ゲーム向きです。が、CPUへの負担が大きいです。何故こういうタイプなのかというと、メッセージ処理は行っているんですが、メッセージループがないのです。

メッセージループとは、文字どおりなんですが、メッセージを取得するループです。通常ではこれがメインループとなります。HSPにはこのループがないのです。
メッセージ処理できなければほぼウィンドウは使えなくなります。しかしHSPでもウィンドウは正常に動いています。ではどうやっているのでしょうか?
実はメッセージ取得を行っている命令があるのです。それはウェイトさせる命令stop、await、wait(他にもあるかも)です。ここでメッセージを取得しているのです。 ゲーム作るときも必ずstop、await、waitのいずれかは使いますよね。そこで取得しているのです。ループ内にウェイトをいれないとウィンドウが固まってしまいますよね。あれはメッセージが取得できないからなのです。
自動でメッセージ処理をしてくれるのは簡単でいいんですが、WinAPIを使う上で、このメッセージが必要なものがいくつか存在するので、loadlib.dllとllmodでメッセージを横取りしてみようと思います。

;メッセージを奪う

#include "llmod.as"

#define WM_MOUSEMOVE 0x0200	;マウスが動いたとき
#define WM_LBUTTONDOWN 0x0201	;マウスの左ボタンが押されたとき
#define WM_LBUTTONUP 0x0202	;マウスの左ボタンが離されたとき
#define WM_RBUTTONDOWN 0x0204	;マウスの右ボタンが押されたとき
#define WM_RBUTTONUP 0x0205	;マウスの右ボタンが離されたとき

#module

#deffunc getmsg val,int
	mref wndmsg,16
	mref status,64
	mref mode,1
	getptr pmsg,msg
	prm=pmsg,0,0,0,0
	if mode {
		dllproc "GetMessageA",prm,4,D_USER
		if (dllret@=0)|(dllret@=-1) : end
	} else {
		dllproc "PeekMessageA",prm,5,D_USER
		if dllret@=0 : status=0 : return
		dllproc "GetMessageA",prm,4,D_USER
		if (dllret@=0)|(dllret@=-1) : end
	}
	wndmsg=msg.1
	dllproc "TranslateMessage",pmsg,1,D_USER
	dllproc "DispatchMessageA",pmsg,1,D_USER
	status=1
return

#deffunc getprm val,val
	mref _wparam,16
	mref _lparam,17
	_wparam=msg.2
	_lparam=msg.3
return

#global

*l
	getmsg msg
	if stat {
		redraw 0
		color 255,255,255 : boxf
		getprm wprm,lprm
		color : pos 0,0
		if msg=WM_MOUSEMOVE {
			mes "マウスポインタが移動しました"
		}
		if msg=WM_LBUTTONDOWN {
			mes "左ボタンが押されました"
		}
		if msg=WM_LBUTTONUP {
			mes "左ボタンが離されました"
		}
		if msg=WM_RBUTTONDOWN {
			mes "右ボタンが押されました"
		}
		if msg=WM_RBUTTONUP {
			mes "右ボタンが離されました"
		}
		mx=lprm & 0xFFFF
		my=lprm >> 16
		mes "座標("+mx+","+my+")"
		redraw
	}
	await
	goto *l

まず、このスクリプトのモジュールで定義された命令を説明しますと

getmsg メッセージを格納する変数,モード
メッセージを取得
モードは省略、もしくは0で、メッセージが無かったらそのまま続行
1でメッセージが取得できるまで停止
モード0ならば取得できたら1、出来なければ0がstatに返ります

getprm wparamを格納する変数,lparamを格納する変数
メッセージの付属情報を取得
メッセージに関する情報を変数に格納します

WM_〜というのはメッセージの呼称です。ここではマウス関係の一部のみ使っています。
各API関数も適当に説明しますと、

GetMessageA MSG構造体ポインタ,ウィンドウハンドル,フィルタリング最小値,最大値
メッセージを取得します。メッセージを取得するまで停止します。

PeekMessageA MSG構造体ポインタ,ウィンドウハンドル,フィルタリング最小値,最大値,メッセージを消すか残すか
メッセージを取得します。GetMessageA関数とは違い、メッセージが無ければそのまま続行します。

TranslateMessage MSG構造体ポインタ
メッセージコードを文字メッセージに変換します

DispatchMessageA MSG構造体ポインタ
ウィンドウプロシージャにメッセージを送ります

MSG構造体
.0 メッセージの対象となるウィンドウのハンドルが格納されます
.1 メッセージコードが格納されます
.2 メッセージの付加情報が格納されます。意味はメッセージにより異なります。
.3 メッセージの付加情報が格納されます。意味はメッセージにより異なります。
.4 メッセージがポストされた時間が格納されます
.5 メッセージがポストされたときのカーソル位置が格納されます

ほんっと適当だなぁと思う。関数の流れは、GetMessageA→メッセージを取得→TlanslateMessahe→DispatchMessageA、もしくはPeekMessageAからスタート。
メッセージを取得できたらTlanslateMessage、DispatchMessageAを実行し、できなかったら帰るという形です。
mxとmyの計算式は、このマウス関係のメッセージの場合、lparamにはマウス座標が一つの変数に入っています。よってそれを分解する必要があるので、mxは下位16ビット、myは上位16ビットから取り出しています。
PeekMessageAを呼び出した後になぜGetMessageAが入っているかというと、PeekMessageAだけでやっている場合、ウィンドウを閉じてもプログラムは実行されたまま、という状態になってしまうので、GetMessageAで終了メッセージを受けとったら endで終了するという形にしないといけないのです。

さて、このプログラムは「横取り」です。つまり、awaitなどで取得されるはずのメッセージを先にとって処理します。だからウェイトより先に取得できるようにしておかないと、時々取得できない場合があります。 ちなみにこの場合、awaitが無くても正常に動作します。メッセージ処理は自分でやっているので。

このメッセージ取得は他のWinAPI(例えばメニューバーとか)を使うために必要になるかもしれないので覚えておくべきです。
ただしサブクラス化の代わりには出来ないのでコモンコントロール系にはほとんど使えません。


戻る