デッドタイムを使う
無限ループを作る
これまで、様々な描画処理の方法を説明してきました
それを駆使して、アニメーションなどを作ろうとした方もいるでしょう
しかし、マルチタスクの環境では思った以上にループ処理は難しいのです
基本的に、ウィンドウプロシージャ内に無限ループを作ってはいけません
なぜならば、ウィンドウプロシージャは有限の時間内に処理を終了し
制御をメッセージループに戻さなければメッセージを処理することができないのです
では、無限ループを使う処理はどのように実現すれば良いのでしょうか?
いくつかの方法がありますが、今回はその中でも最も簡単と思われる
デッドタイムと呼ばれる空き時間を利用した処理を紹介しましょう
デッドタイムとは、ウィンドウがメッセージを処理していない時間帯で
ユーザーからキーボードやマウスからの入力を待っている状態のことです
この状態に別の処理を行うことで、CPUを有効利用することができます
今の状態がデッドタイムかどうかを調べるには GetMessage() 関数では実現できません
この場合は PeekMessage() 関数を使います
BOOL PeekMessage(
LPMSG lpMsg ,
HWND hWnd ,
UINT wMsgFilterMin , UINT wMsgFilterMax ,
UINT wRemoveMsg
);
lpMsg にはメッセージを格納する MSG 構造体へのポインタを
hWnd にはメッセージを取得するウィンドウへのハンドルを
wMsgFilterMin と wMsgFilterMax は受け取るメッセージの最小値と最大値を指定します
ここまでは、GetMessage() 関数の引数とまったく同じです
wRemoveMsg は、メッセージの処理方法を表す定数を指定します
GetMessage() 関数はメッセージを必ず削除していましたが
この関数はメッセージの状態を得ることが目的なので、指定することができます
PM_NOREMOVE を指定すると、メッセージを削除しません
PM_REMOVE ならばメッセージをキューから削除します
GetMessage() の代用として PeekMessage() を使うのならば PM_REMOVE を使います
この関数はメッセージを取得できたなら 0 以外
メッセージを取得できなかった、すなわちデッドタイムならば 0 になります
これを利用して、メッセージを取得したならばそのメッセージを処理し
そうでなければ、他の処理時間に回すということが可能になります
#include <windows.h>
unsigned int lCount;
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
PAINTSTRUCT ps;
static TCHAR strCount[64];
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd , &ps);
wsprintf(strCount , "%d" , lCount);
TextOut(hdc , 10 , 10 , strCount , lstrlen(strCount));
EndPaint(hwnd , &ps);
}
return DefWindowProc(hwnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hwnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hwnd = CreateWindow(
TEXT("KITTY") , TEXT("Kitty on your lap") ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hwnd == NULL) return -1;
while (TRUE) {
if (PeekMessage(&msg , NULL , 0 , 0 , PM_REMOVE)) {
if(msg.message == WM_QUIT) break;
DispatchMessage(&msg);
}
else {
lCount++;
InvalidateRect(hwnd , NULL , FALSE);
}
}
return msg.wParam;
}
これは、数字をインクリメントしウィンドウに表示するという作業を
永遠と繰り返し続けるプログラムです
ただし、PeekMessage() がメッセージを取得した場合は
そのメッセージを優先に処理することができるので、基本的な動作は失われません
ウィンドウを移動するなど、メッセージの処理中はウィンドウのカウントが停止します
PeekMessage()
BOOL PeekMessage(
LPMSG lpMsg ,
HWND hWnd ,
UINT wMsgFilterMin , UINT wMsgFilterMax ,
UINT wRemoveMsg
);
スレッドのメッセージキューにメッセージがあるかどうかをチェックし
あれば、指定された構造体にそのメッセージを格納します
lpMsg - メッセージ情報を格納する、MSG 構造体型変数のポインタを指定します
hWnd - メッセージを取得するウィンドウのハンドルを指定します。全ての場合は NULL
wMsgFilterMin - メッセージの最小値を指定し、フィルタリングします。しない場合は0
wMsgFilterMax - メッセージの最大値を指定し、フィルタリングします。しない場合は0
戻り値 - メッセージを取得したときは 0 以外、取得しなかったときは 0