パソコン活用研究C&C++であそぼ(C、C++、の活用研究)

ウィンドウを表示する
--ウィンドウプロシージャを記述--


今回は、ウィンドウプロシージャをくっけて、完成したプログラムにしてみます。。


window.cpp
#include

// 関数のプロトタイプ宣言
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


int WINAPI WinMain(
                HINSTANCE hInstance ,
                HINSTANCE hPrevInstance ,
                PSTR lpCmdLine ,
                int nCmdShow ) {
        HWND hwnd;
        WNDCLASS wcex;
        MSG msg;

        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            = LoadCursor(NULL , IDC_ARROW);
        wcex.hbrBackground      = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wcex.lpszMenuName       = NULL;
        wcex.lpszClassName      = "AppModel";

        if (!RegisterClass(&wcex)) return 0;

        hwnd = CreateWindow(
                        wcex.lpszClassName , "test" ,
                        WS_OVERLAPPEDWINDOW ,
                        100 , 100 , 200 , 200 , NULL , NULL ,
                        hInstance , NULL
        );

        if (hwnd == NULL) return 0;

        ShowWindow(hwnd , SW_SHOW);

// メッセージループ
while(GetMessage(&msg, NULL, 0, 0)) 
{
     TranslateMessage(&msg);
     DispatchMessage(&msg);
}


        return 0;
}

// ウインドウプロシージャ
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);
}
}



今回以下の通りウィンドウプロシージャを追加しました。

// ウインドウプロシージャ
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);
   }
}


これにともない変更したのはウィンドウクラス構造体の定義の中の、ウィンドウプロシージャの指定部分です。
いままでは、とりあえずDefWindowProc関数を指定してきましたが、ウィンドウプロシージャをちゃんと記述しましたので、ウィンドウプロシージャWindProcを指定します。

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 = LoadCursor(NULL , IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "AppModel";
wcex.lpfnWndProc = (WNDPROC)WndProc;


さて、WinMan()の中ではウィンドウプロシ−ジャWndProcを呼び出しているコードがどこにもありません。DOS時代の知識で考えると、この関数をコールするプログラムコードがどこにも書かれていないことに違和感を感じると思いますが、このWndProcはWindowsから呼び出されることになります。

メッセージループのなかにDispatchMessage関数がありますが、DispatchMessageが実行されると、WindowsからWndProcがコールされることになります(DispatchMessage関数が間接的にウィンドウプロシージャを呼び出している感じになります)。 そしてウィンドウプロシージャWndProcの戻り値がそのままDispatchMessage関数の戻り値になります。


ウィンドウプロシージャの中身をみてみましょう。

WndProcには4つの引数(パラメータ)がありますが、これらのパラメータを設定するのはWinProcを呼び出したWindowsになります。
第一引数のhWndはウィンドウハンドルです。
第二引数のmessageにはWindowsから引き渡されたメッセージが引き渡され、第三、第四引数のwParamとlParamにはメッセージの補足情報が入ります。

簡単にウィンドウハンドルとメッセージとは何ぞやという人のためにおさらいしておくと、
ウィンドウハンドルは、(画面上にたくさん開いている)ウィンドウを識別するための値です。CreateWindowでウィンドウの作成をしたときに、決められたやつですね(CreateWindowの戻り値)。もちろんその値を決めたのは
Windowsですね。
このウィンドウハンドルの値は、ウィンドウプロシージャの中で使われる様々なAPI関数を呼ぶときにパラメータとして使います。たくさんあるウィンドウのうちどのウィンドウに対する命令なのかを、ウィンドウハンドルで指定するわけです。

メッセージは、キーを押したとか、マウスをクリックしたとかいう内容が入っています。第三、第四にはメッセージ補足情報が入ります。例えばキーを押したというメッセージの場合は、押されたキーの文字コードを示す情報がこの補足情報に入ります。
補足情報が2つまでなら、wParamとlParamにそれぞれの補足情報が直接格納され、3つ以上の場合は、wParamとlParamのどちらかに、補足情報の数、もう一方に補足情報が格納されたメモリ領域のポインタが格納されます。

ウィンドウプロシージャ内では、メッセージの種類に応じてswitch文で処理を分岐させます。
最低限処理しなければならないメッセージはWM_DESTROYです。それ以外のメッセージの処理はDefWindowProcに任せておくことができます。

WM_DESTROYは、ウィンドウの右上の[×]ボタンをクリックしたり、メニューからアプリケーションの終了を選択するなど、プログラムを終了する操作時に送られるメッセージです。
このメッセージを受けた時は、以下の通りプログラムを終了させる処理を記述します。
(A) パラメータに0を指定したPostQuitMessage関数を呼ぶ。
 PostQuitMessage(0);
PostQuitMessage関数を呼ぶとMS_Quitメッセージが送出されます。メッセージループのGetMessage関数がMS_Quitメッセージを受け取るとメッセージループが終了します。(=プログラムの終了)
このあたりのプログラム終了の仕組みは正しくないWindowsプログラムその3のメッセージループの説明 
および正しくないWindowsプログラムその3のMS_Quitに書いていますので、ご参照ください。

(B)ウィンドウプロシジャの戻り値として0を返す。
 return 0;

(DefWindowProc関数)
ウィンドウプロシージャ内で自分で処理を記述しないメッセージはDefWindowProc関数にすべて任せて処理を行います。ウィンドウの最小化、最大化、ウィンドウのサイズ変更、システムメニューの表示など一般的なWindowsプログラムに共通した処理はこの関数に任せておけば、この関数が処理してくれます。
とりあえず、自分で処理をしないメッセージは全てDefWindowProc関数に渡してしまって構いません。
DefWindowProc関数のデフォルト処理は、多くお場合単にメッセージを受け取り戻り値として0を返すだけのようです。

DefWindowProc関数に渡すパラメータは、ウィンドウプロシ−ジャに渡したパラメータとまったく同じものを渡します。メッセージ処理後にDefWindowProc関数の戻り値をそのままウィンドウプロシージャの戻り値とすればよいことになっていますので、コードは以下のように記述すればOKです。
 return DefWindowProc(hWnd, message, wParam, lParam);

長いながーい道のりでしたが、これでとりあえずフォームを表示するだけのプログラムですが、正しいWindowsプログラムの完成です。

TopPage