Win32/64アプリ開発 003 基礎の理解続き

提供:yonewiki

Win32/64_アプリケーション開発に戻る。

前の記事:Win32/64アプリ開発 002 基礎の理解

次の記事:[[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アプリ開発 002 基礎の理解

次の記事:[[Win32/64アプリ開発 004]]

Win32/64_アプリケーション開発に戻る。