テクニカル ノート 6: メッセージ マップ
ここでは、 MFC メッセージ マップ機能について説明します。
問題
Microsoft Windows ではメッセージング機能を使用するウィンドウ クラスの仮想関数を実行します。インクルードされた多数のメッセージが原因で各ウィンドウ メッセージに別の仮想関数を提供することは禁則に大きな vtable が作成されます。
システム定義したウィンドウ メッセージの数が時間とともに変更されるため、アプリケーションが独自のウィンドウのメッセージを定義できるので、メッセージ マップは、既存のコードを中断からのインターフェイス変更を防ぐ間接のレベルを提供します。
概要
MFC はウィンドウに送信されたメッセージを処理するために、従来の Windows ベースのプログラムで使用する switch ステートメントの代わりに使用します。メッセージのメソッドへのマッピングはウィンドウにメッセージが受信すると、適切なメソッドが自動的に呼び出されるように定義できます。このメッセージマップ機能は仮想関数と類似するように設計されていますが、 C++ の仮想関数では実行できない追加的な利点があります。
メッセージ マップの定義
DECLARE_MESSAGE_MAP のマクロは、クラスの 3 個のメンバーを宣言します。
AFX_MSGMAP_ENTRY エントリのプライベート配列は _messageEntriesと呼ばれます。
_messageEntries に配列を指す AFX_MSGMAP の保護された構造体は messageMap と呼ばれます。
messageMapのアドレスを返すプロテクト仮想関数は GetMessageMap と呼ばれます。
このマクロは、メッセージ マップを使用して、クラス宣言に配置する必要があります。規則では、クラス宣言の最後にあります。次に例を示します。
class CMyWnd : public CMyParentWndClass
{
// my stuff...
protected:
//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
これで、新しいクラスを作成するときは、 AppWizard によって生成されるフォームと ClassWizard です。//と //{{}})は ClassWizard に必要です。
メッセージ マップの表は、メッセージ マップ エントリに配置するマクロ セットを使用して定義されます。テーブルは、ハンドルされないメッセージが渡される親クラスとこのメッセージ マップで処理されるクラスを定義する BEGIN_MESSAGE_MAP のマクロ呼び出しで始まります。テーブルは END_MESSAGE_MAP のマクロ呼び出しで終了します。
この二つのマクロ呼び出し間のこのメッセージが処理される各メッセージ マップのエントリがあります。標準のウィンドウのメッセージにそのメッセージのエントリを生成するフォーム ON_WM_MESSAGE_NAME のマクロがあります。
標準の関数シグネチャは、各ウィンドウのメッセージのパラメーターのアンロードを状態、タイプ セーフを提供するために定義されています。これらの定義は CWndの宣言のファイル Afxwin.h である場合があります。それぞれが簡単に識別できるように afx_msg キーワードでマークされます。
[!メモ]
ClassWizard は、メッセージ マップ ハンドラーの宣言で afx_msg のキーワードを使用する必要があります。
これらの関数シグネチャは、単純な規則を使用して取得されました。関数の名前は "Onから」常に開始されます。これは 「WM_」と削除される Windows メッセージの名前と大文字になる各単語の最初の文字が続きます。パラメーターの順序は LOWORD(lParam)に、 HIWORD()lParam続く wParam です。未使用のパラメーターは渡されません。MFC クラスでラップされたハンドルが適切な MFC のオブジェクトへのポインターに変換されます。次の例に WM_PAINT のメッセージを処理 CMyWnd::OnPaint 関数を呼び出す方法を示しています:
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
メッセージ マップの表は、関数またはクラス定義のスコープ外で定義する必要があります。これは、 extern 「C」 15 のブロックに配置することはできません。
[!メモ]
ClassWizard は //と //{{}} 注釈のかっこの間で実行されるメッセージ マップ エントリを変更します。
ユーザー定義のウィンドウ メッセージ
ユーザー定義メッセージは、メッセージ マップに ON_MESSAGE マクロを使用して含まれていることがあります。このマクロは、フォームのメッセージ数とメソッドを使用します:
// inside the class declaration
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
#define WM_MYMESSAGE (WM_USER + 100)
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
この例では、ユーザー定義メッセージの WM_USER 標準ベースから派生するウィンドウのメッセージ ID を持つカスタム メッセージのハンドラーを作成します。次の例では、このハンドラーを呼び出す方法を示しています:
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
この方法を使用するユーザー定義メッセージの範囲は 0x7fff に範囲 WM_USER にある必要があります。
[!メモ]
ClassWizard は ClassWizard のユーザー インターフェイスの ON_MESSAGE 入力のハンドラー ルーチンをサポートしません。Visual C++ のエディターから手動で入力します。ClassWizard は、これらのエントリを解析し、他のメッセージマップ エントリのように、参照することができます。
登録したウィンドウ メッセージ
RegisterWindowMessage 関数がシステム全体で一意であることが保証される新しいウィンドウ メッセージを定義するために使用されます。マクロ ON_REGISTERED_MESSAGE がこれらのメッセージを処理するために使用します。このマクロは、登録されたウィンドウのメッセージ ID を含む UINT NEAR 変数の名前を受け入れます次に例を示します。
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd)
afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
登録したウィンドウ メッセージ ID の変数 (この例の WM_FIND)は ON_REGISTERED_MESSAGE の使用方法に NEAR の変数である必要があります。
この方法を使用するユーザー定義メッセージの範囲が 0xFFFF 範囲に 0x C000 にあります。
[!メモ]
ClassWizard は ClassWizard のユーザー インターフェイスの ON_REGISTERED_MESSAGE 入力のハンドラー ルーチンをサポートしません。テキスト エディターから手動で入力します。ClassWizard は、これらのエントリを解析し、他のメッセージマップ エントリのように、参照することができます。
コマンド メッセージ
メニュー、アクセラレータのコマンド メッセージは ON_COMMAND マクロのメッセージ マップで処理されます。このマクロは、コマンド ID とメソッドを使用します。指定されたコマンド ID と同じ wParam がある WM_COMMAND の特定のメッセージだけメッセージマップ エントリに指定されているメソッドによって処理されます。コマンド ハンドラー メンバー関数は、パラメーター、および戻り値の voidを受け取りません。マクロに次の形式があります:
ON_COMMAND(id, memberFxn)
コマンド更新のメッセージが同じ機能を通じてルーティングされますが、 ON_UPDATE_COMMAND_UI の代わりにマクロを使用します。コマンド更新ハンドラー メンバー関数は CCmdUI のオブジェクトに単一のパラメーター、ポインター、および戻り値の voidを受け取ります。マクロにフォームがあります
ON_UPDATE_COMMAND_UI(id, memberFxn)
上級ユーザーがコマンド メッセージ ハンドラー拡張子フォームにある ON_COMMAND_EX のマクロを使用できます。マクロは ON_COMMAND の機能のスーパーセットを提供します。拡張コマンド ハンドラー メンバー関数は、コマンド ID を含む受け取り、を返します BOOL単一のパラメーターを、 UINT 。戻り値は、コマンドが処理されたことを示す TRUE である必要があります。はルーティングは他のコマンド ターゲット オブジェクトに従います。
これらの形式の例:
内部 Resource.h (通常は Visual C++ で生成される)
#define ID_MYCMD 100 #define ID_COMPLEX 101
クラス宣言内
afx_msg void OnMyCommand(); afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI); afx_msg BOOL OnComplexCommand(UINT nID);
メッセージ マップの定義の中
ON_COMMAND(ID_MYCMD, OnMyCommand) ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand) ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
実装ファイル
void CMyClass::OnMyCommand() { // handle the command } void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI) { // set the UI state with pCmdUI } BOOL CMyClass::OnComplexCommand(UINT nID) { // handle the command return TRUE; }
上級ユーザーは一つのコマンド ハンドラーを使用してコマンドの範囲を処理できます: ON_COMMAND_RANGE か ON_COMMAND_RANGE_EX。これらのマクロに関する詳細については、製品マニュアルを参照してください。
[!メモ]
ClassWizard は ON_COMMAND と ON_UPDATE_COMMAND_UI ハンドラーの作成をサポートしますが、 ON_COMMAND_EX か ON_COMMAND_RANGE ハンドラーの作成をサポートしません。ただし、クラス ウィザードは、解析し、 4 種類のコマンド ハンドラーのバリアントをすべて参照することを許可します。
コントロール通知メッセージ
子コントロールからウィンドウに送信されるメッセージのメッセージ マップ エントリで情報の余分なビットがあります: コントロールの IDメッセージ マップ エントリに指定されたメッセージ ハンドラーは、次の条件が当てはまる場合にのみ呼び出されます:
コントロール通知コード ( lParamの上位ワード)は、 BN_CLICKED など、メッセージマップ エントリに指定された通知コードに一致します。
コントロール ID (wParam)はメッセージマップ エントリに指定されるコントロールの ID と一致します。
カスタム コントロールの通知メッセージは、カスタム通知コードのメッセージ マップ エントリを定義するに ON_CONTROL マクロを使用する場合があります。このマクロにフォームがあります
ON_CONTROL(wNotificationCode, id, memberFxn)
同じハンドラーでコントロールの範囲から特定のコントロール通知を処理するには、高度な使用に ON_CONTROL_RANGE を使用できます。
[!メモ]
ClassWizard は、ユーザー インターフェイスの ON_CONTROL または ON_CONTROL_RANGE ハンドラーの作成をサポートしません。手動でテキスト エディターを入力します。ClassWizard は、これらのエントリを解析し、他のメッセージ マップ エントリのように、参照することができます。
Windows のコモン コントロールが複雑なコントロールの通知により強力な WM_NOTIFY を使用します。MFC のこのバージョンに ON_NOTIFY と ON_NOTIFY_RANGE マクロを使用してこの新しいメッセージの直接のサポートがあります。これらのマクロに関する詳細については、製品マニュアルを参照してください。