アークピットのホームページに戻る

WinAPIトピックのトップページに戻る

APIトピックの各章に移動する

ダウンロードのページに移動する
ダウンロードができ
ない場合の対処法
 

ページ移動

2-4-9. MDI子ウィンドウのプロシージャ

 MDI子ウィンドウ(ドキュメントウィンドウ)のウィンドウプロシージャは、普通のプロシージャとはやや異なります。

MDI子ウィンドウのプロシージャ

 MDIの子ウィンドウのウィンドウプロシージャの例を以下に示します。色が違う部分がMDIに関連した部分です。ApMdisysには、2つのMDI子ウィンドウのウィンドウプロシージャがあります。ApHelloのプロシージャは簡単なので、ここではApReditのプロシージャを提示します。
LRESULT CALLBACK ReditProc(HWND hWnd,UINT uMsg
                                    ,WPARAM wParam,LPARAM lParam)
{
    HWND hEdit = (HWND)GetWindowLong(hWnd,0);

    switch(uMsg)
    {
        //
        // MDI子ウィンドウの新規作成で、各種の初期化を行なう。
        //
        case WM_CREATE:
        {
            LPCREATESTRUCT    pcs;
            LPMDICREATESTRUCT pmdic;
            HINSTANCE hInst;
            LPSTR     pFile;

            pcs   = (CREATESTRUCT *)lParam;
            pmdic = (MDICREATESTRUCT *)pcs->lpCreateParams;
            pFile = (LPSTR)pmdic->lParam;
            hInst = (HINSTANCE)GetWindowLong(hWnd,GWL_HINSTANCE);
            hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,"RICHEDIT",NULL,
                             WS_CHILD|WS_BORDER|WS_VISIBLE|WS_VSCROLL|
                             ES_AUTOVSCROLL|ES_MULTILINE|ES_NOHIDESEL,
                             0,0,0,0,
                             hWnd,(HMENU)ID_RTF,hInst,NULL);
            SetWindowLong(hWnd,0,(LONG)hEdit);
                  : (他のコード)
            return 0;
        }
        //
        // このウィンドウのクローズに伴う破棄で後処理とメモリ開放
        //
        case WM_DESTROY:
        {
            if (hEdit) DestroyWindow(hEdit);
            break;
        }
        //
        // ウィンドウをリサイズしたのでRTFコントロールも変更する
        //
        case WM_SIZE:
        {
            if (hEdit)
                MoveWindow(hEdit,0,0,
                           LOWORD(lParam),HIWORD(lParam),TRUE);
            break; // return 0;はダメ
        }
        //
        // このウィンドウにフォーカスが移った
        //
        case WM_SETFOCUS:
        {
            if (hEdit) SetFocus(hEdit);
            break;
        }
        //
        // このMDI子ウィンドウがアクティブなため、固有のメニューを設定する。
        //
        case WM_MDIACTIVATE:
        {
            HMENU hMenu;

            if ((HWND)lParam == hWnd)
            {
                hMenu = GetSubMenu(hMenuRedit,IDMC_WINDOW+1);
                SendMessage(hWndClient,WM_MDISETMENU
                                ,(WPARAM)hMenuRedit,(LPARAM)hMenu);
            }
            else
            {
                hMenu = GetSubMenu(hMenuInit,IDMC_WINDOW);
                SendMessage(hWndClient,WM_MDISETMENU
                                ,(WPARAM)hMenuInit,(LPARAM)hMenu);
            }
            DrawMenuBar(hWndMain);
            return 0;
        }
    }
    return DefMDIChildProc(hWnd,uMsg,wParam,lParam);
}

MDI子ウィンドウのプロシージャの省略時の処理関数

 MDI子ウィンドウのプロシージャの省略時の処理関数は、DefMDIChildProc です。通常は、DefWindowProc 関数を使用しますので間違わないでください。

 また、以下のウィンドウ管理メッセージは、アプリケーションが処理した場合でも、DefMDIChildProc 関数に渡さなければいけません。上記のコードの、WM_SIZE で break しているのは理由があります。

メッセージ 応 答(DefMDIChildProc 関数の処理)
WM_CHILDACTIVATE MDI子ウィンドウのサイズ変更、移動、表示のときのアクティブ化処理を行います。このメッセージは必ず渡してください。
WM_GETMINMAXINFO MDIクライアントウィンドウの現在のサイズに基づいて、最大化したMDI子ウィンドウのサイズを計算します。
WM_MENUCHAR メッセージをMDIフレームウィンドウに渡します。
WM_MOVE MDIクライアントスクロールバーがあれば再計算します。
WM_SETFOCUS アクティブでないMDI子ウィンドウをアクティブ化します。
WM_SIZE MDI子ウィンドウを最大化したり復元するときなど、ウィンドウのサイズ変更に必要な処理を行います。このメッセージをDefMDIChildProc関数に渡さないと、大きな問題が発生します。
WM_SYSCOMMAND コントロールメニューのコマンドを処理します。コントロールメニューのコマンドには、SC_NEXTWINDOW、SC_PREVWINDOW、SC_MOVE、SC_SIZE、SC_MAXIMIZEがあります。

CreateMDIWindow 関数の最後パラメータの lParam の受け取り

 CreateMDIWindow 関数の最後パラメータである、lParam は,WM_CREATE で受け取ります。しかしその方法ややや複雑です。まず、ReditProc のlParam は、LPCREATESTRUCT 構造体を指します。そしてそのメンバである、lpCreateParams は、LPMDICREATESTRUCT 構造体を指します。LPMDICREATESTRUCT 構造体の lParam メンバが、CreateMDIWindow 関数の最後パラメータで渡した値になります。ここでは分かり易くするために、3行になっていますが、以下の様にキャスティングしても結構です。
   MDICREATESTRUCT *pmdic;
   pmdic = (MDICREATESTRUCT *)((CREATESTRUCT *)lParam)->lpCreateParams;
   pFile = (LPSTR)pmdic->lParam;
 ところで、ApMdisysでは、ファイルメニューの「文章ウィンドウの新規作成」コマンドでは、NULLを渡し、「開く」コマンドではユーザが選択したファイル名を渡しています。上記のコードでは省略されていますが、pFile がNULLで無い時は、そのファイルをオープンして、リッチテキストコントロールにロードしています。

ウィンドウ補足メモリブロックの活用

 リッチテキストコントロールを作成して、ウィンドウハンドルを得ますが、そのハンドルを、SetWindowLong(hWnd,0,(LONG)hEdit)関数で、補足メモリブロックに設定します。またプロシージャの一番上に、HWND hEdit = (HWND)GetWindowLong(hWnd,0); と1行あります。これは、初期化文ですので、ReditProc が実行される度に、GetWindowLong 関数をコールして、このハンドルを得ます。プロシージャはシステムのパフォーマンスに関わりますので、こんな文でよけいな時間を掛けたくないのですが、スピードよりサイズを重視しました。この文1つで、hEdit を必要とするすべてのメッセージに対応できる利点は見逃せません。

WM_MDIACTIVATE メッセージ

 MDIの子ウィンドウをクリックしたりして、アクティブが移動すると、対象のウィンドウプロシージャに、WM_MDIACTIVATE メッセージが発生します。まず非アクティブになるウィンドウに、wParam にウィンドウハンドルを渡して発生します。次にアクティブになるウィンドウに、lParam にウィンドウハンドルを渡して発生します。よってアクティブになったら、ApReditの場合は、書式メニューを、ApHelloの場合は、表示メニューを追加します。そのための処理が、WM_MDISETMENU メッセージをMDIクライアントウィンドウに送信することで実行されます。

ページ移動