■主旨
みなさん、こんにちは。この講座はウィンドウズプログラムを作る講座です。
ウィンドウズプログラムというのは、今まで作ってきた黒い画面に白い文字のやつ(コンソールアプリケーションといいます)じゃなくて、
アプリケーション(メモ帳とか、ウィンドウがでてメニューが出るの)を作るためのプログラムです。
つまり、「ちゃんとした動くアプリケーションを作ろう」というのがこの講座の主旨です。
ですが、ウィンドウズプログラムはC言語で書く訳なので...
最低でもC言語「基礎編」の内容を理解してからの方がいいと思います。
まぁ、ある程度分かっていれば大丈夫でしょう。
■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;
}
|
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 - ウィンドウクラス構造体へのポインタ
引数 ...
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 を設定するのが普通。
引数 ...
HWND hWnd ,
nCmdShow
CreateWindow()で作成したウィンドウを表示する。
hWnd - 対象のウィンドウハンドル
nCmdShow - ウィンドウの表示状態を表す定数
引数 ...
int nExitCode
WM_QUIT メッセージをポスト。
nExitCode - WM_QUIT の wParam の値
引数 ...
HWND hWnd
UINT Msg
WPARAM wParam
LPARAM lParam
デフォルトウィンドウプロシージャを呼び出す。ウィンドウの基本的な操作をWindowsが代行してくれる。
hWnd - メッセージを受け取ったウィンドウハンドル
Msg - メッセージ
wParam - メッセージの追加情報1
lParam - メッセージの追加情報2
引数 ...
LPMSG lpMsg
HWND hWnd
UINT wMsgFilterMin
UINT wMsgFilterMax
指定された MSG 構造体にシステムから受け取ったメッセージを格納する。
lpMsg - メッセージを格納する、MSG 構造体へのポインタ
hWnd - メッセージを取得するウィンドウハンドルを指定
wMsgFilterMin - メッセージの最小値を指定
wMsgFilterMax - メッセージの最大値を指定
戻り値 - WM_QUIT を受け取った時は 0、そうでないなら 0 以外、エラーの時は -1
引数 ...
CONST MSG *lpmsg
指定するメッセージをメッセージキューに送る。
lpMsg - メッセージを格納する、MSG 構造体へのポインタ
引数 ...
CONST MSG *lpmsg
指定するメッセージをウィンドウプロシージャに送る。
lpMsg - メッセージを格納する、MSG 構造体へのポインタ
戻り値 - ウィンドウプロシージャの戻り値
■OKボタンのダイアログボックス
ダイアログボックスって何か分かりますか?
そう。ちっちゃいウィンドウでボタンがいくつかあって....ってやつです。
今回は、それを表示するAPIを解説します。
MessageBox(オーナーウィンドウのハンドル, "表示文字列", "タイトル", ダイアログの種類) |
では第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
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); |
また、描画が終わる時に、デバイスコンテキストを解放しなければなりません。それがこの関数。
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
引数 ...
HWND hwnd
LPPAINTSTRUCT lpPaint
デバイスコンテキストへのハンドルを取得する。
hWnd - ウィンドウのハンドル
lpPaint - PAINTSTRUCT構造体へのポインタ
引数 ...
HWND hwnd
CONST PAINTSTRUCT *lpPaint
デバイスコンテキストへのハンドルを解放する。描画処理は上のBeginPaintと挟んで行う。
hWnd - ウィンドウのハンドル
lpPaint - PAINTSTRUCT構造体へのポインタ
引数 ...
HDC hdc
int nXStart
int nYStart
LPCTSTR lpString
int cbString
画面に文字列を描画する。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
nXStart - 描画開始地点のX座標
nYStart - 描画開始地点のY座標
lpString - 描画する文字列
cbString - 文字列のバイト長
引数 ...
HDC hdc
int X
int Y
LPPOINT lpPoint
画面に文字列を描画する。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
nXStart - 描画開始地点のX座標
nYStart - 描画開始地点のY座標
lpPoint - 以前設定されていたカレントポジション
引数 ...
HDC hdc
int nXEnd
int nYEnd
MoveToExで設定した描画開始地点から線を引く。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
nXStart - 描画終了地点のX座標
nYStart - 描画終了地点のY座標
引数 ...
HDC hdc
COLORREF crColor
文字色を設定する。
hdc - BeginPaintで取得したデバイスコンテキストへのハンドル
crColor - 文字色を表す32ビット整数。RGBマクロを用いるのが普通。
引数 ...
BYTE bRed
BYTE bGreen
BYTE bBlue
32ビット整数をRGB形式で設定できる。
bRed - 赤
bGreen - 緑
bBlue - 青
■四角形
まずは、四角形と角丸四角形の描画です。
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;
スタイルは、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);
■今回出て来た型、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
引数 ...
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect
長方形を描画する。
hdc - デバイスコンテキストのハンドル
nLeftRect - 長方形の左
nTopRect - 長方形の上
nRightRect - 長方形の右
nBottomRect - 長方形の下
引数 ...
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect
int nWidth
int nHeight
角丸長方形を描画する。
hdc - デバイスコンテキストのハンドル
nLeftRect - 長方形の左
nTopRect - 長方形の上
nRightRect - 長方形の右
nBottomRect - 長方形の下
nWidth - 丸い部分の横幅
nHeight - 丸い部分の縦幅
引数 ...
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect
指定された長方形に内接する円を描画する。
hdc - デバイスコンテキストのハンドル
nLeftRect - 外接する長方形の左
nTopRect - 外接する長方形の上
nRightRect - 外接する長方形の右
nBottomRect - 外接する長方形の下
引数 ...
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 - 描画終了地点の設定
引数 ...
HDC hdc
HGDIOBJ hgdiobj
オブジェクトを設定する。
hdc - デバイスコンテキストへのハンドル
hgdiobj - 設定するオブジェクト
引数 ...
int fnPenStyle
int nWidth
COLORREF crColor
ペンを作成する。メッセージが終わったら消去しなければいけない。
fnPenStyle - ペンのスタイル
nWidth - ペンの太さ
crColor - ペンの色を表す32ビット整数。RGBマクロを用いるのが普通。
fnPenStyle(ペンスタイル) の定数 PS_SOLID 実線のペン PS_DASH 破線のペン PS_DOT 点線のペン PS_DASHDOT 破線と一点のペン PS_DASHDOTDOT 破線と二点のペン
引数 ...
CONST LOGBRUSH *lplb
ブラシを作成する。メッセージが終わったら消去しなければいけない。
lplb - ブラシを表すLOGBRUSH構造体型へのポインタ
引数 ...
HGDIOBJ hObject
ペンやブラシを消去し、それに関するシステムリソースを解放する。
hObject - ペンやブラシを表すハンドル。(HPEN型やHBRUSH型)
■クリックすると発するメッセージ
さて、描画関係の話は置いといて、マウス操作に関するメッセージをみていきましょう。
まずは、基本である「クリック」されると発生するメッセージ。
さて、これらのメッセージが発生した時、やっとウィンドウプロシージャの第3引数wParamが効果を発揮します。
wParamには、マウスのボタン状態、Shiftキー、Ctrlキーの状態が定数で格納されています。
また、ウィンドウプロシージャの第4引数lParamにもマウスの座標が入っています。
上位16ビットにy座標、下位16ビットにx座標が代入されます。
しかしこのままではx座標とy座標がまとまっていて個別に取り出す事が出来ません。そこで、便利なものがあります。
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);
}
}
|
次回はキーボードに関するメッセージです。
■キーが押されたとき
キーが押された瞬間:WM_KEYDOWN キーが離された瞬間:WM_KEYUP
そこで、「文字入力」を表すメッセージがあります。
文字入力でキー離された瞬間:WM_CHAR
また、逆の『どこどこのキーが押されたか否か』を返すAPIがあります。
GetKeyState()というAPIです。使用例は、
if (GetKeyState(VK_SHIFT)) //シフトキーが押されたか
if (GetKeyState("a")) //aキーが押されたか
さて、前回のサンプルプログラムはつまらなかったので、
今回はキーが押されたらランダムな場所にその文字を描画する、という仕様にしましょう。
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構造体へのポインタは渡す必要はありません。
間違い等ありましたら御指摘をお願いします。