ウィンドウメッセージあれこれ
ウィンドウメッセージは数多く存在します。
ここではよく登場するウィンドウメッセージをまとめて説明します。
既に説明したメッセージもおさらいしておきます。
WM_CREATE
WM_CREATE
メッセージはCreateWindow/CreateWindowEx関数でウィンドウが生成されるときに通知されます。
WPARAMは使用されません。
LPARAMはCREATESTRUCT
構造体のポインタが格納されています。
CREATESTRUCT構造体は以下のようになっています。
- typedef struct tagCREATESTRUCTW {
LPVOID lpCreateParams;
HINSTANCE hInstance;
HMENU hMenu;
HWND hwndParent;
int cy;
int cx;
int y;
int x;
LONG style;
LPCWSTR lpszName;
LPCWSTR lpszClass;
DWORD dwExStyle;
} CREATESTRUCTW, *LPCREATESTRUCTW;
この構造体のメンバはCreateWindowEx関数の引数と全く同じで、引数に渡したものがそのまま送られてきます。
(ただし順序は逆になっています)
例えば以下のようにするとウィンドウタイトルを取得できます。
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CREATESTRUCT* cs;
WCHAR* str;
switch (message)
{
case WM_CREATE: //ウィンドウの生成
cs = (CREATESTRUCT*)lParam;
str = cs->lpszName;
MessageBox(NULL, str, L"情報", MB_OK);
//変数を介さず直接取得しても良い
//str = ((CREATESTRUCT*)lParam)->lpszName;
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
lpCreateParamsメンバ
CREATESTRUCT構造体のlpCreateParams
メンバは、CreateWindow(Ex)関数の最後の引数「LPVOID lpParam」に指定した値が格納されます。
これを利用してウィンドウプロシージャに任意の値を渡すことができます。
データ型は「LPVOID型」つまり「void*型」なので、任意のデータ型のポインタを格納できます。
(ポインタ型と同サイズの整数値を渡すことも可能です)
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindow(
szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
L"あいうえお" //lpParam
);
if (!hWnd) {
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
return TRUE;
}
//省略
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WCHAR* str;
switch (message)
{
case WM_CREATE:
str = ((CREATESTRUCT*)lParam)->lpCreateParams;
//strには"あいうえお"へのポインタが格納される
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ウィンドウ生成の拒否
WM_CREATE
メッセージは、ウィンドウを生成する前に送られてきます。
通常はウィンドウプロシージャで0
を返してウィンドウを生成しますが、-1
を返すとウィンドウの生成をキャンセルすることができます。
例えば条件を満たした場合だけプログラムを正常起動させたい場合などに利用できます。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: //ウィンドウの生成
if (MessageBox(NULL, L"ウィンドウを作成します。", L"情報", MB_OKCANCEL) == IDCANCEL)
return -1;
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
このコードは、メッセージボックスで「キャンセル」を選択するとウィンドウが生成されません。
その結果CreateWindow(Ex)関数はNULLを返すので、特別な処理をしない限りプログラムは終了します。
WM_DESTROY
WM_DESTROY
メッセージはウィンドウが破棄されたときに通知されます。
WPARAMとLPARAMは共に使用されません。
このメッセージが通知された時点ですでにウィンドウの破棄のための処理は完了しています。
このメッセージ内で破棄を中止することはできません。
メインウィンドウではこのメッセージを受け取ったらPostQuitMessage関数でプログラムを終了させるのが一般的です。
(プログラムを終了させないことも自由です)
WM_CLOSE
WM_CLOSE
メッセージはウィンドウが閉じられようとしているときに通知されます。
WPARAMとLPARAMは共に使用されません。
プログラマがこのメッセージを処理しない場合、DefWindowProc
関数によってDestroyWindow
関数が実行されます。
- BOOL DestroyWindow(
HWND hWnd
); - ウィンドウhWndを破棄する。
DestroyWindow関数は指定のウィンドウを破棄し、WM_DESTROYメッセージを発行します。
ウィンドウ破棄の拒否
WM_CLOSE
メッセージ内の処理は、(必要な処理の後に)DestroyWindow
関数を実行してウィンドウを破棄するのが一般的です。
逆に言えばDestroyWindow関数を実行しなければウィンドウは閉じられません。
例えばウィンドウを閉じて良いかをユーザーに確認するダイアログを表示すると、間違えてウィンドウを閉じてしまうことを防ぐことが出来ます。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE: //ウィンドウを閉じる
if (MessageBox(hWnd,
L"終了してもよろしいですか?",
L"確認", MB_YESNO) == IDYES)
{
MessageBox(hWnd,
L"終了します。",
L"確認", MB_OK);
DestroyWindow(hWnd);
}
else
{
MessageBox(hWnd,
L"終了をキャンセルしました。",
L"確認", MB_OK);
}
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
WM_QUIT
WM_QUIT
はPostQuitMessage関数が実行されたときに通知されます。
WPARAMはPostQuitMessage関数の引数に指定された値です。
LPARAMは使用されません。
このメッセージはメッセージループでプログラム終了の判定のために使用されるもので、ウィンドウプロシージャでは処理しません。
このメッセージ取得時のWPARAMの値をプログラムの終了値(WinMain関数のreturn文)に指定するのが一般的です。
WM_PAINT
WM_PAINT
メッセージはウィンドウの描画要求があったときに通知されます。
WPARAMとLPARAMは共に使用されません。
このメッセージではクライアント領域の表示に必要な描画処理を行います。
基本的な使い方は文字の表示の項を参照してください。
WM_MOVE
WM_MOVE
メッセージはウィンドウが移動されたときに通知されます。
WPARAMは使用されません。
LPARAMはクライアント領域のスクリーン座標(画面全体の座標)が格納されています。
(ウィンドウの座標ではない)
座標はGET_X_LPARAM
、GET_Y_LPARAM
マクロなどで取り出すことができます。
マウス座標の項も参照してください。
このメッセージはウィンドウが移動された後に通知されます。
LPARAMの値も移動後の座標です。
このメッセージ内で移動をキャンセルすることはできません。
WM_MOVING
WM_MOVING
メッセージはウィンドウが移動されようとしているときに通知されます。
WPARAMは使用されません。
LPARAMはウィンドウの座標を表すRECT構造体のポインタです。
LPARAMのRECT構造体の値を書き換えることで、移動先を変更することができます。
例えば以下のようにすると画面からはみ出さないウィンドウを作ることができます。
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT* rt;
static RECT rtScreen;
switch (message)
{
case WM_CREATE: //ウィンドウの作成
//画面サイズの取得
GetWindowRect(GetDesktopWindow(), &rtScreen);
break;
case WM_MOVING: //ウィンドウの移動中
rt = (RECT*)lParam; //移動しようとしている座標(RECT構造体)
if (rt->left < 0) { //左座標が0以下
rt->right += 0 - rt->left;
rt->left = 0;
}
if (rt->right > rtScreen.right) { //上座標が0以下
rt->left += rtScreen.right - rt->right;
rt->right = rtScreen.right;
}
if (rt->top < 0) { //右座標が画面サイズ以上
rt->bottom += 0 - rt->top;
rt->top = 0;
}
if (rt->bottom > rtScreen.bottom) { //下座標が画面サイズ以上
rt->top += rtScreen.bottom - rt->bottom;
rt->bottom = rtScreen.bottom;
}
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
GetWindowRect
関数はウィンドウサイズを取得します。
GetDesktopWindow
関数は画面のウィンドウハンドルを取得します。
WM_SIZE
WM_SIZE
メッセージはウィンドウのサイズが変更されたときに通知されます。
WPARAMはサイズ変更の種類を表す定数です。
LPARAMはサイズ変更後のクライアント領域のサイズが格納されています。
WPARAMは以下の定数のいずれかです。
定数 | 説明 |
---|---|
SIZE_RESTORED | 通常のサイズ変更 最大化/最小化から元に戻った時もこの値 |
SIZE_MINIMIZED | ウィンドウの最小化 |
SIZE_MAXIMIZED | ウィンドウの最大化 |
SIZE_MAXSHOW | 他のウィンドウが最大化状態から元に戻ったときに、すべてのポップアップウィンドウに送信される |
SIZE_MAXHIDE | 他のウィンドウが最大化されたときに、すべてのポップアップウィンドウに送信される |
サイズ変更後のクライアント領域のサイズはGET_X_LPARAM
、GET_Y_LPARAM
マクロなどで取り出すことができます。
マウス座標の項も参照してください。
このメッセージはウィンドウの生成後にも一度送られてきます。
ウィンドウやクライアント領域のサイズはこのメッセージ以外では変更されないので、GetClientRect関数などはここで実行して値を保存しておくとプログラムが少し軽くなります。
なお、ウィンドウクラスのスタイルにCS_HREDRAW
およびCS_VREDRAW
を指定していると、ウィンドウの幅および高さを変更したときにWM_PAINT
メッセージが送られるようになります。
(→WNDCLASS構造体)
WM_SIZING
WM_SIZING
メッセージはウィンドウのサイズが変更されようとしているときに通知されます。
WPARAMはサイズ変更されるウィンドウの端を表す定数です。
LPARAMは変更後のサイズを表すRECT構造体のポインタです。
WPARAMは以下の定数のいずれかです。
定数 | 説明 |
---|---|
WMSZ_LEFT | ウィンドウの左端 |
WMSZ_RIGHT | ウィンドウの右端 |
WMSZ_TOP | ウィンドウの上端 |
WMSZ_TOPLEFT | ウィンドウの左上端 |
WMSZ_TOPRIGHT | ウィンドウの右上端 |
WMSZ_BOTTOM | ウィンドウの下端 |
WMSZ_BOTTOMLEFT | ウィンドウの左下端 |
WMSZ_BOTTOMRIGHT | ウィンドウの右下端 |
LPARAMはこれから変更しようとしているサイズ情報で、これを書き換えることで変更後のサイズを制御することができます。
例えば一定以上の大きさにはできないウィンドウを作ることができます。
WM_COMMAND
WM_COMMAND
メッセージは、ウィンドウ上のコントロール(ボタンなど)を操作したときに通知されます。
詳しくはボタンコントロールで説明します。
キー/マウス入力
マウス入力に関してはマウス操作を参照してください。
キー入力に関してはキー入力を参照してください。
ユーザー定義のメッセージ
ウィンドウメッセージは、Windows API側があらかじめ定義しているもののほか、プログラマが自分で定義して使用できるものもあります。
WM_APP
WM_APP
メッセージは、アプリケーション内で共通のメッセージを定義するために使用します。
具体的には「WM_APP + 1
」のように、定数に数字を加算して使用します。
#define WM_MYMESSAGE1 (WM_APP + 1)
#define WM_MYMESSAGE2 (WM_APP + 2)
加算できる数値は「0~16383」の範囲です。
定義したメッセージはSendMessage関数などでウィンドウプロシージャに送信します。
受け取ったユーザー定義メッセージをどのように使用するかはプログラマの自由です。
MDI子ウィンドウの操作の項では具体的な使用例を掲載しています。
WM_USER
WM_USER
メッセージは、ウィンドウクラスに固有のメッセージを定義するために使用します。
こちらも「WM_USER + 1
」のようにして使用します。
加算できる数値は「0~31743」の範囲です。
こちらは主にコントロール(ボタンとかのアプリケーションの「部品」のこと)を自作する場合に使用します。
実は「WM_USER + 1
」などは既存のコントロールに内部的に使用されていますが、異なるウィンドウクラスで同じ値が使用されている場合は問題ありません。
アプリケーション共通のメッセージ(WM_APP
を使用すべきもの)として使用すると問題が発生する可能性があります。