テクニカル ノート 61: ON_NOTIFY メッセージと WM_NOTIFY メッセージ

Note

次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。

このテクニカル ノートでは、新しい WM_NOTIFY メッセージに関する背景情報を提供し、MFC アプリケーションで WM_NOTIFY メッセージを処理する際に推奨される (最も一般的な) 方法について説明します。

Windows 3.x の通知メッセージ

Windows 3.x では、コントロールは親にメッセージを送信することによって、マウスのクリック、コンテンツや選択の変更、コントロールの背景描画などのイベントについて親に通知します。 単純な通知は、特殊な WM_COMMAND メッセージとして送信されますが、このとき、通知コード (BN_CLICKED など) とコントロール ID は wParam にパックされ、コントロールのハンドルは lParam にパックされます。 wParamlParam は満杯となるので、追加のデータを渡す方法はありません。これらのメッセージは単純な通知にすぎません。 たとえば、BN_CLICKED 通知では、ボタンがクリックされたときの、マウス カーソルの位置に関する情報を送信する方法はありません。

Windows 3.x のコントロールから追加のデータを含む通知メッセージを送信する必要がある場合は、特殊な目的を持つさまざまなメッセージ (WM_CTLCOLOR、WM_VSCROLL、WM_HSCROLL、WM_DRAWITEM、WM_MEASUREITEM、WM_COMPAREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKEYTOITEM など) が使用されます。 これらのメッセージは、その送信元となったコントロールに反映することができます。 詳細については、「テクニカル ノート 62: Windows コントロールへのメッセージ リフレクション」を参照してください。

Win32 の通知メッセージ

Win32 API では、Windows 3.1 に存在したコントロールについては、Windows 3.x で使用された通知メッセージのほとんどが使用されます。 ただし、Win32 では、Windows 3.x でサポートされていたもの以外に、複雑で洗練されたコントロールが追加されています。 多くの場合、これらのコントロールでは、通知メッセージと共に追加のデータを送信する必要があります。 Win32 API の設計者たちは、追加のデータを必要とする新しい通知ごとに新しい WM_* メッセージを追加するのではなく、標準化された方法で任意の量の追加データを渡すことができるメッセージである WM_NOTIFY を 1 つだけ追加することにしました。

WM_NOTIFY メッセージの wParam にはメッセージを送信するコントロールの ID が含まれており、また lParam には構造体へのポインターが含まれています。 この構造体は、NMHDR 構造体か、先頭のメンバーとして NMHDR 構造体を含む、いくぶん大きな構造体のいずれかとなります。 NMHDR メンバーが先頭にあるため、この構造体へのポインターは、キャスト方法に応じて、NMHDR へのポインター、またはより大きな構造体へのポインターのいずれかとして使用できることに注意してください。

ほとんどの場合、ポインターはより大きな構造体を指し、それを使用するときにキャストする必要があります。 実際のところ NMHDR 構造体が使用されるのは、共通の通知 (名前が NM_ で始まる) と、ツール ヒント コントロールの TTN_SHOW 通知や TTN_POP 通知など、いくつかの通知でのみです。

NMHDR 構造体または先頭のメンバーには、メッセージを送信するコントロールのハンドルと ID、および通知コード (TTN_SHOW など) が含まれます。 NMHDR 構造体の形式を次に示します。

typedef struct tagNMHDR {
    HWND hwndFrom;
    UINT idFrom;
    UINT code;
} NMHDR;

TTN_SHOW メッセージであれば、コード メンバーは TTN_SHOW に設定されます。

ほとんどの通知は、先頭のメンバーとして NMHDR 構造体を含むより大きな構造体へのポインターを渡します。 たとえば、リスト ビュー コントロールの LVN_KEYDOWN 通知メッセージで使用される構造体について考えてみましょう。これは、リスト ビュー コントロールでキーが押されたときに送信されます。 ポインターは LV_KEYDOWN 構造体を指します。これは、次に示すように定義されています。

typedef struct tagLV_KEYDOWN {
    NMHDR hdr;
    WORD wVKey;
    UINT flags;
} LV_KEYDOWN;

NMHDR メンバーはこの構造体の先頭にあるので、通知メッセージで渡されるポインターは、NMHDR へのポインターまたは LV_KEYDOWN へのポインターのいずれかにキャストできることに注意してください。

すべての新しい Windows コントロールに共通の通知

一部の通知は、すべての新しい Windows コントロールに共通しています。 これらの通知は、NMHDR 構造体へのポインターを渡します。

通知コード 送信理由
NM_CLICK ユーザーがコントロールでマウスの左ボタンをクリックした
NM_DBLCLK ユーザーがコントロールでマウスの左ボタンをダブルクリックした
NM_RCLICK ユーザーがコントロールでマウスの右ボタンをクリックした
NM_RDBLCLK ユーザーがコントロールでマウスの右ボタンをダブルクリックした
NM_RETURN コントロールに入力フォーカスがあるときにユーザーが Enter キーを押した
NM_SETFOCUS コントロールに入力フォーカスが与えられた
NM_KILLFOCUS コントロールから入力フォーカスが失われた
NM_OUTOFMEMORY メモリ不足のために、コントロールで操作を完了できなかった

ON_NOTIFY: MFC アプリケーションで WM_NOTIFY メッセージを処理する

関数 CWnd::OnNotify は、通知メッセージを処理します。 既定の実装では、呼び出す通知ハンドラーのメッセージ マップがチェックされます。 一般に、OnNotify はオーバーライドしません。 代わりに、ハンドラー関数を指定し、そのハンドラーのメッセージ マップ エントリをオーナー ウィンドウのクラスのメッセージ マップに追加します。

ClassWizard では、ClassWizard プロパティ シートを介して ON_NOTIFY メッセージ マップ エントリを作成し、スケルトン ハンドラー関数を提供することができます。 ClassWizard を使用してこれを簡単に行う方法の詳細については、「関数へのメッセージのマッピング」を参照してください。

ON_NOTIFY メッセージ マップ マクロの構文は次のとおりです。

ON_NOTIFY(wNotifyCode, id, memberFxn)

パラメーターは次のとおりです。

wNotifyCode
処理する通知メッセージ (LVN_KEYDOWN など) のコード。

id
通知が送信されるコントロールの子識別子。

memberFxn
この通知を送信したときに呼び出されるメンバー関数。

メンバー関数は、次のプロトタイプを使用して宣言する必要があります。

afx_msg void memberFxn(NMHDR* pNotifyStruct, LRESULT* result);

パラメーターは次のとおりです。

pNotifyStruct
通知構造体へのポインター (上記のセクションを参照)。

result
返す前に設定する結果コードへのポインター。

ID が IDC_LIST1 である CListCtrl からの LVN_KEYDOWN メッセージをメンバー関数 OnKeydownList1 で処理するように指定する場合は、ClassWizard を使用して次の情報をメッセージ マップに追加します。

ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, OnKeydownList1)

上の例では、ClassWizard によって提供される関数は次のようになります。

void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;

    // TODO: Add your control notification handler
    //       code here

    *pResult = 0;
}

ClassWizard によって、適切な型のポインターが自動的に提供されています。 通知構造体には、pNMHDR または pLVKeyDow を介してアクセスできます。

ON_NOTIFY_RANGE

一連のコントロールに対して同じ WM_NOTIFY メッセージを処理する必要がある場合は、ON_NOTIFY ではなく ON_NOTIFY_RANGE を使用できます。 たとえば、同じアクションを実行して特定の通知メッセージを送信する必要がある一連のボタンがあるとします。

ON_NOTIFY_RANGE を使用すると、範囲の先頭と末尾の子識別子を指定することで、通知メッセージを処理する必要がある連続した子識別子の範囲を指定できます。

ClassWizard では ON_NOTIFY_RANGE の処理は行われません。これを使用するには、自分でメッセージ マップを編集する必要があります。

ON_NOTIFY_RANGE 用のメッセージ マップのエントリと関数プロトタイプを次に示します。

ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn)

パラメーターは次のとおりです。

wNotifyCode
処理する通知メッセージ (LVN_KEYDOWN など) のコード。

id
連続した識別子の範囲の最初の識別子。

idLast
連続した識別子の範囲の末尾の識別子。

memberFxn
この通知を送信したときに呼び出されるメンバー関数。

メンバー関数は、次のプロトタイプを使用して宣言する必要があります。

afx_msg void memberFxn(UINT id, NMHDR* pNotifyStruct, LRESULT* result);

パラメーターは次のとおりです。

id
通知を送信したコントロールの子識別子。

pNotifyStruct
通知構造体へのポインター (上記を参照)。

result
返す前に設定する結果コードへのポインター。

ON_NOTIFY_EX、ON_NOTIFY_EX_RANGE

通知ルーティング内の複数のオブジェクトでメッセージを処理する場合は、ON_NOTIFY (または ON_NOTIFY_RANGE) ではなく ON_NOTIFY_EX (または ON_NOTIFY_EX_RANGE) を使用できます。 EX バージョンと通常バージョンの唯一の違いは、EX バージョンに対して呼び出されたメンバー関数が、メッセージ処理を続行するかどうかを示す BOOL を返す点にあります。 この関数から FALSE が返ってきた場合、複数のオブジェクトで同じメッセージを処理することができます。

ClassWizard では、ON_NOTIFY_EX や ON_NOTIFY_EX_RANGE の処理は行われません。どちらかを使用する場合は、自分でメッセージ マップを編集する必要があります。

ON_NOTIFY_EX および ON_NOTIFY_EX_RANGE 用のメッセージ マップ エントリと関数プロトタイプを次に示します。 パラメーターの意味は、非 EX バージョンの場合と同じです。

ON_NOTIFY_EX(nCode, id, memberFxn)
ON_NOTIFY_EX_RANGE(wNotifyCode, id, idLast, memberFxn)

上記の両方のプロトタイプは同じです。

afx_msg BOOL memberFxn(UINT id, NMHDR* pNotifyStruct, LRESULT* result);

いずれの場合も、id には通知を送信したコントロールの子識別子が保持されます。

関数では、通知メッセージが完全に処理されている場合は TRUE を返す必要があります。コマンド ルーティング内の他のオブジェクトがメッセージを処理する可能性がある場合は FALSE を返す必要があります。

関連項目

番号順テクニカル ノート
カテゴリ別テクニカル ノート