「Win32/64アプリ開発 003 基礎の理解続き」の版間の差分
(→概要) |
(→概要) |
||
(同じ利用者による、間の9版が非表示) | |||
3行目: | 3行目: | ||
前の記事:[[Win32/64アプリ開発 002 基礎の理解]] | 前の記事:[[Win32/64アプリ開発 002 基礎の理解]] | ||
次の記事:[[Win32/64アプリ開発 004]] | 次の記事:<nowiki>[[Win32/64アプリ開発 004]]</nowiki> | ||
== '''概要''' == | == '''概要''' == | ||
前の記事の続きです。引き続き、C++(Cpp)を使います。 | |||
17行目: | 17行目: | ||
LRESULT型の戻り値や関数規約のCALLBACKとかがついてますが、ウィンドウプロシージャ関数の定義です。関数規約によって特殊な関数としてコンパイラが扱ってくれます。CALLBACKは実際は__stdcallというキーワードになっています。LRESULTはLONG_PTR型を意味していて更に、LONG_PTR型はlong long型が元になっています。ようするに64ビットの整数です。そんなに長いビットが必要な返り値ではないですが、このように定義することになっています。 | |||
23行目: | 23行目: | ||
'''<span style="color:darkred;">■WndProc</span>''' | === WndProc === | ||
'''<span style="color:darkred;">■WndProc(HWND, UINT, WPARAM, LPARAM)</span>''' | |||
'''第1引数:''' HWND hWnd | '''第1引数:''' HWND hWnd | ||
メッセージを受け取ったウィンドウハンドル番号。どのウィンドウに対して送られてきたものなのかがわかります。 | メッセージを受け取ったウィンドウハンドル番号。どのウィンドウに対して送られてきたものなのかがわかります。 | ||
'''第2引数:''' UINT msg | '''第2引数:''' UINT msg | ||
送られてきたメッセージの番号。ものすごい数の種類がありますが、受け取る側は、必要なメッセージだけを見つけて処理する感じになります。 | 送られてきたメッセージの番号。ものすごい数の種類がありますが、受け取る側は、必要なメッセージだけを見つけて処理する感じになります。 | ||
'''第3引数:''' WPARAM wParam | '''第3引数:''' WPARAM wParam | ||
送られてきたメッセージによって異なるパラメータです。メッセージによっては、wParamの上位ビットだけで意味を持っていたり、下位ビットだけで意味を持っていたりもします。 | 送られてきたメッセージによって異なるパラメータです。メッセージによっては、wParamの上位ビットだけで意味を持っていたり、下位ビットだけで意味を持っていたりもします。 | ||
'''第4引数:''' LPARAM lParam | '''第4引数:''' LPARAM lParam | ||
42行目: | 46行目: | ||
次に以下のswitch文について確認しましょう。 | |||
72行目: | 76行目: | ||
'''WM_SIZE''': | '''WM_SIZE''': | ||
ウィンドウのサイズ変更を検出する都度送られてくるメッセージです。マウスでウィンドウの縁をつかんで、ドラッグしたときには連続して送られてきます。このとき、lParamの上位16ビットに新しいWindow高さ値、下位16ビットに新しいWindowの横幅値が格納されています。ウィンドウの大きさが変わったので中身の表示も再描画してほしいことがほとんどですから、処理の終わりにはWM_PAINTというメッセージも発行して欲しいので、UpdateWindow関数を実行をすることが多いです。その前に再描画すべき無効領域の指定も必要となります。その辺の細かいところは、後ほど。 | |||
86行目: | 90行目: | ||
それではひとつづつのメッセージ毎の処理を確認してみましょう。最初はWM_CREATEの部分についてです。 | |||
<syntaxhighlight lang="cpp"> | |||
HWNDg_hEdit = CreateWindowEx( | |||
0, | |||
L"EDIT", // エディットコントロールのクラス名 | |||
L"Hello, World!",//エディットコントロールの初期値 | |||
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_HSCROLL | WS_VSCROLL, | |||
0, 0, // エディットコントロールの位置(ウィンドウの左上に配置) | |||
0, 0, // エディットコントロールのサイズ | |||
hWnd, NULL, NULL, NULL | |||
); | |||
break; | |||
</syntaxhighlight> | |||
2回目のCreateWindowEx関数呼び出しです。これで既に描画されたWindowの中にWindowを描画します。但し、ここではシステムの定義済みコントロールのクラス名を第2引数で設定しました。L"EDIT"としています。これでWindowの中にテキスト編集が可能なエディットコントロールが内蔵されます。後は、最初のWindow生成の要領と同じですが、最初はウィンドウの左上隅に大きさ0として、サイズ0の見えないウィンドウとして初期描画します。第3引数は、ウィンドウの場合はタイトルの文字列でしたが、エディットコントロールでは、テキストの初期値になります。第9引数も最初のウィンドウでは親のウィンドウというものが無いのでNULLとしていましたが、最初のWindowのハンドル値が入ってるWndProc関数の引数でもあるhWndを設定しています。グローバル変数にも最初のWindowのハンドル値を格納してありますので、HWNDg_hWndと指定しても同じことです。 | |||
WM_CREATEのメッセージが処理された後、InitializeWindow()関数というものを実行されます。メッセージ処理時の戻り値が-1ならInitializeWindow()関数が実行されることはありません。親ウィンドウを持つCreateWindowsEx関数を呼び出しても、再びWM_CREATEメッセージが投げられることはありません。 | |||
テキストを変更しても、保存する機能がないので、更新した内容をコピーしないと駄目な状態です。基礎なので、こんなもんですね。ウィンドウのサイズの変更や、ウィンドウの移動をしてもテキストが保持されるだけでもありがたいです。エディットコントロールではない、文字描画の場合はサイズ変更とかをすると内容が失われて消えますので、再描画が必要になったりします。テキストの表示を制御する方法を習得すれば、Win32アプリでもコンソールアプリと同じように描画できるようになって、役に立つテキスト表示アプリを作ることができるようになります。少しづつ、複雑なウィンドウの構造や複雑な描画・画像表示・音声再生制御について学習して、やれることを広げていきましょう。 | |||
次にWM_SIZEメッセージの部分について確認します。 | |||
<syntaxhighlight lang="cpp"> | |||
int width = LOWORD(lParam); | |||
int height = HIWORD(lParam); | |||
MoveWindow(HWNDg_hEdit, | |||
0, 0, width, height, TRUE); | |||
InvalidateRect(hWnd, NULL, TRUE); | |||
UpdateWindow(hWnd); | |||
break; | |||
</syntaxhighlight> | |||
ウィンドウのサイズに変更があった場合、その大きさがどうなったかを知りたいモノですが、これはlParamという変数で情報を受け取ることができます。lParamの下位16bitにウィンドウの横幅、上位16bitにウィンドウの高さの情報が格納されているので、 | |||
int width = LOWORD(lParam)のようにLOWORDという関数で下位16bitのみを取り出した整数値を得ることができます。同じようにint height = HIWORD(lParam)、HIWORDという関数で上位16bitのみを取り出した整数値を得ることができます。 | |||
次にMoveWindow関数でWindowの大きさを変更します。WM_CREATEメッセージ処理のときに生成したエディットコントロールのウィンドウハンドルをHWND_hEditに確保していたので、これをつかって、新しく作ったエディットコントロールの位置をhWndが親の領域としているので、hWnd領域の原点、左上隅を水平x方向座標 0, 垂直y方向座標 0 として、幅をwidth、高さをheightにします。 | |||
=== MoveWindow === | |||
'''<span style="color:darkred;">■MoveWindow(HWND, int, int, int, int, BOOL)</span>''' | |||
'''第1引数:''' HWND hWnd | |||
ウィンドウ領域を移動させるウィンドウのハンドル値 | |||
'''第2引数:''' int X | |||
親領域の座標系の水平x位置の指定された値に第一引数のウィンドウのハンドルの領域の左上隅を配置する | |||
'''第3引数:''' int Y | |||
親領域の座標系の垂直x位置の指定された値に第一引数のウィンドウのハンドルの領域の左上隅を配置する | |||
'''第4引数:''' int nWidth | |||
第一引数のウィンドウのハンドルの領域変更後の幅 | |||
'''第5引数:''' int nHeight | |||
第一引数のウィンドウのハンドルの領域変更後の高さ | |||
'''第6引数:''' BOOL bRepaint | |||
ウィンドウを再描画するかどうかを示します。このパラメーターが TRUE の場合、ウィンドウはUpdateWindow関数を呼び出しWM_PAINTメッセージを受信します。FALSEの場合ウィンドウ内の表示を更新するには、プログラムで明示的に呼び出す必要があります。 | |||
MoveWindow関数でWindowを更新するべく第6引数がTRUEにしてありますので、必要ないかもしれませんが、続いて | |||
InvalidateRect関数とUpdateWindow関数を実行しています。 | |||
InvalidateRectは無効領域を定義する関数です。UpdateWindow関数は無効になっている領域を更新します。 | |||
=== InvalidateRect === | |||
'''<span style="color:darkred;">■InvalidateRect(HWND, CONST RECT, BOOL)</span>''' | |||
'''第1引数:'''HWND hWnd | |||
再描画する領域のウィンドウハンドル。 | |||
'''第2引数:'''CONST RECT *lpRect | |||
再描画する無効領域座標を示したRECT型ポインタ。NULLなら第1引数のウィンドウハンドルのウィンドウ領域全体が対象になります。 | |||
'''第3引数:'''BOOL bErase | |||
バックグラウンドを消去するかのフラグ。TRUEならバックグラウンドも消去します。 | |||
次にWM_DESTROYメッセージの処理です。 | |||
<syntaxhighlight lang="cpp"> | |||
PostQuitMessage(0); | |||
</syntaxhighlight> | |||
PostQuitMessage関数を実行するとWM_QUITメッセージが送信されます。引数の値がメッセージ型のwParamメンバ関数に設定され、この値をWinMain関数の戻り値として処理します。0は正常終了を意味します。 | |||
以上が基礎となるWindowsプログラムの紹介でした。長くなりましたが、これでも理解できない部分は多かったと思います。応用していこうとすると、もっとひとつづつを掘り下げて自分で調べていく必要があるかもしれません。でも知らなくても、いつのまにか覚えていけると思うので、また、続きをどこかで?ここで?知っていくと良いと思います。 | |||
93行目: | 216行目: | ||
前の記事:[[Win32/64アプリ開発 002 基礎の理解]] | 前の記事:[[Win32/64アプリ開発 002 基礎の理解]] | ||
次の記事:[[Win32/64アプリ開発 004]] | 次の記事:<nowiki>[[Win32/64アプリ開発 004]]</nowiki> | ||
[[Win32/64_アプリケーション開発#記事|Win32/64_アプリケーション開発]]に戻る。 | [[Win32/64_アプリケーション開発#記事|Win32/64_アプリケーション開発]]に戻る。 |
2024年1月25日 (木) 00:18時点における最新版
次の記事:[[Win32/64アプリ開発 004]]
概要
前の記事の続きです。引き続き、C++(Cpp)を使います。
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
…省略
}
LRESULT型の戻り値や関数規約のCALLBACKとかがついてますが、ウィンドウプロシージャ関数の定義です。関数規約によって特殊な関数としてコンパイラが扱ってくれます。CALLBACKは実際は__stdcallというキーワードになっています。LRESULTはLONG_PTR型を意味していて更に、LONG_PTR型はlong long型が元になっています。ようするに64ビットの整数です。そんなに長いビットが必要な返り値ではないですが、このように定義することになっています。
Windowsから送られてくるメッセージから4つの引数が受け取ることが出来るようになっています。それぞれの引数について確認しておきます。
WndProc
■WndProc(HWND, UINT, WPARAM, LPARAM)
第1引数: HWND hWnd
メッセージを受け取ったウィンドウハンドル番号。どのウィンドウに対して送られてきたものなのかがわかります。
第2引数: UINT msg
送られてきたメッセージの番号。ものすごい数の種類がありますが、受け取る側は、必要なメッセージだけを見つけて処理する感じになります。
第3引数: WPARAM wParam
送られてきたメッセージによって異なるパラメータです。メッセージによっては、wParamの上位ビットだけで意味を持っていたり、下位ビットだけで意味を持っていたりもします。
第4引数: LPARAM lParam
こちらも送られてきたメッセージによって異なるパラメータです。メッセージによっては、lParamの上位ビットだけで意味を持っていたり、下位ビットだけで意味を持っていたりもします。
次に以下のswitch文について確認しましょう。
switch (msg){
case WM_CREATE:
…省略…
break;
case WM_SIZE:
…省略…
break;
case WM_DESTROY:
…省略…
break;
default:
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
これで受け取ったmsg毎の処理に分けています。全てのメッセージ番号には別名がついています。windows.hを読み込んでいれば、様々な定数が定義されています。これまでにも何の気なしにキーワードを使えていたのも、windows.hのおかげです。今回メッセージを受け取って処理をするのは3つのメッセージですので、その3つについて説明します。ここで一気に説明したいところですが、おびただしい数のメッセージがありますので、用途ごとに覚えた方が頭に入りやすいと思うので、目的をもってプログラムをする上で必要になったときに覚えていったほうが良いと思います。
WM_CREATE:
ウィンドウを生成されるような処理が起こった後に呼び出されます。最初のウィンドウ表示のShowWindow関数の後すぐにWM_CREATEが呼ばれるわけではなく、その関数を実行した後にやってくる2個ほどのメッセージが処理されるとウィンドウが表示されます。このあとでWM_CREATEというメッセージが送られてきます。ここでは最初のウィンドウが出来上がった所で次のウィンドウ作成処理を行うような処理を実施しようと思っています。
WM_SIZE:
ウィンドウのサイズ変更を検出する都度送られてくるメッセージです。マウスでウィンドウの縁をつかんで、ドラッグしたときには連続して送られてきます。このとき、lParamの上位16ビットに新しいWindow高さ値、下位16ビットに新しいWindowの横幅値が格納されています。ウィンドウの大きさが変わったので中身の表示も再描画してほしいことがほとんどですから、処理の終わりにはWM_PAINTというメッセージも発行して欲しいので、UpdateWindow関数を実行をすることが多いです。その前に再描画すべき無効領域の指定も必要となります。その辺の細かいところは、後ほど。
WM_DESTROY:
通常はWM_CLOSEメッセージのような閉じるボタンを押した時に送信されるメッセージを受け取って、DestroyWindow関数が呼ばれた時にWM_DESTROYメッセージが送信されます。次にPostQuitMessage関数を実行してWM_QUITメッセージを送るという手順になります。
このようにメッセージ毎に次々と処理をリレー形式で行うこともあれば、メッセージにあわせてアプリケーション独自の処理を実行したりすることになります。
メッセージを全部網羅して処理することはできないので、捕まえるメッセージ以外が来たときはswitchのdefault処理で規定値の動きをしてもらう関数DefWindowProc(ウィンドウプロシージャ関数の引数をそのまま4つ設定する)という関数を呼び出すことができます。
それではひとつづつのメッセージ毎の処理を確認してみましょう。最初はWM_CREATEの部分についてです。
HWNDg_hEdit = CreateWindowEx(
0,
L"EDIT", // エディットコントロールのクラス名
L"Hello, World!",//エディットコントロールの初期値
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_HSCROLL | WS_VSCROLL,
0, 0, // エディットコントロールの位置(ウィンドウの左上に配置)
0, 0, // エディットコントロールのサイズ
hWnd, NULL, NULL, NULL
);
break;
2回目のCreateWindowEx関数呼び出しです。これで既に描画されたWindowの中にWindowを描画します。但し、ここではシステムの定義済みコントロールのクラス名を第2引数で設定しました。L"EDIT"としています。これでWindowの中にテキスト編集が可能なエディットコントロールが内蔵されます。後は、最初のWindow生成の要領と同じですが、最初はウィンドウの左上隅に大きさ0として、サイズ0の見えないウィンドウとして初期描画します。第3引数は、ウィンドウの場合はタイトルの文字列でしたが、エディットコントロールでは、テキストの初期値になります。第9引数も最初のウィンドウでは親のウィンドウというものが無いのでNULLとしていましたが、最初のWindowのハンドル値が入ってるWndProc関数の引数でもあるhWndを設定しています。グローバル変数にも最初のWindowのハンドル値を格納してありますので、HWNDg_hWndと指定しても同じことです。
WM_CREATEのメッセージが処理された後、InitializeWindow()関数というものを実行されます。メッセージ処理時の戻り値が-1ならInitializeWindow()関数が実行されることはありません。親ウィンドウを持つCreateWindowsEx関数を呼び出しても、再びWM_CREATEメッセージが投げられることはありません。
テキストを変更しても、保存する機能がないので、更新した内容をコピーしないと駄目な状態です。基礎なので、こんなもんですね。ウィンドウのサイズの変更や、ウィンドウの移動をしてもテキストが保持されるだけでもありがたいです。エディットコントロールではない、文字描画の場合はサイズ変更とかをすると内容が失われて消えますので、再描画が必要になったりします。テキストの表示を制御する方法を習得すれば、Win32アプリでもコンソールアプリと同じように描画できるようになって、役に立つテキスト表示アプリを作ることができるようになります。少しづつ、複雑なウィンドウの構造や複雑な描画・画像表示・音声再生制御について学習して、やれることを広げていきましょう。
次にWM_SIZEメッセージの部分について確認します。
int width = LOWORD(lParam);
int height = HIWORD(lParam);
MoveWindow(HWNDg_hEdit,
0, 0, width, height, TRUE);
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;
ウィンドウのサイズに変更があった場合、その大きさがどうなったかを知りたいモノですが、これはlParamという変数で情報を受け取ることができます。lParamの下位16bitにウィンドウの横幅、上位16bitにウィンドウの高さの情報が格納されているので、
int width = LOWORD(lParam)のようにLOWORDという関数で下位16bitのみを取り出した整数値を得ることができます。同じようにint height = HIWORD(lParam)、HIWORDという関数で上位16bitのみを取り出した整数値を得ることができます。
次にMoveWindow関数でWindowの大きさを変更します。WM_CREATEメッセージ処理のときに生成したエディットコントロールのウィンドウハンドルをHWND_hEditに確保していたので、これをつかって、新しく作ったエディットコントロールの位置をhWndが親の領域としているので、hWnd領域の原点、左上隅を水平x方向座標 0, 垂直y方向座標 0 として、幅をwidth、高さをheightにします。
MoveWindow
■MoveWindow(HWND, int, int, int, int, BOOL)
第1引数: HWND hWnd
ウィンドウ領域を移動させるウィンドウのハンドル値
第2引数: int X
親領域の座標系の水平x位置の指定された値に第一引数のウィンドウのハンドルの領域の左上隅を配置する
第3引数: int Y
親領域の座標系の垂直x位置の指定された値に第一引数のウィンドウのハンドルの領域の左上隅を配置する
第4引数: int nWidth
第一引数のウィンドウのハンドルの領域変更後の幅
第5引数: int nHeight
第一引数のウィンドウのハンドルの領域変更後の高さ
第6引数: BOOL bRepaint
ウィンドウを再描画するかどうかを示します。このパラメーターが TRUE の場合、ウィンドウはUpdateWindow関数を呼び出しWM_PAINTメッセージを受信します。FALSEの場合ウィンドウ内の表示を更新するには、プログラムで明示的に呼び出す必要があります。
MoveWindow関数でWindowを更新するべく第6引数がTRUEにしてありますので、必要ないかもしれませんが、続いて
InvalidateRect関数とUpdateWindow関数を実行しています。
InvalidateRectは無効領域を定義する関数です。UpdateWindow関数は無効になっている領域を更新します。
InvalidateRect
■InvalidateRect(HWND, CONST RECT, BOOL)
第1引数:HWND hWnd
再描画する領域のウィンドウハンドル。
第2引数:CONST RECT *lpRect
再描画する無効領域座標を示したRECT型ポインタ。NULLなら第1引数のウィンドウハンドルのウィンドウ領域全体が対象になります。
第3引数:BOOL bErase
バックグラウンドを消去するかのフラグ。TRUEならバックグラウンドも消去します。
次にWM_DESTROYメッセージの処理です。
PostQuitMessage(0);
PostQuitMessage関数を実行するとWM_QUITメッセージが送信されます。引数の値がメッセージ型のwParamメンバ関数に設定され、この値をWinMain関数の戻り値として処理します。0は正常終了を意味します。
以上が基礎となるWindowsプログラムの紹介でした。長くなりましたが、これでも理解できない部分は多かったと思います。応用していこうとすると、もっとひとつづつを掘り下げて自分で調べていく必要があるかもしれません。でも知らなくても、いつのまにか覚えていけると思うので、また、続きをどこかで?ここで?知っていくと良いと思います。
次の記事:[[Win32/64アプリ開発 004]]