SendMessage関数
前ページではボタンの作成について説明しました。
ボタンのスタイルについても説明しましたが、もっと詳細なカスタマイズを行うにはSendMessage
関数を使用します。
- LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
); - ウィンドウhWndのウィンドウプロシージャにメッセージMsgを送信する。
追加のデータとしてwParam、lParamを指定できる。
戻り値は送信するメッセージにより異なる。
SendMessage関数はコントロール類の設定に限らず、Windows APIでは頻繁に使用される関数です。
引数はウィンドウプロシージャと同じで、この関数は指定のウィンドウハンドルのウィンドウプロシージャに任意のメッセージを送信(=実行)することができます。
通常のウィンドウのほか、コントロールもウィンドウの一種なので、コントロールに対しても様々な命令を実行することができます。
送信可能なメッセージの種類やパラメーター、戻り値などは送信先によって異なります。
ここではボタンコントロールに対して使用できるメッセージを説明します。
ボタンのカスタマイズ
フォントの変更
SendMessage
関数を使用してボタンのフォントを変更してみましょう。
フォントの変更はWM_SETFONT
メッセージをコントロールに送信します。
#include <windows.h>
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
#define IDC_BUTTON2 101
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HFONT hFont;
HWND hBtn;
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
hBtn = CreateWindow(
L"BUTTON", L"Click",
WS_CHILD | WS_VISIBLE,
10, 10, 60, 20,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
//ボタンの作成
//こちらはフォントを変更しない
CreateWindow(
L"BUTTON", L"Click",
WS_CHILD | WS_VISIBLE,
10, 35, 60, 20,
hWnd, (HMENU)IDC_BUTTON2, hInst, NULL);
//フォントの作成
hFont = CreateFont(12, 0, 0, 0,
FW_NORMAL, FALSE, FALSE, 0,
SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
L"MS UI Gothic");
//フォントの変更
SendMessage(hBtn, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
break;
case WM_DESTROY: //ウィンドウの破棄
//フォントを削除
DeleteObject(hFont);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
実行結果です。
上がフォントを変更したボタン、下が何も設定していないボタンです。
メッセージの送信先はボタンのハンドル(CreateWindow関数の戻り値)を指定します。
(ボタン識別子(IDC_BUTTON1
)ではない)
メッセージにはWM_SETFONT
を指定します。
パラメーターのWPARAMにはあらかじめ取得/作成したフォントのハンドルを指定します。
要求されるのはWPARAM型ですから、キャストしておきます。
(キャストしなくても動作はしますがVisual Studioが警告を出します)
なお、フォントを作成した場合はそのフォントを指定したコントロールが破棄されるまではフォントを削除しないようにしてください。
WPARAM、LPARAMは常に必要なパラメータがセットされるわけではなく、使用されないこともあります。
その場合は0
またはNULL
を指定します。
0を指定する場合はキャストは不要です。
LPARAMはMAKELPARAM
マクロを使用してパラメータを作成します。
なお、WM_SETFONTメッセージ送信時のSendMessage関数の戻り値はありません。
(常に0。何か値を返したとしても意味のない値)
MAKEWPARAM、MAKELPARAMマクロ
SendMessage
関数の第三引数はWPARAM型、第四引数はLPARAM型です。
これらの型の実体は整数値ですが、一度に複数の情報をやり取りするために、メモリ領域を二つに分割してそれぞれに別のデータを格納することがあります。
メモリ領域の前半分を上位ワード、後半分を下位ワードといいます。
ふたつのデータを合体してひとつのデータにして送信するわけですが、そのためにMAKEWPARAM
マクロやMAKELPARAM
マクロを使用します。
名前の通り、MAKEWPARAMマクロはWPARAM用のデータを作成し、MAKELPARAMマクロはLPARAM用のデータを作成します。
WM_SETFONTメッセージのLPARAMは、下位ワードにコントロールを再描画するか否かをBOOL型で要求します。
TRUE
を指定するとフォントの変更後に再描画されます。
(再描画しない場合はFALSE
を指定します)
上位ワードは使用しないので0を指定します。
この二つのデータをMAKELPARAMマクロでLPARAM型に変換し、送信します。
データの指定は(下位ワード,上位ワード)の順で、これはMAKEWPARAMマクロでも同様です。
//MAKELPARAM(LOWORD, HIWORD)
MAKELPARAM(TRUE, 0)
ちなみに、このような合体されたデータから元のデータを復元するにはHIWORD
マクロ、LOWORD
マクロを使用します。
WPARAM wparam = MAKEWPARAM(123, 456);
WORD loword = LOWORD(wparam); //123
WORD hiword = HIWORD(wparam); //456
WM_○○メッセージ
WM_SETFONTメッセージなどのWM_から始まるメッセージは、ボタンに限らずウィンドウ全般に対して使用できるメッセージです。
(WindowMessageの略)
対して、次に紹介するBM_から始まるメッセージはボタン用のメッセージです。
(ButtonMessageの略)
その他にもエディットコントロール用のEM_やリストボックス用のLB_など、プリフィックス(接頭辞)である程度メッセージの種類を見分けることができます。
ビットマップの表示
ボタンにはビットマップ画像を表示することもできます。
ビットマップリソースの追加およびビットマップの読み込みについてはリソースファイル、ビットマップの表示の項を参照してください。
ビットマップを表示するには、まずボタンスタイルにBS_BITMAP
を追加します。
SendMessage関数ではBM_SETIMAGE
メッセージを指定します。
WPARAMには定数IMAGE_BITMAP
を指定します。
LPARAMにはビットマップハンドルを指定します。
戻り値は元々ボタンに設定されていたビットマップハンドルで、なければNULL
です。
#include <windows.h>
#include "resource.h"
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hBmp;
HWND hBtn;
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
hBtn = CreateWindow(
L"BUTTON", L"Click",
WS_CHILD | WS_VISIBLE | BS_BITMAP,
10, 10, 64, 64,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
//ビットマップの読み込み
hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
//ボタンにビットマップを表示
SendMessage(hBtn, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
MessageBox(hWnd, L"クリック!", L"情報", MB_OK);
break;
}
break;
case WM_DESTROY: //ウィンドウの破棄
DeleteObject(hBmp);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
実行結果です。
テキストの書き換え
ボタンは条件によってテキスト(キャプション)を書き換えたい時があります。
テキストの差し替えはWM_SETTEXT
メッセージを送信します。
WPARAMは使用しません。
LPARAMは差し替えたい文字列を指定します。
戻り値はテキストの設定に成功した場合はTRUE
です。
失敗した場合は0以下の値です。
このメッセージは失敗した時にFALSE
、つまり0
を返すとは限りません。
#include <windows.h>
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hBtn;
static BOOL state;
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
hBtn = CreateWindow(
L"BUTTON", L"OFF",
WS_CHILD | WS_VISIBLE,
10, 10, 60, 25,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
state = !state;
//テキストの書き換え
SendMessage(hBtn, WM_SETTEXT, 0, (LPARAM)(state ? L"ON" : L"OFF"));
break;
}
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ボタンをクリックするごとに「ON」「OFF」の表示が切り替わります。
なお、テキスト関係のメッセージはハンドルさえわかっていればテキスト情報を持つウィンドウやコントロールにも使用できます。
例えばウィンドウにWM_SETTEXTメッセージを送信するとタイトルバー文字列を変更することができます。
(TextOut関数などでクライアント領域に直接描画しているテキストは書き換えることはできません)
SetWindowText関数
テキストの変更はWM_SETTEXT
メッセージのほか、SetWindowText
関数でも行えます。
- BOOL SetWindowTextW(
HWND hWnd,
LPCWSTR lpString
); - ウィンドウhWndのテキストを文字列lpStringに設定する。
成功した場合は0以外を、失敗した場合は0を返す。
こちらのほうが引数が少なく扱いやすいかと思います。
ただし変更可能なのは関数呼び出し元と同一プロセスのウィンドウやコントロールだけです。
SetWindowText関数は内部的にWM_SETTEXTメッセージを送信しています。
この関数のように、いくつかのメッセージはSendMessage関数で送信するのと同等の機能を持つ関数(またはマクロ関数)が用意されています。
テキストの取得
テキストの取得はWM_GETTEXT
メッセージを送信します。
WPARAMは取得する最大の文字数です。
(NULL文字を含む)
LPARAMは文字列を格納するバッファへのポインタです。
戻り値は取得された文字数です。
(NULL文字は含まない)
バッファサイズが足りない場合でも文字列の終端にはNULL文字が挿入されます。
(ただし文字列は途中で切れます)
#include <windows.h>
#include <strsafe.h>
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
#define BUFFERSIZE 32
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hBtn;
WCHAR buf[BUFFERSIZE];
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
hBtn = CreateWindow(
L"BUTTON", L"あいうえお",
WS_CHILD | WS_VISIBLE,
10, 10, 120, 25,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
//ボタンテキストの取得
SendMessage(hBtn, WM_GETTEXT, (WPARAM)BUFFERSIZE, (LPARAM)buf);
MessageBox(hWnd, buf, L"ボタンのテキスト", MB_OK);
break;
}
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ボタンのテキストを取得し、メッセージボックスに表示しています。
GetWindowText関数
テキストの取得はWM_GETTEXT
メッセージのほか、GetWindowText
関数でも行えます。
ただし取得可能なのは同一プロセスのウィンドウやコントロールだけです。
- int GetWindowTextW(
HWND hWnd,
LPWSTR lpString,
int nMaxCount
); - ウィンドウhWndのテキストを文字列バッファlpStringにnMaxCount文字分コピーする。
成功した場合はコピーされた文字列の文字数を返す。
失敗した場合は0を返す。
テキストの長さの取得
テキストの長さの取得はWM_GETTEXTLENGTH
メッセージを送信します。
WPARAMとLPARAMは使用しないので、両方とも0を指定します。
戻り値は文字列の文字数です。
(終端のNULL文字は含まない)
#include <windows.h>
#include <strsafe.h>
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
#define BUFFERSIZE 32
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const WCHAR *format = L"ボタンテキストの長さは%uです。";
static HWND hBtn;
size_t length;
WCHAR buf[BUFFERSIZE];
switch (message)
{
case WM_CREATE: //ウィンドウ作成
//ボタンの作成
hBtn = CreateWindow(
L"BUTTON", L"あいうえお",
WS_CHILD | WS_VISIBLE,
10, 10, 120, 25,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
break;
case WM_COMMAND: //コントロールの操作
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
//ボタンテキストの長さを取得
length = (size_t)SendMessage(hBtn, WM_GETTEXTLENGTH, 0, 0);
StringCchPrintf(buf, BUFFERSIZE, format, length);
MessageBox(hWnd, buf, L"情報", MB_OK);
break;
}
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
実行結果です。
GetWindowTextLength関数
テキストの長さの取得はWM_GETTEXTLENGTH
のほか、GetWindowTextLength
関数でも行えます。
ただし取得可能なのは同一プロセスのウィンドウやコントロールだけです。
- int GetWindowTextLengthW(
HWND hWnd
); - ウィンドウhWndのテキストの長さを返す。
ボタンのクリック
ボタンのクリックをプログラムから行う場合はBM_CLICK
メッセージを使用します。
WPARAM、LPARAM共に使用しないのでNULL
(または0)を指定します。
戻り値はありません。
HWND hBtn;
hBtn = CreateWindow(L"BUTTON"
//省略
//hBtnをクリック
SendMessage(hBtn, BM_CLICK, 0, 0);
ボタンの強調表示
ボタンの状態を強調表示状態にするにはBM_SETSTATE
メッセージを使用します。
WPARAMにTRUE
を指定すると強調表示がオンになります。
FALSE
を指定するとオフになります。
LPARAMは使用しないのでNULL
(または0)を指定します。
戻り値はありません。
強調表示とはボタンを押している状態の「外見」のことです。
このメッセージはボタンの見た目が変化するだけで、ボタンのクリックは発生しません。
(WM_COMMAND
メッセージは送信されない)
#include <windows.h>
//ウィンドウの生成等は省略
#define IDC_BUTTON1 100
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hBtn;
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE: //ウィンドウ作成
hBtn = CreateWindow(
L"BUTTON", L"Click",
WS_CHILD | WS_VISIBLE,
10, 10, 60, 20,
hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
break;
case WM_LBUTTONDOWN: //マウス左ダウン
SendMessage(hBtn, BM_SETSTATE, TRUE, 0);
break;
case WM_LBUTTONUP: //マウス左アップ
SendMessage(hBtn, BM_CLICK, 0, 0);
SendMessage(hBtn, BM_SETSTATE, FALSE, 0);
break;
case WM_RBUTTONDOWN: //マウス右ダウン
SendMessage(hBtn, BM_SETSTATE, TRUE, 0);
break;
case WM_RBUTTONUP: //マウス右アップ
SendMessage(hBtn, BM_SETSTATE, FALSE, 0);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
MessageBox(hWnd, L"ボタンをクリックしました。", L"情報", MB_OK);
break;
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 10, 40, L"マウス左: ボタンをクリック", 14);
TextOut(hdc, 10, 65, L"マウス右: ボタンを押下(見た目だけ)", 19);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ボタンのクリック/強調表示のサンプルです。
ウィンドウ上を左クリックするとボタンに対してBM_CLICK
メッセージを送信します。
ボタンがクリックされ、メッセージボックスが表示されます。
右クリックすると同じようにボタンが押されますが、BM_SETSTATE
メッセージで見た目を変化させているだけで実際にクリック処理は発生しません。
PostMessage関数
SendMessage関数と似た動作をするものにPostMessage
関数があります。
- BOOL PostMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
); - ウィンドウhWndのメッセージキューにメッセージMsgをポストする。
追加のデータとしてwParam、lParamを指定できる。
成功した場合は0以外、失敗した場合は0を返す。
SendMessage関数はウィンドウプロシージャを直接実行します。
送信したメッセージの処理が終了するまで呼び出し元は待機し、処理結果を戻り値として取得します。
いわゆる同期処理です。
PostMessage関数はウィンドウのメッセージキューにメッセージを送信します。
メッセージキューに送信されたメッセージは、システムが都合の良いタイミングで実行します。
呼び出し元はウィンドウプロシージャの処理を待たないので、すぐに制御が返ってきます。
いわゆる非同期処理です。
メッセージを送信してもすぐに処理が実行される保証はなく、処理の実行結果を戻り値で受け取ることはできません。
(メッセージの「ポスト」とも言います。マイクロソフトの翻訳では「投稿」となっていることもあります。)
非同期であるため、PostMessage関数の実行から送信先のウィンドウプロシージャの処理の実行までの間には多少の時間差があります。
そのため、パラメーターにポインタを指定する場合は注意が必要です。
PostMessage関数の実行時にはポインタが指す先のデータが存在していても、メッセージの送信先がデータを処理する時には既にそのデータが解放されていて存在しない(不定なデータである)可能性があるためです。
特にローカル変数のポインタを指定する場合は変数の寿命に気を付けましょう。