ダイアログベースのアプリケーションを、[Esc]キーで終了しないようにする

Visual C++プログラミングのメモ

【Visual C++ 2008】【MFC】

 「MFCアプリケーションウィザード」にて「ダイアログベース」のアプリケーションを生成した場合、 そのアプリケーションの終了方法には以下のパターンがある。

操作終了時に通るCDialogメンバ関数DoModalの戻り値終了コード
[OK]ボタン (IDOK)OnOK0x1 (IDOK)0x0
[Enter]キー
(IDOKボタン無し、IDCANCELボタンにフォーカス無し)
OnOK0x1 (IDOK)0xD (VK_RETURN)
[キャンセル]ボタン (IDCANCEL)OnCancel0x2 (IDCANCEL)0x0
[システムメニュー]-[閉じる]OnCancel0x2 (IDCANCEL)0x2
[システムメニュー]ダブルクリックOnCancel0x2 (IDCANCEL)0x2
[X]ボタンOnCancel0x2 (IDCANCEL)0x2
Alt+F4OnCancel0x2 (IDCANCEL)0x2
[Esc]キーOnCancel0x2 (IDCANCEL)0x1B (VK_ESCAPE)

 [Esc]キー押下でアプリケーション終了してしまうのは、Windowsの作法として違和感がある。 おそらく、通常のダイアログ(ポップアップ)の作法を引きずっているためだろう。 そこで、[Esc]キーをアクセラレータ(ショートカットキー)として設定して、押下しても何も反応しないようにする。

(1)
 リソースビューにて、アクセラレータを追加する(例えばIDR_ACCELERATOR1)。 IDは任意(例えばID_ESC)、修飾子は「なし」、キーは「VK_ESCAPE」。 もしくは、rcファイルを直接書き換えるなら、以下を追加。


IDR_ACCELERATOR1 ACCELERATORS
BEGIN
    VK_ESCAPE,      ID_ESC,                 VIRTKEY, NOINVERT
END

(2)
 ダイアログクラスのクラスヘッダのprivateに、メンバ定義を追加。


    HACCEL m_hAccel;
    virtual BOOL PreTranslateMessage(MSG* pMsg);

(3)
 OnInitDialogの「// TODO: 初期化をここに追加します。」より後にて、アクセラレータを読み込む。


    m_hAccel = LoadAccelerators(AfxGetResourceHandle(),
        MAKEINTRESOURCE(IDR_ACCELERATOR1));

(4)
 メンバ関数(CWnd::PreTranslateMessageのオーバーライド)を記述。


BOOL CXxxxDlg::PreTranslateMessage(MSG* pMsg)
{
    if (::TranslateAccelerator(GetSafeHwnd(), m_hAccel, pMsg))
    {
        // アクセラレータの場合、こちらを通る
        return TRUE;
    }

    return CDialog::PreTranslateMessage(pMsg);
}

 単に[Esc]キーを無視するだけなら、ここまででよい。 [Esc]キーに何らかの動作を割り当てる場合、引き続き(5)以降も記述する。

(5)
 ダイアログクラスのクラスヘッダのprivateに、メンバ関数定義を追加。 例えば関数名をOnEscとする場合、


    afx_msg void OnEsc();

(6)
 メッセージマップに、ON_COMMAND(ID_ESC, OnEsc)を追加。 これにより、キーのIDと、その行き先が結びつく。

(7)
 メンバ関数を記述。


void CXxxxDlg::OnEsc()
{
    // [Esc]キーの動作を、ここに記述。
    // 何もしない場合は、returnのみ。

    return;
}

 [Esc]キー以外のキーをアクセラレータに設定する場合は、(1)にそのキーを追加して、そのキー用の(5)~(7)を実装する。

 ただし、冒頭に挙げた表のIDOKに対応する操作、および、IDCANCELに対応する操作にアプリケーション終了前の動作を追加する場合は、 それぞれCDialog::OnOK、CDialog::OnCancelのオーバーライドのみでよい。 アクセラレータの設定は不要。 メッセージマップには、デフォルトで内部的に以下の定義がされていると考えると、わかりやすい。(実際は記述不要)


ON_COMMAND(IDOK, OnOK)
ON_COMMAND(IDCANCEL, OnCancel)

 [Esc]キーもデフォルトではIDCANCELに対応する操作であるが、アクセラレータ設定が優先される。

(メモ)

  • [Enter]キーで終了しないようにするには、IDOKボタンを削除して、OnOKのオーバーライドにてreturn返しする (他のボタンにフォーカスが無い場合)
  • CDialogはCWndの派生クラス
  • MSDNライブラリの「CDialog::OnOK」「CDialog::OnCancel」「CWnd::PreTranslateMessage」「TranslateAccelerator」「LoadAccelerators」を参照のこと