|
|||||
|
|||||
|
||||||
たいていのメインウィンドウにはメニューバーがあり、WM_DESTROYメッセージハンドラでは
単にPostQuitMessage()を呼び出しています。
ところが、これまでの例のようにCWindowImplクラスをメインウィンドウの基底クラスとして使用すると、
毎回自分でCWndClassInfo構造体をカスタマイズしてメニューリソースを設定したり、
メッセージマップにWM_DESTROYメッセージ用のエントリを追加して、
PostQuitMessage()を呼び出すためだけにWM_DESTROYメッセージハンドラを書かなければなりません。WTLはこのような基本的な機能を持つウィンドウを簡単に作るために、 CFrameWindowImplというクラスを
用意しています。CFrameWindowImpl(または、その基底クラスであるCFrameWindowImplBase)クラスには、
WM_DESTROYメッセージ用のデフォルトのハンドラが用意されている他、
ウィンドウサイズが変更されたときに自動的にレイアウトを調整するWM_SIZEメッセージハンドラ、
ツールバーやステータスバーを作成する関数、キーボードアクセラレータ用のコードやツールチップ用の
メッセージハンドラなどが備えられています。また、メニューバーなどのリソースを簡単に設定するマクロをサポートしています。ここでは、前回のポップアップメニューを追加した「Hello, ATL/WTL」プログラムの基底クラスを CWindowImplからCFrameWindowImplへ変更して書き換えます。![]()
まず、stdafx.h内では CFrameWindowImplクラスを使用するためにatlframe.hヘッダをインクルードします。次に、メインウィンドウである CMyWindowクラスの基底クラスを、
CWindowImplクラスからCFrameWindowImplクラスへ変更します。
CFrameWindowImplクラスもCWindowImplクラスの時と同様に、
3つのテンプレート引数を持ちます。ここでは省略可能な第2、3テンプレート引数を省略し、
第1テンプレート引数に派生クラスの名前を渡します。なお、デフォルトで第2テンプレート引数にはCWindow、
第3テンプレート引数にはウィンドウ特性としてCFrameWinTraitsが渡されます。
CFrameWinTraitsはATLのatlwin.hヘッダで次のように定義されています。
CMyWindowクラスではまず、フレームウィンドウ用のウィンドウクラス名登録マクロを使って、
ウィンドウクラス名、リソースID、スタイル、背景色を登録しています。
この時、リソースIDは以下のリソースで共通です。
つまり、これらのリソースに共通のリソースIDを付け、そのIDを DECLARE_FRAME_WND_CLASS_EXマクロによって
登録すると、CFrameWindowImplクラスはそれらのリソースを自動的にウィンドウに設定するのです。
今回の例では、共通リソースIDとしてIDR_MAINFRAMEを使用することにしました。
そこで、アイコンリソースやウィンドウタイトルのための文字列リソースを追加し、それぞれにIDR_MAINFRAMEというIDを付けます。
また、メニューバーのためのメニューリソースIDもIDR_MAINFRAMEに変更します。![]() なお、今回の例ではツールバーやキーボードアクセラレータは使用しないので、そのためのリソースは追加していません。 次に、メッセージフィルタでは、基底クラスである CFrameWindowImplクラスの
PreTranslateMessage()を呼び出します。CFrameWindowImpl::PreTranslateMessage()は、
キーボードアクセラレータ用に::TranslateAccelerator()を呼び出しています。以下はそのソースです。
ただし、今回はキーボードアクセラレータを使用しないのであまり関係ありません。 次に、メッセージマップに、基底クラスである CFrameWindowImplクラスへチェーンを追加します。
これによって、WM_DESTROYメッセージに対する処理は、CMyWindowクラスではなく、
CFrameWindowImplクラスに任せることができるので、上記のソースコードからは
WM_DESTROYメッセージハンドラ用のコードを削除しています。もちろん、自分で WM_DESTROYメッセージハンドラを用意して、
そこに自分のアプリケーション特有の処理を加えることができます。その場合、基底クラスである
CFrameWindowImplクラスへメッセージを送ることを忘れてはいけません。
そうしないと、最終的に::PostQuitMessage()が呼び出されず、
プログラムが終了しないからです。
以下は自分でWM_DESTROYメッセージハンドラを用意した場合のコードです。
この例ではまず、 WM_DESTROYメッセージが送られてくると、
CMyWindowクラスのメッセージマップのMSG_WM_DESTROY()エントリによって
CMyWindow::OnDestroy()が実行されます。
CMyWindow::OnDestroy()ではアプリケーション特有の処理をしてから
SetMsgHandled(false)を呼び出します。
この呼び出しによってさらにメッセージマップ内が検索され、
WM_DESTROYメッセージは、
チェーンによって基底クラスであるCFrameWindowImplクラスへ送られます。
CFrameWindowImplクラス内ではさらにチェーンによって基底クラスであるCFrameWindowImplBaseクラスへ送られ、
最終的にCFrameWindowImplBaseクラスのWM_DESTROYメッセージハンドラで
::PostQuitMessage()が呼び出されてアプリケーションは終了します。_tWinMain()では、ウィンドウを作成するためにCreateEx()を使用しています。
CFrameWindowImplクラスはCreate()も用意していますが、
共通リソースIDによって自動的にリソースを設定するにはCreateEx()を使用する必要があります。
|