メッセージハンドラの追加方法
[クラスビュー]-[CWndの派生クラス]-[右クリックメニュー]-[プロパティ]-
[プロパティウィンドウ]-[メッセージ]–[WM_XXX(任意のメッセージを選択)]-[コンボボックス]-[表示されたメソッド名]
メッセージハンドラを追加すると対応するヘッダファイルにはメッセージハンドラ関数のプロトタイプ宣言
cppファイルにはハンドラの定義とメッセージマップマクロが追加されます。
CApp4ViewクラスにWM_LBUTTONDOWNメッセージに対応するメッセージハンドラOnLButtonDownを追加した場合
//------------------------------------------------------------- // CApp4View.h class CApp4View : public CView { //~略~ public: afx_msg void OnLButtonDown(UINT nFlags, CPoint point); } //------------------------------------------------------------- //CApp4View.cpp // ハンドラの定義 void CApp4View::OnLButtonDown(UINT nFlags, CPoint point) { CView::OnLButtonDown(nFlags, point); } //メッセージマップ BEGIN_MESSAGE_MAP(CApp1View, CView) // 標準印刷コマンド ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CApp1View::OnFilePrintPreview) ON_WM_CONTEXTMENU() ON_WM_RBUTTONUP() ON_WM_LBUTTONDOWN() // ←OnLButtonDownに対応するマクロ END_MESSAGE_MAP() ///------------------------------------------------------------
メッセージマップのマクロを詳しく見てみようと思います。
まず、BEGIN_MESSAGE_MAPの中身は次のようになっています。
//------------------------------------------------------------ #define BEGIN_MESSAGE_MAP(theClass, baseClass) \ PTM_WARNING_DISABLE \ const AFX_MSGMAP* theClass::GetMessageMap() const \ { return GetThisMessageMap(); } \ const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \ { \ typedef theClass ThisClass; \ typedef baseClass TheBaseClass; \ static const AFX_MSGMAP_ENTRY _messageEntries[] = \ { ///------------------------------------------------------------
PTM_WARNING_DISABLEマクロの中身は__pragmaなので気にしないでいいです。
AFX_MSGMAPというクラスへのポインタを返す関数が2つ定義されます。
1つ目のGetMessageMap()は2つ目の関数を呼び出しているだけです。
2つ目のGetThisMessageMap()は定義の途中で終わっていますが、
staticなAFX_MSGMAP_ENTRY型の配列_messageEntriesを定義しています。
この配列、及び関数の定義の末尾はEND_MESSAGE_MAP()マクロで生成されています
//------------------------------------------------------------ #define END_MESSAGE_MAP() \ {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \ static const AFX_MSGMAP messageMap = \ { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \ return &messageMap; \ } \ PTM_WARNING_RESTORE ///------------------------------------------------------------
配列の最後に0とAfxSig_endで構成される配列の終端チェック用と思われるオブジェクトが挿入されています。
配列_messageEntriesの定義の後は、このGetThisMessageMap()関数で返される
AFX_MSGMAP型のstaticオブジェクトmessageMapが定義されています。
この関数自身のポインタ(メンバ関数へのポインタ)と先ほど定義した配列_messageEntriesの先頭アドレスで構成されています。
BEGIN_MESSAGE_MAP()マクロとEND_MESSAGE_MAP()マクロの中身で分かるように
この二つに挟まれたマクロは展開するとAFX_MSGMAP_ENTRY型のオブジェクトになります。
AFX_MSGMAP_ENTRY型はメッセージIDや、対応するメンバ関数へのポインタ等で構成されています。
//------------------------------------------------------------ struct AFX_MSGMAP_ENTRY { UINT nMessage; // windows message UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages) UINT nLastID; // used for entries specifying a range of control id's UINT_PTR nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) };//------------------------------------------------------------
それぞれのメッセージハンドラ関数自体は引数付の関数ですが、マクロ展開の過程で、複数のキャストを組み合わせて、
引数なし関数へのポインタ(AFX_PMSG型)に変換されます。
//------------------------------------------------------------ #define ON_WM_LBUTTONDOWN() \ { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \ (AFX_PMSG)(AFX_PMSGW) \ (static_cast ( &ThisClass :: OnLButtonDown)) }, };//------------------------------------------------------------