文字の表示

描画処理

続いて、ウィンドウに簡単な文字列である、「Hello,World」を表示するサンプルを取り上げます。ウィンドウに文字を表示する際には、文字をグラフィックとして表示するTextOut関数を用います。また、その際には_TCHAR型で文字列の操作を行う方法についても簡単に説明します。

サンプルプログラム

では実際にウィンドウ上に簡単な文字列を表示するプログラムを見てみましょう。まずは以下のプロジェクトを実行してみてください。

win32proj3-1:WinMain.cpp
ダウンロード
// ************************************
// Ex 文字列を表示するプログラム
// ************************************
//必要なヘッダーファイルのインクルード
#define STRICT

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>

// シンボル定義及びマクロ
#define WINDOW_WIDTH    800
#define WINDOW_HEIGHT   600

//	インスタンス(グローバル変数)
HINSTANCE hInst;

//	ウィンドウプロシージャのコールバック関数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
	static TCHAR szWindowClass[] = _T("Sample03");
	static TCHAR szTitle[] = _T("ウィンドウを使ったアプリのサンプル②");
	
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

	if (!RegisterClassEx(&wcex))
	{
		MessageBox(NULL,
			_T("RegisterClassExの処理に失敗しました"),
			_T("Sample03"),
			NULL);

		return 1;
	}

	hInst = hInstance; // グローバル変数に値を入れる

	// The parameters to CreateWindow explained:
	// szWindowClass				: アプリケーションの名前
	// szTitle						: タイトルバーに現れる文字列
	// WS_OVERLAPPEDWINDOW			: 生成するウィンドウのタイプ
	// CW_USEDEFAULT, CW_USEDEFAULT	: 最初に置くポジション (x, y)
	// WINDOW_WIDTH, WINDOW_HEIGHT	: 最初のサイズ (幅, 高さ)
	// NULL							: このウィンドウの親ウィンドウのハンドル
	// NULL							: メニューバー(このサンプルでは使用せず)
	// hInstance					: WinMain関数の最初のパラメータ
	// NULL							: WM_CREATE情報(このアプリケーションでは使用せず)
	HWND hWnd = CreateWindow(
		szWindowClass,
		szTitle,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		WINDOW_WIDTH, WINDOW_HEIGHT,
		NULL,
		NULL,
		hInstance,
		NULL
		);
	//	ウィンドウが生成できなかった場合
	if (!hWnd)
	{
		MessageBox(NULL,
			_T("ウィンドウ生成に失敗しました!"),
			_T("Sample03"),
			NULL);
		return 1;
	}

	// ウィンドウの表示に必要なパラメータ:
	// hWnd		: CreateWindowの戻り値
	// nCmdShow	: WinMainの引数の4番目
	ShowWindow(hWnd,
		nCmdShow);
	UpdateWindow(hWnd);

	// メインのメッセージループ:
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return (int)msg.wParam;
}

//	ウィンドウプロシージャ(メッセージに対するコールバック関数)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("Hello, World!");
	switch (message)
	{
	case WM_PAINT:
		//	描画処理の開始
		hdc = BeginPaint(hWnd, &ps);
		// 文字列の出力。「Hello, World!」と出力する。
		TextOut(hdc,
			5, 5,
			greeting, _tcslen(greeting));
		//	ペイント処理の終了
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}

	return 0;
}

実行結果は、以下のようになります。(図3-1.)

図3-1.ウィンドウに文字列の表示

ウィンドウに文字列の表示

このプログラムを実行すると、「Hello,World!」という文字列が表示されたウィンドウが出現します。右上の赤い×ボタンを押すと、ウィンドウは消えて、プログラムは終了します。

プログラムの仕組み

WM_PAINT

このプログラムは、2日目で取り上げた、単純なウィンドウを表示するサンプルが表示されるサンプルとほとんど一緒です。大きな違いは、描画部分である113行目から、122行の処理です。

ここに出てくる、WM_PAINTは、ウィンドウズのイベントの一つで、ウィンドウの再描画を行うことを要求するメッセージです。ウィンドウズは、絶えずこのメッセージをアプリに送信し続け、常に画面の更新を行っています。したがって、描画処理は、このイベントの部分に記述することになります。

描画の開始と終了

描画の開始

文字列に限らず、WM_PAINTの処理で何らかの描画を行う際には、まずBeginPaint関数を呼び出す必要があります。(115行目)

BeginPaint関数
hdc = BeginPaint(hWnd, &ps);

この関数には、引数として、ウィンドウハンドルhWndを渡します。すると、デバイスコンテキストのハンドルである、HDC型のhdcと、PAINTSTRUCT型のpsを得られます。デバイスコンテキストのハンドルは、描画処理の関数に必要なハンドルです。描画命令には、このハンドルが必要となります。

そして、121行目に出てくるEndPaint関数には、このpsを与えます。これは、描画の終わりを意味する関数です。文字列描画に限らず、描画処理は、この二つの関数の間に記述します。

EndPaint関数
EndPaint(hWnd, &ps);

この処理によって、描画処理は終了します。

TextOut関数

では、次に具体的な描画処理を行っている、TextOut関数について説明しましょう。このサンプルでは、117~119行目に記述されています。 TextOut関数の処理

TextOut(hdc,
5, 5,
greeting, _tcslen(greeting));

このTextOut関数の仕様は以下のようになっています。

TextOut関数
BOOL TextOut(
    HDC hdc,                      // デバイスコンテキストのハンドル
    int nXStart,                   // 開始位置(基準点)の x 座標
    int nYStart,                   // 開始位置(基準点)の y 座標
    LPCTSTR lpString,           // 文字列
    int cbString                   // 文字数
);

最初の引数は、デバイスコンテキストで、描画命令であるTextOutには必ず必要です。二つ目、三つ目の座標は、ウィンドウ内のクライアント領域と呼ばれる場所での座標を表します。この領域は、ウィンドウ内の白い部分であり、左上隅の座標が、(0,0)となります。

そして、その次に表示する文字列、最後に文字数を指定します。ここで開設が必要なのが、最後の文字数のカウントです。

このサンプルでは、文字列の描画を取り上げましたが、その他の図形の描画なども、同様の処理を行います。

文字列のカウント

このプログラムの110行目で定義されている文字列greetingが、表示する文字列であり、その文字列の長さを取得しているのが、121行目の_tcslen関数です。これは、C言語の標準関数である、strlenに相当する関数で、TCHAR型で定義された文字列の長さを得るには、この関数を利用する必要があります。

以上で、ウィンドウへの描画処理に関する説明を終了します。次はキーボードやマウスなど、様々なイベントに対する処理について説明します。