次の方法で共有


TN061: メッセージのON_NOTIFYとWM_NOTIFY

次のテクニカル ノートは、最初にオンライン ドキュメントに含まれてから更新されていません。 その結果、一部の手順やトピックが古くなっているか、正しくない可能性があります。 最新情報については、オンライン ドキュメント インデックスで関心のあるトピックを検索することをお勧めします。

このテクニカル ノートでは、新しい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など、さまざまな特殊なメッセージを使用します。 これらのメッセージは、送信したコントロールに反映できます。 詳細については、「 TN062: Windows コントロールのメッセージ リフレクション」を参照してください。

Win32 の通知メッセージ

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

WM_NOTIFYメッセージには、 wParam でメッセージを送信するコントロールの ID と 、lParam 内の構造体へのポインターが含まれます。 この構造体は、 NMHDR 構造体か、最初のメンバーとして NMHDR 構造体を持ついくつかの大きな構造体です。 NMHDR メンバーが最初であるため、この構造体へのポインターは、NMHDR へのポインターとして、またはキャスト方法に応じて、より大きな構造体へのポインターとして使用できます。

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

NMHDR 構造体または初期メンバーには、メッセージと通知コード (TTN_SHOW など) を送信するコントロールのハンドルと ID が含まれています。 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 プロパティ シートを使用して、ON_NOTIFYメッセージ マップ エントリを作成し、スケルトン ハンドラー関数を提供できます。 これを容易にするために ClassWizard を使用する方法の詳細については、「 関数へのメッセージのマッピング」を参照してください。

ON_NOTIFY メッセージ マップ マクロには、次の構文があります。

ON_NOTIFY(wNotifyCode, id, memberFxn)

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

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

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

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

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

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

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

pNotifyStruct
上記のセクションで説明したように、通知構造へのポインター。

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

メンバー関数OnKeydownList1 ID がIDC_LIST1されているCListCtrlからのLVN_KEYDOWNメッセージを処理するように指定するには、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
前述のように、通知構造へのポインター。

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

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 を返す必要があります。

こちらも参照ください

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