●Win32API(C言語)編 第37章 ダイアログボックス(モードレス)

'2007/2/12 GetMessage() が -1 を返す可能性に対応。
'2006/12/25 VisualC++2005 ExpressEdition への対応。

○モードレスダイアログボックス

前章ではモーダルダイアログボックスを説明しました。今回はモードレスダイアログボックス です。前章でも説明したように、モードレスダイアログボックスは、表示中であっても、元ウィンドウ側を操作することが できます。

もちろん、リソースを作成する必要がありますが、これはモーダルダイアログボックスとまったく同じで構いません。 違いはソースコード側だけです。今回は、前章で作成したダイアログをそのまま使用します。

#include <windows.h>
#include <tchar.h>
#include "resource.h"

// 定数
#define WINDOW_WIDTH  (400)		// ウィンドウの幅
#define WINDOW_HEIGHT (300)		// ウィンドウの高さ
#define WINDOW_X ((GetSystemMetrics( SM_CXSCREEN ) - WINDOW_WIDTH ) / 2)
#define WINDOW_Y ((GetSystemMetrics( SM_CYSCREEN ) - WINDOW_HEIGHT ) / 2)

// グローバル変数
HINSTANCE g_hInst;              // インスタンスハンドル
HWND g_hDlg;                    // ダイアログボックスのハンドル

// プロトタイプ宣言
HWND Create(HINSTANCE hInst);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);


// 開始位置
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, int showCmd)
{
	HWND hWnd;
	MSG msg;

	g_hInst = hInst;  // インスタンスハンドルをグローバル変数に保存しておく

	// ウィンドウを作成する
	hWnd = Create( hInst );
	if( hWnd == NULL )
	{
		MessageBox( NULL, _T("ウィンドウの作成に失敗しました"), _T("エラー"), MB_OK );
		return 1;
	}

	// ウィンドウを表示する
	ShowWindow( hWnd, SW_SHOW );
	UpdateWindow( hWnd );

	// メッセージループ
	while( 1 )
	{
		BOOL ret = GetMessage( &msg, NULL, 0, 0 );  // メッセージを取得する
		if( ret == 0 || ret == -1 )
		{
			// アプリケーションを終了させるメッセージが来ていたら、
			// あるいは GetMessage() が失敗したら( -1 が返されたら )、ループを抜ける
			break;
		}
		else
		{
			// ダイアログボックスに対するメッセージか
			if( g_hDlg == NULL || !IsDialogMessage( g_hDlg, &msg ) )
			{
				// ダイアログへのメッセージでなければ、ウィンドウに送る
				TranslateMessage( &msg );
				DispatchMessage( &msg );
			}
		}
	}

	return 0;
}

// ウィンドウを作成する
HWND Create(HINSTANCE hInst)
{
	WNDCLASSEX wc;

	// ウィンドウクラスの情報を設定
	wc.cbSize = sizeof(wc);               // 構造体サイズ
	wc.style = CS_HREDRAW | CS_VREDRAW;   // スタイル
	wc.lpfnWndProc = WndProc;             // ウィンドウプロシージャ
	wc.cbClsExtra = 0;                    // 拡張情報1
	wc.cbWndExtra = 0;                    // 拡張情報2
	wc.hInstance = hInst;                 // インスタンスハンドル
	wc.hIcon = (HICON)LoadImage(          // アイコン
		NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON,
		0, 0, LR_DEFAULTSIZE | LR_SHARED
	);
	wc.hIconSm = wc.hIcon;                // 子アイコン
	wc.hCursor = (HCURSOR)LoadImage(      // マウスカーソル
		NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR,
		0, 0, LR_DEFAULTSIZE | LR_SHARED
	);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // ウィンドウ背景
	wc.lpszMenuName = NULL;                     // メニュー名
	wc.lpszClassName = _T("Default Class Name");// ウィンドウクラス名
	
	// ウィンドウクラスを登録する
	if( RegisterClassEx( &wc ) == 0 ){ return NULL; }

	// ウィンドウを作成する
	return CreateWindow(
		wc.lpszClassName,      // ウィンドウクラス名
		_T("Sample Program"),  // タイトルバーに表示する文字列
		WS_OVERLAPPEDWINDOW,   // ウィンドウの種類
		WINDOW_X,              // ウィンドウを表示する位置(X座標)
		WINDOW_Y,              // ウィンドウを表示する位置(Y座標)
		WINDOW_WIDTH,          // ウィンドウの幅
		WINDOW_HEIGHT,         // ウィンドウの高さ
		NULL,                  // 親ウィンドウのウィンドウハンドル
		NULL,                  // メニューハンドル
		hInst,                 // インスタンスハンドル
		NULL                   // その他の作成データ
	);
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
	switch( msg )
	{
	case WM_CREATE:			// ウィンドウが作成されたとき
		// モードレスダイアログボックスを作成する
		g_hDlg = CreateDialog( g_hInst, _T("IDD_DIALOG1"), hWnd, DlgProc );
		return 0;

	case WM_RBUTTONUP:      // マウスの右ボタンが離されたとき
		// 作成したダイアログを表示する
		ShowWindow( g_hDlg, SW_SHOW );
		return 0;

	case WM_DESTROY:        // ウィンドウが破棄されるとき
		// ダイアログボックスを破棄する
		DestroyWindow( g_hDlg );

		PostQuitMessage( 0 );
		return 0;
	}

	return DefWindowProc( hWnd, msg, wp, lp );
}

// ダイアログプロシージャ
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
	switch( msg ){
	case WM_INITDIALOG:  // ダイアログボックスが作成されたとき
		return TRUE;

	case WM_COMMAND:     // ダイアログボックス内の何かが選択されたとき
		switch( LOWORD( wp ) ){
		case IDOK:       // 「OK」ボタンが選択された
		case IDCANCEL:   // 「キャンセル」ボタンが選択された
			// ダイアログボックスを消す
			ShowWindow( g_hDlg, SW_HIDE );
			break;
		}
		return TRUE;

	case WM_CLOSE:		// ダイアログボックスが閉じられるとき
		// ダイアログボックスを消す
		ShowWindow( g_hDlg, SW_HIDE );
		return TRUE;
	}

	return FALSE;  // DefWindowProc()ではなく、FALSEを返すこと!
}

モードレスダイアログボックスの場合、作成するときに使う関数はDialogBox()ではなく、 CreateDialog()になります。

HWND CreateDialog(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc);

引数に関しては、モーダルダイアログボックスを作るDialogBox()と同じです。しかし戻り値は異なります。 これは、モーダルダイアログボックスの場合は、元ウィンドウが操作できなくなるため、制御が返されないことに 対し、モードレスダイアログボックスの場合は、制御がすぐに返されるためです。CreateDialog()で返されるのは、 作成したモードレスダイアログボックスのハンドルです。

注意事項として、CreateDialog()を呼び出しただけでは、まだ画面上に表示されないという点があります。正確 には、リソースを作成したときに「可視」設定を行っておけば、ただちに表示されますが、デフォルトではそうなって いません。ダイアログボックスを表示させるには、ShowWindow()を呼び出します。ShowWindow()は、ウィンドウを表示 させるために使っているものです。

ダイアログプロシージャが必要であることに、変わりはありません。しかし、モードレスダイアログボックス の場合には、メッセージの配信を行うメッセージループの部分に違いがあります。ウィンドウプロシージャへメッセージ を配信するためのTranslateMessage()やDispatchMessage()を呼び出しても、モードレスダイアログボックスへは メッセージが送られません。そこで、IsDialogMessage()という別の関数が登場します。

BOOL IsDialogMessage(HWND hDlg, LPMSG lpMsg);

この関数は、第2引数で指定したメッセージが、ダイアログボックスに対してのものなのかを判定し、そうであれば 処理を行い、TRUEを返します。ダイアログボックス宛てのものでなければ、FALSEを返します。これによって、メッセージ の配信先の切り分けを行う訳です。

最後に、モードレスダイアログボックスを破棄する方法です。モーダルダイアログボックスの場合は、ダイアログ プロシージャ内でEndDialog()を呼び出していましたが、モードレスダイアログボックスの場合は、ダイアログ側でも、 ウィンドウ側でも破棄命令が実行できなくてはなりません。そこで、モードレスダイアログボックスの破棄は、 ウィンドウの破棄と同じく、DestroyWindow()を使うことになっています。引数に、CreateDialog()が返したダイアログ ボックスのハンドルを指定します。




モーダルダイアログボックスと比べて、少し面倒な部分もありますが、それほど難しい訳ではありません。ただ、 ShowWindow()を呼び出さないと画面に見えないという点は、案外忘れがちなので注意が必要です。ちなみにそれは、 ウィンドウでも同じことですが、それは同じスケルトンプログラムを使っていれば気にならないでしょう。


Win32API(C言語)編のトップページに戻る

サイトのトップページに戻る

1