EternalWindows
コントロール基礎 / コントロールの操作

基本的にコントロールを操作するのはアプリケーションを使用するユーザーですが、 場合によってはアプリケーション内からコントロールを操作したいことがあります。 このような場合、アプリケーションは主に、メッセージ、マクロ、関数のいずれかの方法を使用して、 コントロールを操作することになります。 たとえば、チェックボックスにチェックを付ける方法は、次に示す3通りあります。

SendMessage(hwndCheckBox, BM_SETCHECK, BST_CHECKED, 0);

Button_SetCheck(hwndCheckBox, BST_CHECKED);

CheckDlgButton(hwnd, ID_CHECKBOX, BST_CHECKED);

1番目の方法は、コントロールにメッセージを送ることでコントロールを操作しています。 BMというのはButtonMessageの略であり、エディットコントロールなどではEMのようになります。 2番目の方法は、マクロを使用してコントロールを操作しています。 マクロの正体は、SendMessageでメッセージを送信するコードをラッピングしたものであり、 1番目の方法と比べて引数が少ないという利点があります。 ただし、マクロは数が非常に多く、windowsx.hのインクルードが必要な場合があります。 3番目の方法は、専用の関数を使用してコントロールを操作しています。 ただし、こうした関数が用意されているコントロールというのは非常に少ないため、 基本的にはメッセージかマクロでコントロールを操作することになります。

コントロールへのメッセージの送信はSendMessageで行うのが常ですが、 PostMessageでは何か問題があるのでしょうか。 たとえば、WM_SETTEXTというメッセージはコントロールの文字列を変更する場合に使用しますが、 このようなアドレスを要求するメッセージをPostMessageで送信するわけにはいきません。 PostMessageに指定したメッセージが処理されるのはウインドウプロシージャから制御を返し後であり、 この時点で先に示したアドレスが無効になってしまうからです。

メッセージやマクロでコントロールを操作する場合はコントロールのハンドルが必要になりますが、 IDさえ判っていればGetDlgItemでいつでも取得することができます。

HWND GetDlgItem(
  HWND hDlg,
  int nIDDlgItem
);

hDlgは、親ウインドウのハンドルを指定します。 nIDDlgItemは、コントロールのIDを指定します。 戻り値は、コントロールのウインドウハンドルになります。

今回のプログラムは、チェックボックスにチェックが付いている場合にボタンを無効にします。 動作内容としては前節と同様ですが、今回はメッセージを使用して実装されています。

#include <windows.h>

#define ID_BUTTON   100
#define ID_CHECKBOX 200

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {
		
	case WM_CREATE:
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 30, 30, 80, 40, hwnd, (HMENU)ID_BUTTON, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタンを有効化する"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 30, 80, 170, 40, hwnd, (HMENU)ID_CHECKBOX, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		SendMessage(GetDlgItem(hwnd, ID_CHECKBOX), BM_SETCHECK, BST_CHECKED, 0);
		return 0;

	case WM_COMMAND: {
		int nId = LOWORD(wParam);

		if (nId == ID_BUTTON)
			MessageBox(NULL, TEXT("ボタンが押されました。"), TEXT("OK"), MB_OK);
		else if (nId == ID_CHECKBOX)
			EnableWindow(GetDlgItem(hwnd, ID_BUTTON), SendMessage(GetDlgItem(hwnd, ID_CHECKBOX), BM_GETCHECK, 0, 0) == BST_CHECKED);
		else
			;
		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

前節では、CheckDlgButtonを使用してチェックを付けていましたが、 今回はBM_SETCHECKメッセージを送ることでチェックを付けています。 また、チェックの確認はIsDlgButtonCheckedではなく、BM_GETCHECKメッセージで実装しています。

メッセージクラッカについて

windowsx.hは、コントロールを操作するためのマクロに加えてメッセージクラッカも定義しています。 メッセージクラッカとは、メッセージがウインドウプロシージャに送られた時に特定の関数を呼び出す仕組みであり、 送られたメッセージの処理はこの関数内で行うことになります。 メッセージクラッカには、メッセージの数だけ関数を用意しなければならないという欠点がありますが、 その代わりとしてウインドウプロシージャの処理を簡素にすることができます。 次に、コード例を示します。

#include <windows.h>
#include <windowsx.h>

#define ID_BUTTON   100
#define ID_CHECKBOX 200

BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
void OnDestroy(HWND hwnd);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {
		
	HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
	HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
	HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
	CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタン"), WS_CHILD | WS_VISIBLE, 30, 30, 80, 40, hwnd, (HMENU)ID_BUTTON, lpCreateStruct->hInstance, NULL);
	CreateWindowEx(0, TEXT("BUTTON"), TEXT("ボタンを有効化する"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 30, 80, 170, 40, hwnd, (HMENU)ID_CHECKBOX, lpCreateStruct->hInstance, NULL);
	
	CheckDlgButton(hwnd, ID_CHECKBOX, BST_CHECKED);
	
	return TRUE;
}

void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	if (id == ID_BUTTON)
		MessageBox(NULL, TEXT("ボタンが押されました。"), TEXT("OK"), MB_OK);
	else if (id == ID_CHECKBOX)
		EnableWindow(GetDlgItem(hwnd, ID_BUTTON), IsDlgButtonChecked(hwnd, ID_CHECKBOX) == BST_CHECKED);
	else
		;
}

void OnDestroy(HWND hwnd)
{
	PostQuitMessage(0);
}

WindowProcで使用されているHANDLE_MSGがメッセージクラッカです。 これはマクロであり、適切な引数を指定して第3引数の関数を呼び出すコードをラッピングしています。 第3引数の関数のプロトタイプはメッセージによって異なっており、 たとえばWM_CREATEのプロトタイプを調べたい場合は、 HANDLE_WM_CREATEで検索することになります。



戻る