Windows API

Windows API

「ウィンドウズプログラムを作ろう!」

各章の最後は、簡易リファレンス(?)になってるので、御活用下さい。

※無断転載は禁止。転載したい時は僕にメール等で一声かけて下さい。


〜目次〜

0.この講座について

1.初めの一歩

2.ダイアログボックス

3.図形を描こう

4.図形を描こう(その2)

5.マウスイベント一挙公開

6.キーイベント一挙公開

7〜



0.この講座について


■主旨

みなさん、こんにちは。この講座はウィンドウズプログラムを作る講座です。
ウィンドウズプログラムというのは、今まで作ってきた黒い画面に白い文字のやつ(コンソールアプリケーションといいます)じゃなくて、
アプリケーション(メモ帳とか、ウィンドウがでてメニューが出るの)を作るためのプログラムです。
つまり、「ちゃんとした動くアプリケーションを作ろう」というのがこの講座の主旨です。

ですが、ウィンドウズプログラムはC言語で書く訳なので...
最低でもC言語「基礎編」の内容を理解してからの方がいいと思います。
まぁ、ある程度分かっていれば大丈夫でしょう。


1.初めの一歩


■APIって?

いきなりタイトルに「API」っていう訳分からないものが出てきたと思います。
API(Apprlication Programming Interface)っていうのは、
「システムが提供する関数群」とか言ったりしますが、
要は、普通に動かすアプリケーションの標準の機能をまとめたのがAPI。
昔アプリケーションを作る時、例えばキー操作の部分を作るときで、
とてもめんどくさく複雑なプログラムを組む必要がありました。
そこでAPIは、そのめんどくさく複雑なプログラムを一つにまとめ、
一発で呼び出して実行できるようにしたものです。


■ひな形


では、ウィンドウを表示させるだけのプログラムを組んでみます。

<サンプルプログラム>
#include <windows.h>



int APIENTRY WinMain ( HINSTANCE hInstance,
																																			  HINSTANCE hPrevInstance,
																																					LPSTR lpCmdLine,
																																					int nCmdShow)
{
						WNDCLASSEX wcex;									//ウィンドウクラス構造体
						HWND hWnd;																				//ウィンドウハンドル
						MSG msg;																								//メッセージ構造体
			
						//ウィンドウクラスの設定
						//要は、どんな感じのウィンドウを作るのかを記述している。
						wcex.cbSize = sizeof(WNDCLASSEX);
						wcex.style = CS_HREDRAW | CS_VREDRAW;
						wcex.lpfnWndProc = (WNDPROC)WndProc;
						wcex.cbClsExtra = 0; 
						wcex.cbWndExtra = 0;
						wcex.hInstance = hInstance;
						wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
						wcex.hCursor = LoadIcon(NULL, IDC_ARROW);
						wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
						wcex.lpszMenuName = NULL;
      wcex.lpszClassName = "ModelApp";
						wcex.hIconsm = LoadIcon(NULL, IDI_APPLICATION);

						//ウィンドウクラスの登録
						RegisterClassEx(&wcex);


						//ウィンドウの作成
						hWnd = CreateWindow(wcex.lpszClassName,
																																									"ひな形",
																																									WS_OVERLAPPEDWINDOW,
																																									CW_USEDEFAULT,
																																									CW_USEDEFAULT,
																																									CW_USEDEFAULT,
																																									CW_USEDEFAULT,
																																									NULL,
																																									NULL,
																																									hInstance,
																																									NULL);
	
							//ウィンドウを表示
							ShowWindow(hWnd, nCmdShow);
							UpdateWindow(hWnd);	


							while(GetMessage(&msg, NULL, 0, 0))
							{
															TranslateMessage(&msg);
															DispatchMessage(&msg);
							}


							return msg.wParam;

}

とりあえずここまで。長いですが。
ウィンドウズプログラムは、main関数からでなく、WinMain関数から始まります。
						WNDCLASSEX wcex;									//ウィンドウクラス構造体
						HWND hWnd;																				//ウィンドウハンドル
						MSG msg;																								//メッセージ構造体

ウィンドウズクラス構造体と言うのは、ウィンドウの形、大きさ等をきめるものです。

ウィンドウハンドルは、アプリケーションが起動する時、Windowsが勝手に決めちゃう値。
つまり、その値によって、どんなAPIをどのウィンドウに使うかを区別するための物です。

メッセージ構造体は、メッセージの情報を格納するためのもの。
メッセージと言うのは、「マウスが押された」とか、「スペースキーが押された」とか、
それによってアプリケーションになんらかの操作を起こすもので、
Windowsは常にそのメッセージをチェックして、あれば、アプリケーションに「お知らせ」します。
						//ウィンドウクラスの設定
						//要は、どんな感じのウィンドウを作るのかを記述している。
						wcex.cbSize = sizeof(WNDCLASSEX);
						wcex.style = CS_HREDRAW | CS_VREDRAW;
						wcex.lpfnWndProc = (WNDPROC)WndProc;
						wcex.cbClsExtra = 0; 
						wcex.cbWndExtra = 0;
						wcex.hInstance = hInstance;
						wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
						wcex.hCursor = LoadIcon(NULL, IDC_ARROW);
						wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
						wcex.lpszMenuName = NULL;
      wcex.lpszClassName = "Sample";
						wcex.hIconsm = LoadIcon(NULL, IDI_APPLICATION);

						//ウィンドウクラスの登録
						RegisterClassEx(&wcex);

ウィンドウクラス構造体wcexのそれぞれのメンバーに値を代入しています。

cbSizeは構造体のサイズを設定します。

styleはウィンドウのスタイルを設定します。
CS_HREDRAW | CS_VREDRAW は、ウィンドウが横方向または縦方向にサイズ変更された時に再描画する事をあらわします。

lpfnWndProcはウィンドウプロシージャのアドレスを設定します。
ウィンドウプロシージャーっていうのは、さっき話した 「Windowsからくるお知らせ」を
アプリケーションが受ける関数です。これで入ってきたメッセージを処理します。

cbClsExtraとcbWndExtraはクラスに補助領域を持たせる場合の補助領域のサイズを表します。
ちなみにこの補助領域はめったに使われません。

hInstanceはインスタンスハンドルを設定します。
インスタンスは、メモリ上にロードされたプログラムの実体のこと。
Windowsはハンドルという32ビットの整数値で、
インスタンスを管理しているのです。それでWindowsは複数の同じプログラムを実行できるのです。

hIconはプログラムのアイコンを設定します。ウィンドウの左上にでるアレです。
LoadIcon関数で、アイコンをロードしています。

hbrBackgroundはバックグラウンドカラー(背景色) を設定します。
COLOR_WINDOW + 1は、白色を表します。

lpszMenuNameはメニューの情報を設定します。
いまはメニューを作るのは飛ばします。のでNULLを設定。

lpszClassNameはウィンドウクラスの名前を設定します。
これは自由に設定していいものです。

hIconSmは小さいアイコンを設定します。

RegisterClassEx(&wcex) というAPIでこれまで頑張って設定してきたウィンドウクラス構造体を
一括して登録します。


						//ウィンドウの作成
						hWnd = CreateWindow(wcex.lpszClassName,													//ウィンドウクラス名
																																									"ひな形",																																		//タイトル(任意)
																																									WS_OVERLAPPEDWINDOW,						//ウィンドウの形
																																									CW_USEDEFAULT,																			//水平位置
																																									CW_USEDEFAULT,																			//垂直位置
																																									CW_USEDEFAULT,																			//幅
																																									CW_USEDEFAULT,																			//高さ
																																									NULL,																																						//親ウィンドウ
																																									NULL,																																					//ウィンドウメニュー
																																									hInstance,																														//インスタンスハンドル
																																									NULL);																																				//補足情報
	
							//ウィンドウを表示
							ShowWindow(hWnd, nCmdShow);
							UpdateWindow(hWnd);	

ウィンドウの作成、表示を行っています。
CreateWindowはウィンドウを作成すAPIです。
やたらとパラメータ多いですが、コメントつけたので大体分かると思います。
親ウィンドウと言うのはあとでやるのでいまは分からなくていいです。
補足情報も後々解説する事にします。

ShowWindowはウィンドウを表示するAPIです。
nCmdShowはどんな状態でウィンドウの表示するかを設定するものです。
							while(GetMessage(&msg, NULL, 0, 0))
							{
															TranslateMessage(&msg);
															DispatchMessage(&msg);
							}

これはメッセージループです。
メッセージループは、前に話したメッセージの有無を確認するための物です。
GetMessageは、第1引数のmsg構造体へ、メッセージがあれば代入します。
TranslateMessageではメッセージキュー(溜まり場)にGetMessageで取ったメッセージをポスト(送り)ます。
DispatchMessageはウィンドウプロシージャへメッセージを渡します。


LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				switch (message) 
				{
									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

これはコールバック関数と呼ばれ、Windowsから勝手にプログラムに送られる関数です。
UINT messageはメッセージの種類が格納されます。
hWndはどのウィンドウにメッセージを流すのか、
wParamとlParamはメッセージを送る時の補足情報を表します。

WM_DESTROYはウィンドウが消された時にWindowsがアプリケーションにお知らせするもの。
ウィンドウが消される、すなわちアプリケーションの終了ですね。
PostQuitMessage(0) というのは、プログラムを終了します。

DefWindowProc(hWnd, message, wParam, lParam) は、プログラムで処理しないメッセージを渡します。
ウィンドウの移動、最大化等は勝手にこの間数でやってくれるので、
非常に便利です。つまり普通のアプリケーションでのウィンドウ操作等は、このAPIにおまかせできる訳です。

さて、WndProcというコールバック関数を追加したため、プロトタイプ宣言をします。
LRESLT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


とりあえず今回は、ウィンドウズプログラムの基本的な流れと、
ウィンドウの作り方、メッセージの流れかたを紹介しました。
かなり長かったと思います。ウィンドウを表示させるだけでもこんなに大変なのです。
次回は、ダイアログボックスの作り方、どのボタンが押されたかの判定を作ります。
これは今回にくらべて楽なはず。


■今回出て来た型、API、用語

用語、型、構造体
API(Apprlication Programming Interface)   システムが提供する関数群。
普通のアプリケーションの持つ標準の機能をまとめたもの。

WNDCLASSEX(ウィンドウクラス構造体)   ウィンドウの形、大きさ等をきめるもの

HWND(ウィンドウハンドル)   アプリケーションが起動した時、Windowsにより決められる値。
この値によりウィンドウを選別する。

MSG(メッセージ構造体)   メッセージの種類を格納するためのもの。コールバック関数で使われる。

HINSTANCE(インスタンスハンドル)   メモリ上にロードされたプログラムの実体を管理するハンドル


API
ATOM RegisterClassEx()

引数 ..
CONST WNDCLASS *lpwcx

ウィンドウクラス構造体を設定する。
lpwcx - ウィンドウクラス構造体へのポインタ


HWND CreateWindow()

引数 ...
LPCTSTR lpClassName ,
LPCTSTR lpWindowName,
DWORD dwStyle ,
int x ,
int y ,
int nWidth ,
int nHeight ,
HWND hWndParent ,
HMENU hMenu ,
HANDLE hInstance ,
LPVOID lpParam

ウィンドウを内部的に作成する。
lpClassName - ウィンドウクラス名
lpWindowName - ウィンドウ名(任意)
dwStyle - ウィンドウのスタイル
x - ウィンドウのX座標
y - ウィンドウのY座標
nWidth - ウィンドウの横幅
nHeight - ウィンドウの縦幅
hWndParend - 親ウィンドウ
hMenu - メニュー
hInstance - インスタンスハンドル
lpParam - WM_CREATE メッセージの lParam パラメータとして渡される構造体ポインタ

第4〜第7引数までは、CW_USEDEFAULT でWindowsに設定をおまかせできる。

第3引数は、タイトルバー、枠などを持つ WS_OVERLAPPEDWINDOW を設定するのが普通。


BOOL ShowWindow( );

引数 ...
HWND hWnd ,
nCmdShow

CreateWindow()で作成したウィンドウを表示する。
hWnd - 対象のウィンドウハンドル
nCmdShow - ウィンドウの表示状態を表す定数


VOID PostQuitMessage()

引数 ...
int nExitCode

WM_QUIT メッセージをポスト。
nExitCode - WM_QUIT の wParam の値


LRESULT DefWindowProc()

引数 ...
HWND hWnd
UINT Msg
WPARAM wParam
LPARAM lParam

デフォルトウィンドウプロシージャを呼び出す。ウィンドウの基本的な操作をWindowsが代行してくれる。
hWnd - メッセージを受け取ったウィンドウハンドル
Msg - メッセージ
wParam - メッセージの追加情報1
lParam - メッセージの追加情報2


BOOL GetMessage()

引数 ...
LPMSG lpMsg
HWND hWnd
UINT wMsgFilterMin
UINT wMsgFilterMax

指定された MSG 構造体にシステムから受け取ったメッセージを格納する。
lpMsg - メッセージを格納する、MSG 構造体へのポインタ
hWnd - メッセージを取得するウィンドウハンドルを指定
wMsgFilterMin - メッセージの最小値を指定
wMsgFilterMax - メッセージの最大値を指定

戻り値 - WM_QUIT を受け取った時は 0、そうでないなら 0 以外、エラーの時は -1


BOOL TranslateMessage()

引数 ...
CONST MSG *lpmsg

指定するメッセージをメッセージキューに送る。
lpMsg - メッセージを格納する、MSG 構造体へのポインタ


BOOL DispatchMessage()

引数 ...
CONST MSG *lpmsg

指定するメッセージをウィンドウプロシージャに送る。
lpMsg - メッセージを格納する、MSG 構造体へのポインタ

戻り値 - ウィンドウプロシージャの戻り値


2.ダイアログボックス


■OKボタンのダイアログボックス

ダイアログボックスって何か分かりますか?
そう。ちっちゃいウィンドウでボタンがいくつかあって....ってやつです。
今回は、それを表示するAPIを解説します。
MessageBox(オーナーウィンドウのハンドル, "表示文字列", "タイトル", ダイアログの種類)

オーナーウィンドウのハンドルは、ここではNULLを入れて下さい。
"表示文字列"はダイアログの中にでる文字列です。
"タイトル"はウィンドウのキャプションバーにでる文字列です。
ダイアログの種類は解答に用いられるボタンを設定します。
これは幾つか定数で定められています。

では第1回で作った「ひな形」のウィンドウプロシージャを少し改造して、
画面内(正確にはクライアント領域 後でやるから今は分からなくていいです)をクリックした時に、ダイアログボックスを表示するように書き換えてみよう。

LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				
				switch (message) 
				{
									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

								case WM_RBUTTONUP:
														//右ボタンが押されて離された
														MessageBox(NULL, "右ボタンを押しましたね!?", "ボタンチェッカー", MB_OK);
														return 0;

							case WM_LBUTTONUP:
														//左ボタンが押されて離された
														MessageBox(NULL, "左ボタンを押しましたね!?", "分かっちゃうんだなこれが", MB_OK);
														return 0;

									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

WM_RBUTTONUPは右ボタンが押されて離された時、反対にWM_LBUTTONUPは左ボタンが押されて離された時。
ちなみにWM_RBUTTONDOWNだと...右ボタンが押された瞬間に送られます。

MessageBox(NULL, "右ボタンを押しましたね!?", "ボタンチェッカー", MB_OK);
ウィンドウのキャプションバーに"ボタンチェッカー"、
中身はに"右ボタンを押しましたね!?"。
MB_OKはOKボタンだけのダイアログボックスを示します。

コンパイルし、実行してみて下さい。ちゃんとできましたか?


■はい/いいえボタンのダイアログボックス

OKボタンを表示させるだけじゃつまらない。
はいといいえの選択肢を持つダイアログを作成してみよう。
はい/いいえボタンを表示するには、MB_OKの所を、MB_YESNOに変えます。
YESNOだからはい/いいえ...分かりやすいw

では、アプリケーションを終了させる時に、本当に終了させるか尋ねるプログラムを作ります。

LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				int theresult;

				switch (message) 
				{
									case WM_DESTROY:
														//ウィンドウが消された時

														//ホントに終わる?
														theresult = MessageBox(NULL, "本当に終わりますか?", "質問", MB_YESNO);
														if (theresult == IDYES){
																		PostQuitMessage(0);
														}

														return 0;
		
									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

MessageBox関数は、押されたボタンが返ってきます。
といっても、直接ボタン名が返ってくる訳ではなく、返値は整数です。
でも押されたボタンによって返値は決まっているので、全部定数化されています。

IDYESは、「はい」ボタンが押された事、IDNOは、「いいえ」ボタンが押された事を意味します。

さて次回は、文字列や線分をウィンドウに直接描画する方法を解説します。


■今回出て来た型、API、用語

MessageBox()

引数 ...
HWND hWnd
LPCTSTR lpText
LPCTSTR lpCaption
UINT uType

ダイアログを表示。
hWnd - メッセージを格納する、MSG 構造体へのポインタ
lpText - ダイアログのメッセージ
lpCaption - ダイアログのタイトル
uType - ダイアログボタンの種類 [OKのみ] MB_OK  [はい/いいえ] MB_YESNO

戻り値 - 「はい」....IDYES、「いいえ」.....IDNO


3.図形を描こう


こんにちは。今回は早速図形をいろいろ描いてみます。
まずこの前ちょっと触れた「クライアント領域」がどうのこうのという話をします。
「クライアント領域」というのは、アプリケーションが自由に使える領域です。
「非クライアント領域」というのは、描画されるものが決まっていて、アプリケーションが自由に使えない領域です。
具体的に言うと「非クライアント領域」は、ウィンドウの枠やキャプションバー等、
「クライアント領域」はその真ん中にある領域です。
という訳で図形はクライアント領域に描画します。

LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				HDC hDC;
				PAINTSTRUCT ps;

				switch (message) 
				{
									case WM_PAINT:
														//再描画が行われた時
														hDC = BeginPaint(hWnd, &ps);

														//文字列描画
														TextOut(hDC,150,180,"文字列",6);

														//始点移動
														MoveToEx(hDC , 10 , 10 , NULL);
														//線分描画
														LineTo(hDC , 80, 80);

														//文字色の設定
														SetTextColor(hdc , RGB(255 , 0 , 0));
													//文字列描画
														TextOut(hDC,50,80,"カラーだ",8);

													EndPaint(hWnd , &ps);

									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

	
									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

WM_PAINTは再描画が行われた時に発生します。
例えば、ウィンドウが他のウィンドウに隠れて、他のウィンドウが閉じられて見えるようになった時など。
一回ウィンドウに隠れてしまうと、その部分のグラフィックは消えてしまうので。
次に、アプリケーションはその部分を有効な状態にしなければいけません。
そのためには、背景を背景色で塗りつぶす必要があります。
しかも、全体を描くのではなく無効な領域だけを再描画するのです。
この作業の多くも、APIが行ってくれます。そのためにウィンドウごとの情報を Windows は構造体で扱っていて、
その構造体がPAINTSTRUCT構造体です。
PAINTSTRUCT ps;

これは文字列を描画していますが、文字列もWindowsではグラフィックです。
クライアント領域にグラフィックを描画するには、 GDI を用います。
GDI とは Graphics Device Interface のことで、グラフィック関連関数群のことです。
グラフィックス関連の処理を行うためには、まずデバイスコンテキストを得ます。
これは、GDI が管理する構造体で、ウィンドウ表示に関係します。
GDIを利用するには、まずデバイスコンテキストのハンドルを取得します。
HDC hDC;
hDC = BeginPaint(hWnd, &ps);
	

HDC BeginPaint(HWND hwnd , LPPAINTSTRUCT lpPaint);
デバイスコンテキストへのハンドルを取得します。
PAINTSTRUCT へのポインタを受け取り、それをを初期化し無効領域を再び描きなおしてクライアント領域を有効にします。
hwnd には再描画するウィンドウのハンドルを、lpPaint には、PAINTSTRUCT へのポインタを指定します。
(LPPAINTSTRUCT 型は、PAINTSTRUCTのポインタ型です。)

また、描画が終わる時に、デバイスコンテキストを解放しなければなりません。それがこの関数。
BOOL EndPaint(HWND hWnd , CONST PAINTSTRUCT *lpPaint);
hWnd は、再描画したウィンドウのハンドルを、
lpPaint は、BeginPaint() で取得したPAINTSTRUCTへのポインタを指定します。

次に、肝心な描画関数。
BOOL TextOut( HDC hdc,int nXStart, int nYStart,LPCTSTR lpString,int cbString);
hdcは、デバイスコンテキストへのハンドル。
nXStartは開始x座標、nYStartは開始y座標、lpStringは表示したい文字列、
cbStringは文字数。
文字数は、バイトで数えるので、全角3文字なので6です。

次に線分を描きます。まず描画の始点(カレントポジション とか言ったりします)を得る関数。
MoveToEx(HDC hdc , int X , int Y , LPPOINT lpPoint);
hdcには、デバイスコンテキストのハンドルを、XとYには、始点のX座標とY座標を指定します。
lpPointは、以前設定されていた描画の始点が格納されます。
以前の始点を得る必要がないならば、NULLを指定します。

次に線分を描画する関数です。
BOOL LineTo(HDC hdc , int nXEnd , int nYEnd);
hdcは、デバイスコンテキストのハンドルです。
nXEndは、終点のX座標、nYEndは終点のY座標を指定します。
さっき MoveToExで設定した描画の始点から、終点まで線分を描画します。
さらに、終点の座標を、次の描画の始点に設定します。

次に文字色を設定する関数。
COLORREF SetTextColor(HDC hdc , COLORREF crColor);
hdc は、デバイスコンテキストのハンドルを、crColor は、文字色を表す32ビット整数を指定します。
crColor は、普通下のRGBマクロを使います。
COLORREF RGB(BYTE bRed , BYTE bGreen , BYTE bBlue);
bRedが赤、bGreenが緑、bBlueが青を表します。
RGB(200 , 0 , 0); のように0〜255を整数で表します。

次回は円と四角形、弧の描きかたです。


■今回出て来た型、API、用語

用語、型、構造体
GDI(Graphics Device Interface)   システムが提供する描画関数群。 使うにはデバイスコンテキストを得る必要がある。

デバイスコンテキスト   GDI が管理するウィンドウ表示に関する構造体。
BeginPaint()で取得し、EndPaint()で解放する。

PAINTSTRUCT構造体   Windowsがウィンドウごとに管理している構造体。
デバイスコンテキストを得るのに必要。


API


HDC BeginPaint()

引数 ...
HWND hwnd
LPPAINTSTRUCT lpPaint

デバイスコンテキストへのハンドルを取得する。
hWnd - ウィンドウのハンドル
lpPaint - PAINTSTRUCT構造体へのポインタ


BOOL EndPaint()

引数 ...
HWND hwnd
CONST PAINTSTRUCT *lpPaint

デバイスコンテキストへのハンドルを解放する。描画処理は上のBeginPaintと挟んで行う。
hWnd - ウィンドウのハンドル
lpPaint - PAINTSTRUCT構造体へのポインタ


BOOL TextOut()

引数 ...
HDC hdc
int nXStart
int nYStart
LPCTSTR lpString
int cbString

画面に文字列を描画する。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
nXStart - 描画開始地点のX座標
nYStart - 描画開始地点のY座標
lpString - 描画する文字列
cbString - 文字列のバイト長


BOOL MoveToEx()

引数 ...
HDC hdc
int X
int Y
LPPOINT lpPoint

画面に文字列を描画する。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
nXStart - 描画開始地点のX座標
nYStart - 描画開始地点のY座標
lpPoint - 以前設定されていたカレントポジション


BOOL LineTo()

引数 ...
HDC hdc
int nXEnd
int nYEnd

MoveToExで設定した描画開始地点から線を引く。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
nXStart - 描画終了地点のX座標
nYStart - 描画終了地点のY座標


COLORREF SetTextColor()

引数 ...
HDC hdc
COLORREF crColor

文字色を設定する。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
crColor - 文字色を表す32ビット整数。RGBマクロを用いるのが普通。


COLORREF RGB()

引数 ...
BYTE bRed
BYTE bGreen
BYTE bBlue

32ビット整数をRGB形式で設定できる。
bRed - 赤
bGreen - 緑
bBlue - 青


4.図形を描こう(その2)


■四角形


さて、今回もウィンドウに図形を描いてみます。
今回は四角形、円、弧の描画です。
今回は前回めんどくさい説明(デバイスコンテキストがあーだこーだとか...)は無いので、
関数の説明だけになるでしょう。

まずは、四角形と角丸四角形の描画です。
LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				HDC hDC;
				PAINTSTRUCT ps;

				switch (message) 
				{
									case WM_PAINT:
														//再描画が行われた時

														hDC = BeginPaint(hWnd, &ps);

														//長方形描画
														Rectangle(hdc , 70 , 50 , 200 , 180);

														//角丸四角形
														RoundRect(hdc , 90 , 70 , 220 , 200 , 10 , 10);

														EndPaint(hWnd , &ps);

									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

	
									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

長方形を描画する関数。
BOOL Rectangle(HDC hdc , int nLeftRect , int nTopRect ,int nRightRect , int nBottomRect);
hdc は、デバイスコンテキストのハンドルを
nLeftRect は、長方形の左
nTopRect は、長方形の上
nRightRect は、長方形の右
nBottomRect は、長方形の下
注意したいのは、MoveToEx などと描画開始地点を設定しなくてもいい事です。まぁ当たり前ですが。

角丸長方形を描画する関数。
BOOL RoundRect(HDC hdc , int nLeftRect , int nTopRect ,int nRightRect , int nBottomRect, int nWidth , int nHeight);
hdc は、デバイスコンテキストのハンドルを
nLeftRect は、長方形の左
nTopRect は、長方形の上
nRightRect は、長方形の右
nBottomRect は、長方形の下
nWidth は、丸い部分の大きさ(横)
nHeight は、丸い部分の大きさ(縦)

これも簡単ですね。RectAngle に引数が2つくっついただけです。



次は、円と弧、扇形の描画です。

■円、弧、扇形

LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				HDC hDC;
				PAINTSTRUCT ps;

				switch (message) 
				{
									case WM_PAINT:
														//再描画が行われた時

														hDC = BeginPaint(hWnd, &ps);

														//円の描画
														Ellipse(hdc , 30 , 30 , 90 , 90);

														//弧の描画
														Arc(hdc , 50 , 50 , 200 , 200 , 0 , 100 , 110 , 0);

														//扇形の描画
														Pie(hdc , 210 , 10 , 400 , 200 , 0 , 100 , 310 , 0);

														EndPaint(hWnd , &ps);

									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

	
									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

円を描画する関数。
BOOL Ellipse(HDC hdc , int nLeftRect , int nTopRect ,int nRightRect , int nBottomRect);
hdc は、デバイスコンテキストのハンドル
nLeftRect は、長方形の左
nTopRect は、長方形の上
nRightRect は、長方形の右
nBottomRect は、長方形の下
これは簡単。長方形と引き数は同じで、長方形に内接する円が描かれます。

弧を描画する関数。
BOOL Arc(HDC hdc , int nLeftRect , int nTopRect ,int nRightRect , int nBottomRect, int nXStartArc , int nYStartArc ,int nXEndArc , int nYEndArc);
hdc は、デバイスコンテキストのハンドル
nLeftRect は、長方形の左
nTopRect は、長方形の上
nRightRect は、長方形の右
nBottomRect は、長方形の下
nXStartArc と nYStartArc は、始点座標を示す座標。
nXEndArc と nYEndArc は、終点座標を示す座標。
これは少しややこしいですね。図で説明します。
解説
こんな感じで図形が描画されます。
最後の4つの引き数で、弧の描画開始地点を求めているのですが、
弧の始点座標は、弧の中心座標と、4つの引き数で指定した座標の線分と、弧の交点となります。
あと一つ注意したい事。『反時計回り』に描画されるので注意して下さい。

扇形を描く「Pie」も引き数は同じです。ただ円の中心と始点座標、終点座標の線分が描かれるだけです。



いよいよ最後。ペン、ブラシ設定の説明をして今回は終わりです。

■ペンとブラシ

LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				HDC hdc;
				PAINTSTRUCT ps;
				LOGBRUSH theBrush;
				switch (message) 
				{
									case WM_PAINT:
														//再描画が行われた時

														hdc = BeginPaint(hWnd, &ps);

													//ペン作成、設定
             SelectObject(hdc , CreatePen(PS_DASH , 0 , RGB(0 , 0 , 255)));
														
													//ブラシ情報設定
													theBrush.lbStyle = BS_SOLID;
													theBrush.lbColor = RGB(255 , 0 , 0);
													theBrush.lbHatch = NULL;

													//ブラシ作成、設定
													SelectObject(hdc , CreateBrushIndirect(*theBrush));

														//長方形描画
														Rectangle(hdc , 70 , 50 , 200 , 180);

														EndPaint(hWnd , &ps);
			
														DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));
														DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));
									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

	
									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

ペンやブラシを作ったら、まず、「現在使用するものですよ」、と設定しなければなりません。
その設定されているペンやブラシを、「カレントペン」、「カレントブラシ」とか言ったりします。

ペンやブラシを設定する関数。
HGDIOBJ SelectObject(HDC hdc , HGDIOBJ hgdiobj);
hdc は、デバイスコンテキストのハンドル
hgdiobj は 設定するオブジェクト。

設定するオブジェクトは後術する作る関数で設定します。

ペンを作成する関数。
HPEN CreatePen(int fnPenStyle , int nWidth , COLORREF crColor);
fnPenStyle は、ペンのスタイル。
nWidth は ペンの太さ。
crColor は、ペンのカラー。

ブラシを作成する関数。
HBRUSH CreateBrushIndirect(CONST LOGBRUSH *lplb);
lplb は、ブラシを表すLOGBRUSH構造体型へのポインタ

ブラシを表すLOGBRUSH構造体。

typedef struct tagLOGBRUSH {
   UINT     lbStyle; 
   COLORREF lbColor; 
   LONG     lbHatch; 
} LOGBRUSH;

lbStyle は、ブラシのスタイル。
lbColor は、ブラシのカラー。
lbHatch は、ハッチブラシの設定。

スタイルは、BS_SOLIDを設定すると、lbColorで塗りつぶす普通のブラシになりますが、
BS_HATCHEDを設定すると、lbHatchの設定が適用されてハッチブラシになります。
lbHatchはハッチの様々なスタイルが定数で定義されています。
あとの例のリファレンスに詳しく書いておきますので、試して遊んでみて下さい。

また、ペンやブラシは、ペン型とブラシ型の変数に格納して、保存する事が出来ます。
ペン型は HPEN   ブラシ型は HBRUSH  です。

HPEN thePen;
thePen = CreatePen(PS_DASH , 0 , RGB(0 , 0 , 255));
SelectObject(hdc , thePen);

のように一回設定してからカレントブラシに登録する事も出来ます。ブラシも同じです。

さて、一つ大事な事。一回作ったブラシやペンは描画が終わったら消去しなければなりません。
BOOL DeleteObject(HGDIOBJ hObject);
hObject は、 消去するブラシやペン。

消去するには、SelectObjectでブラシやペンを再設定します。
また、ブラシやペンを変数に格納する場合は、

HPEN thePen;
thePen = CreatePen(PS_DASH , 0 , RGB(0 , 0 , 255));
SelectObject(hdc , thePen);
//もろもろ描画
DeleteObject(thePen);

のように、ブラシやペンの変数を渡してやればOKです。

さて、今回で描画関係の関数はおしまいです。
結構盛り沢山でした。

■今回出て来た型、API、用語

用語、型、構造体
HPEN   ペンの変数。CreatePenの返値で代入させる事が多い。

HBRUSH   ブラシの変数。CreateBrushIndirectの返値で代入させる事が多い。

LOGBRUSH構造体   ブラシを表す構造体。
UINT lbStyle
COLORREF lbColor
LONG lbHatch


lbStyle の定数
BS_SOLID		普通
BS_HATCHED		ハッチブラシ

lbHatch の定数
HS_BDIAGONAL		45 度の右下がり
HS_CROSS		十字
HS_DIAGCROSS		斜め十字
HS_FDIAGONAL		45 度の右上がり
HS_HORIZONTAL		水平
HS_VERTICAL		垂直

API


BOOL Rectangle()

引数 ...
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect

長方形を描画する。
hdc - デバイスコンテキストのハンドル
nLeftRect - 長方形の左
nTopRect - 長方形の上
nRightRect - 長方形の右
nBottomRect - 長方形の下


BOOL RoundRect()

引数 ...
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect
int nWidth
int nHeight

角丸長方形を描画する。
hdc - デバイスコンテキストのハンドル
nLeftRect - 長方形の左
nTopRect - 長方形の上
nRightRect - 長方形の右
nBottomRect - 長方形の下
nWidth - 丸い部分の横幅
nHeight - 丸い部分の縦幅


BOOL Ellipse()

引数 ...
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect

指定された長方形に内接する円を描画する。
hdc - デバイスコンテキストのハンドル
nLeftRect - 外接する長方形の左
nTopRect - 外接する長方形の上
nRightRect - 外接する長方形の右
nBottomRect - 外接する長方形の下


BOOL Arc()

引数 ...
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect
int nXStartArc
int nYStartArc
int nXEndArc
int nYEndArc

弧を描画する。
hdc - デバイスコンテキストのハンドル
nLeftRect - 外接する長方形の左
nTopRect - 外接する長方形の上
nRightRect - 外接する長方形の右
nBottomRect - 外接する長方形の下
nXStartArc、nYStartArc - 描画開始地点の設定
nXEndArc、nYEndArc - 描画終了地点の設定


HGDIOBJ SelectObject()

引数 ...
HDC hdc
HGDIOBJ hgdiobj

オブジェクトを設定する。
hdc - デバイスコンテキストへのハンドル
hgdiobj - 設定するオブジェクト


CreatePen(int fnPenStyle , int nWidth , COLORREF crColor); COLORREF CreatePen()

引数 ...
int fnPenStyle
int nWidth
COLORREF crColor

ペンを作成する。メッセージが終わったら消去しなければいけない。
fnPenStyle - ペンのスタイル
nWidth - ペンの太さ
crColor - ペンの色を表す32ビット整数。RGBマクロを用いるのが普通。


fnPenStyle(ペンスタイル) の定数
PS_SOLID		実線のペン
PS_DASH		破線のペン
PS_DOT		点線のペン
PS_DASHDOT 	破線と一点のペン
PS_DASHDOTDOT		破線と二点のペン

HBRUSH CreateBrushIndirect()

引数 ...
CONST LOGBRUSH *lplb

ブラシを作成する。メッセージが終わったら消去しなければいけない。
lplb - ブラシを表すLOGBRUSH構造体型へのポインタ


BOOL DeleteObject()

引数 ...
HGDIOBJ hObject

ペンやブラシを消去し、それに関するシステムリソースを解放する。
hObject - ペンやブラシを表すハンドル。(HPEN型やHBRUSH型)


5.マウスイベント一挙公開


■クリックすると発するメッセージ

さて、描画関係の話は置いといて、マウス操作に関するメッセージをみていきましょう。
まずは、基本である「クリック」されると発生するメッセージ。
左
押した WM_LBUTTONDOWN	
離した WM_LBUTTONUP

右
押した WM_RBUTTONDOWN	
離した WM_RBUTTONUP

これはメッセージ表示の時に少しやったと思います。
WM_LBUTTONDOWN は押した瞬間に、WM_LBUTTONUPは押されたボタンが離された瞬間に送られます。

さて、これらのメッセージが発生した時、やっとウィンドウプロシージャの第3引数wParamが効果を発揮します。
wParamには、マウスのボタン状態、Shiftキー、Ctrlキーの状態が定数で格納されています。
こんな感じで。

MK_LBUTTON		左ボタンが押されている 
MK_RBUTTON		右ボタンが押されている 
MK_SHIFT		Shift キーが押されている 
MK_CONTROL		Ctrl キーが押されている 

これがあれば、WM_LBUTTONDOWNが発生した時、MK_RBUTTONで右ボタンの状態も調べられます。
逆も然りです。

また、ウィンドウプロシージャの第4引数lParamにもマウスの座標が入っています。
上位16ビットにy座標、下位16ビットにx座標が代入されます。
しかしこのままではx座標とy座標がまとまっていて個別に取り出す事が出来ません。そこで、便利なものがあります。
HIWORD()とLOWORD()です。HIWORDは上位16ビット、LOWORDは下位16ビットを取り出す事が出来ます。
こんな感じで。

x = LOWORD(lParam);
y = HIWORD(lParam); 

HIWORD()とLOWORD()です。HIWORDは上位16ビット、LOWORDは下位16ビットを取り出す事が出来ます。

マウス操作には他にもいろいろなものがあります。
マウスが移動したら発生する「WM_MOUSEMOVE」

左ボタンのダブルクリック:WM_LBUTTONDBLCLK
右ボタンのダブルクリック:WM_RBUTTONDBLCLK
ダブルクリックのメッセージは、実はこれだけでは駄目で、ウィンドウクラス構造体のstyleに、「CS_DBLCLKS」を加える必要があります。

WNDCLASSEX wcex;									//ウィンドウクラス構造体
wcex.style		= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

さて、ずらーっと紹介してきましたが、これだけではつまらないので、
何か作りましょう。ダブルクリックするとマウス座標がメッセージボックスに...つまらないか。
でもマウスメッセージの使い方の例なので、参考にして下さい。
LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				int x = 0 , y = 0;
				switch (message) 
				{
									case WM_LBUTTONDBLCLK:
														//左ボタンがダブルクリックされた時

														x = LOWORD(lParam);
														y = HIWORD(lParam);

														MessageBox(NULL, x , "マウスのX座標", MB_OK);
														MessageBox(NULL, y , "マウスのY座標", MB_OK);

									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

	
									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

次回はキーボードに関するメッセージです。


6.キーイベント一挙公開


■キーが押されたとき


前回はマウスをやったので、今回はキーです。
難しそうに聞こえるかも知れませんが、単純です。
キーが押された瞬間:WM_KEYDOWN
キーが離された瞬間:WM_KEYUP

これだけです。「これじゃ何のキーが押されたか分からないじゃないか!!」
...そうです。このメッセージは「何のキーが押されたか」というのは関係なく、
なんの文字が入力されたかというのは関係ないのです。

そこで、「文字入力」を表すメッセージがあります。

文字入力でキー離された瞬間:WM_CHAR

なぜ分かるかというと、第3引数のwParamに文字コードが代入されているからです。
これで、キー入力がされた時に、どのキーが押されたのか分かります。

また、逆の『どこどこのキーが押されたか否か』を返すAPIがあります。
GetKeyState()というAPIです。使用例は、

if (GetKeyState(VK_SHIFT))				//シフトキーが押されたか
if (GetKeyState("a"))				//aキーが押されたか

特殊キーの場合は、VK_どこどこ、と書きます(どこどこ..に特殊なやつがあるので、最後に詳しく書きます)。
a〜z、テンキーの場合は、そのままASCIIコードを書けばOKです。

さて、前回のサンプルプログラムはつまらなかったので、
今回はキーが押されたらランダムな場所にその文字を描画する、という仕様にしましょう。
LRESULT CALLBACK WndProc(HWND hWnd, 
																																			           UINT message, 
																																			           WPARAM wParam,
																																			           LPARAM lParam)
{
				HDC hdc;
				PAINTSTRUCT ps;
				int x = 0 , y = 0;
				switch (message) 
				{
									case WM_CHAR:
														//文字入力がされた時

													hdc = GetDC(hwnd);
													TextOut(hdc , rand()%100 , rand()%100 , &wp , 1);
													ReleaseDC(hwnd , hdc);

									case WM_DESTROY:
														//ウィンドウが消された時
														PostQuitMessage(0);
														return 0;

	
									default:
														//デフォルトの処理
														return DefWindowProc(hWnd, message, wParam, lParam);

				}
}	

GetDC(hwnd); という見なれない関数が出てきました。
これはデバイスコンテキストのハンドルを取得するための物です。あれ、どっかで聞いた事あるような...
そうです、描画処理の時にいやという程出てきた BeginPaint(hWnd, &ps); と同機能の物です。
ただ、再描画が行われる時以外は(つまりWM_PAINT 以外では) GetDC を使います。

ReleaseDC(hwnd , hdc) も EndPaint と同機能のもの。
これで挟めば立派に描画処理の役目をはたします。
ただし、PAINTSTRUCT構造体へのポインタは渡す必要はありません。


間違い等ありましたら御指摘をお願いします。