「MFCの基礎 - キーボード」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

編集の要約なし
編集の要約なし
 
1行目: 1行目:
== 概要 ==
== 概要 ==
MFCにおけるキーボードに関する処理を記載する。<br>
MFCアプリケーションにおけるキーボード処理は、Windowsのメッセージドリブン設計に基づいて実装される。<br>
<br><br>
<br>
 
キーボード入力は、WM_KEYDOWN、WM_KEYUP、WM_CHARなどのWindowsメッセージとして処理される。<br>
== 仮想キーの取得 ==
MFCは、これらのメッセージを処理するための便利なメッセージハンドラ (OnKeyDown、OnKeyUp、OnChar等) を提供している。<br>
仮想キーには、[Shift]キー、[Ctrl]キー、[Alt]キー等がある。<br>
<br>
* キーボードメッセージの種類と処理フロー
* 仮想キーコードと文字コードの違い
* GetKeyState関数とGetAsyncKeyState関数によるキー状態の取得
* MFCのキーボードイベントハンドラ
* アクセラレータキーとホットキーの登録
* キーボード入力のシミュレーション
<br>
<br>
仮想キーを取得するには、GetKeyState関数を使用する。<br>
仮想キーコードは、キーボードのキー自体を識別するためのコードであり、<code>VK_RETURN</code>、<code>VK_SHIFT</code> 等の定数で表現される。<br>
<syntaxhighlight lang="c++">
一方、文字コードは、キー入力によって生成される文字を表すコードであり、<code>WM_CHAR</code> メッセージで取得できる。<br>
SHORT GetKeyState(
                    int  nVirtKey  // 仮想キーコード
);
</syntaxhighlight>
<br>
<br>
以下の例では、定数<code>VK_SHIFT</code>を使用して、[Shift]キーか押下されているかどうかを確認している。<br>
MFCのキーボード処理を理解することにより、以下に示す機能を実装できる。<br>
また、[Ctrl]キーの状態を調べる場合は定数<code>VK_CONTROL</code>、[Alt]キーの状態を調べる場合は定数<code>VK_MENU</code>を使用する。<br>
<syntaxhighlight lang="c++">
if(GetKeyState(VK_SHIFT) < 0)
{  // [Shift]キーが押下されている場合
}
else
{  // [Shift]キーが押下されていない場合
}
</syntaxhighlight>
<br>
<br>
<u>※注意</u><br>
* ショートカットキーの実装
<u>数字キーの仮想キー定数は、WinUser.hには定義されていないため、<code>0x30</code>~<code>0x39</code>を使用する。</u><br>
* ゲームや対話型アプリケーションのキー入力処理
<u>同様に、英字キーの仮想キー定数は、WinUser.hには定義されていないため、<code>0x41</code>~<code>0x5A</code>を使用する。(必ず大文字を使用すること)</u><br>
* カスタムキーバインディング
* キーボードマクロとオートメーション
<br><br>
 
== キーボードメッセージ ==
Windowsは、キーボード入力を複数のメッセージとして処理する。<br>
<br>
<br>
下表に、仮想キーの定数を示す。<br>
<center>
<center>
{| class="wikitable" style="background-color:#fefefe;"
{| class="wikitable"
|+ 仮想キーの定数
|+ 主要なキーボードメッセージ
|-
|-
! style="background-color:#66CCFF;" | 定数
! メッセージ !! 説明
! style="background-color:#66CCFF;" | 意味
|-
|-
! style="background-color:#66CCFF;" colspan="2" | マウス
| WM_KEYDOWN / WM_KEYUP || キーが押下された時と離された時に送信される。
|-
|-
|VK_LBUTTON || マウスの左ボタン
| WM_CHAR || 文字入力時に送信される。<br>(TranslateMessage関数による変換後)
|-
|-
|VK_RBUTTON || マウスの右ボタン
| WM_SYSKEYDOWN / WM_SYSKEYUP || Altキーと組み合わせたシステムキー入力時に送信される。
|}
</center>
<br>
キーボードメッセージの処理フローを理解することは、適切なキーボード処理の実装に不可欠である。<br>
<br>
==== WM_KEYDOWN / WM_KEYUP ====
* WM_KEYDOWNメッセージ
*: キーが押下された時に送信される。
* WM_KEYUPメッセージ
*: キーが離された時に送信される。
<br>
<u>※注意</u><br>
<u>WM_KEYDOWN と WM_KEYUP は、文字入力ではなく、キーの物理的な押下と解放を検出する場合に使用する。</u><br>
<br>
<center>
{| class="wikitable"
|+ メッセージパラメータ
|-
|-
|VK_MBUTTON || マウスの中央ボタン
! パラメータ !! 説明
|-
|-
|VK_XBUTTON1 || マウスの左サイドボタン(Windows 2000/XP以降)
| wParam || 仮想キーコード (VK_RETURN、VK_ESCAPE等)
|-
|-
|VK_XBUTTON2 || マウスの右サイドボタン(Windows 2000/XP以降)
| lParam || キーの詳細情報 (リピートカウント、スキャンコード、拡張キーフラグ等)
|}
</center>
<br>
<center>
{| class="wikitable"
|+ lParamの構造
|-
|-
! style="background-color:#66CCFF;" colspan="2" |シフトキー
! ビット !! 説明
|-
|-
|VK_SHIFT || [Shift]
| 0〜15 || リピートカウント
|-
|-
|VK_CONTROL || [Ctrl]
| 16〜23 || スキャンコード
|-
|-
|VK_MENU || [Alt]
| 24 || 拡張キーフラグ (1の場合、拡張キー)
|-
|-
|VK_NUMLOCK || [Num Lock]
| 29 || コンテキストコード (Altキーが押されている場合は1)
|-
|-
|VK_CAPITAL || [Caps Lock]
| 30 || 前のキー状態 (既に押されていた場合は1)
|-
|-
|VK_SCROLL || [Scroll Lock]
| 31 || 遷移状態 (WM_KEYUPの場合は1)
|}
</center>
<br>
==== WM_CHAR ====
* WM_CHARメッセージ
*: 文字入力時に送信される。
<br>
WM_CHARメッセージは、<code>TranslateMessage</code> 関数により <code>WM_KEYDOWN</code> メッセージから生成される。<br>
<code>TranslateMessage</code> 関数は、仮想キーコードを文字コードに変換する。<br>
<br>
<center>
{| class="wikitable"
|+ メッセージパラメータ
|-
|-
! style="background-color:#66CCFF;" colspan="2" |左右シフトキー
! パラメータ !! 説明
|-
|-
|VK_LSHIFT || 左[Shift]
| wParam || 文字コード (ASCII文字やUnicode文字)
|-
|-
|VK_RSHIFT || [Shift]
| lParam || キーの詳細情報 (WM_KEYDOWNと同様)
|}
</center>
<br>
WM_CHARメッセージは、テキスト入力を処理する場合に使用する。<br>
例えば、エディットコントロールやカスタムテキストエディタの実装に利用する。<br>
<br>
また、WM_CHARメッセージは、Shift、Ctrl、Altキーの状態を考慮した文字コードを提供する。<br>
例えば、[Shift]キー + [A]キーを同時押下する時、wParamは"A"の文字コードとなる。<br>
<br>
==== WM_SYSKEYDOWN / WM_SYSKEYUP ====
WM_SYSKEYDOWNメッセージ と WM_SYSKEYUPメッセージは、[Alt]キーと組み合わせたキー入力時に送信される。<br>
<br>
これらのメッセージは、システムキーの処理に使用される。<br>
システムキーには、[Alt] + [F4] (ウィンドウを閉じる)、[Alt] + [Tab] (タスク切り替え) 等が含まれる。<br>
<br>
メッセージパラメータは、WM_KEYDOWN と WM_KEYUPと同様である。<br>
<br>
WM_SYSKEYDOWN と WM_SYSKEYUPを処理する場合、通常は <code>DefWindowProc</code> 関数に渡して、デフォルトのシステム動作を維持する。<br>
カスタム処理が必要な場合のみ、これらのメッセージを独自に処理する。<br>
<br>
==== メッセージの処理フロー ====
キーボードメッセージの処理フローを以下に示す。<br>
<br>
# ユーザがキーを押下する。
# Windowsは、WM_KEYDOWNメッセージを生成する。
# アプリケーションのメッセージループは、TranslateMessage関数を呼び出す。
# TranslateMessage関数は、WM_KEYDOWNからWM_CHARメッセージを生成する。(文字キーの場合)
# DispatchMessage関数は、メッセージをウィンドウプロシージャに送信する。
# ウィンドウプロシージャは、メッセージハンドラを呼び出す。
<br>
この処理フローを理解することにより、適切なメッセージハンドラを選択できる。<br>
<br>
文字入力を処理する場合は、WM_CHARメッセージを使用する。<br>
キーの物理的な押下を検出する場合は、WM_KEYDOWNメッセージを使用する。<br>
<br><br>
 
== 仮想キーの取得 ==
<code>GetKeyState</code> 関数は、特定の仮想キーの状態を取得する関数である。<br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
SHORT GetKeyState(int nVirtKey);
</syntaxhighlight>
<br>
<center>
{| class="wikitable"
|+ 引数 (パラメータ) と 戻り値
|-
|-
|VK_LCONTROL || 左[Ctrl]
! 種別 !! 項目 !! 説明
|-
|-
|VK_RCONTROL || 右[Ctrl]
| 引数 (パラメータ) || nVirtKey || 仮想キーコード (VK_SHIFT、VK_CONTROL等)
|-
|-
|VK_LMENU || 左[Alt]
| rowspan="2" | 戻り値 || 最上位ビット (ビット15) || キーが押されている場合は1、それ以外は0
|-
|-
|VK_RMENU || [Alt]
| 最下位ビット (ビット0) || キーのトグル状態 (CapsLockやNumLockの場合、ONの時は1)
|}
</center>
<br>
使用例を以下に示す。<br>
<syntaxhighlight lang="c++">
// Shiftキーが押されているか確認
if (GetKeyState(VK_SHIFT) & 0x8000) {
    // [Shift]キーが押下されている場合
}
// CapsLockがONか確認
if (GetKeyState(VK_CAPITAL) & 0x0001) {
    // [CapsLock]がONの状態
}
</syntaxhighlight>
<br>
下表に、主要な仮想キー定数を示す。<br>
<br>
<center>
{| class="wikitable"
|+ 主要な仮想キー定数
! 定数 !! 説明
|-
|-
! style="background-color:#66CCFF;" colspan="2" |IMEモード
| VK_SHIFT || Shiftキー
|-
|-
|VK_KANA || IMEかなモード
| VK_CONTROL || Ctrlキー
|-
|-
|VK_HANGEUL<br>VK_HANGUL || IMEハングル・モード
| VK_MENU || Altキー
|-
|-
|VK_JUNJA || IEM
| VK_LSHIFT || 左Shiftキー
|-
|-
|VK_FINAL || IEM
| VK_RSHIFT || 右Shiftキー
|-
|-
|VK_HANJA || IEM
| VK_LCONTROL || 左Ctrlキー
|-
|-
|VK_KANJI || IME漢字モード
| VK_RCONTROL || 右Ctrlキー
|-
|-
|VK_CONVERT || IME変換[XFER]
| VK_LMENU || 左Altキー
|-
|-
|VK_NONCONVERT || IME無変換[NFER]
| VK_RMENU || 右Altキー
|}
</center>
<br>
<center>
{| class="wikitable"
|+ 矢印キーと編集キー
! 定数 !! 説明
|-
|-
|VK_ACCEPT || IME
| VK_LEFT || 左矢印キー
|-
|-
|VK_MODECHANGE || IMEモード変更
| VK_UP || 上矢印キー
|-
|-
! style="background-color:#66CCFF;" colspan="2" |特殊キー
| VK_RIGHT || 右矢印キー
|-
|-
|VK_CANCEL || [Ctrl] + [Break]
| VK_DOWN || 下矢印キー
|-
|-
|VK_ESCAPE || [Esc]
| VK_INSERT || Insertキー
|-
|-
|VK_TAB || [Tab]
| VK_DELETE || Deleteキー
|-
|-
|VK_BACK || [Back Space]
| VK_HOME || Homeキー
|-
|-
|VK_SPACE || スペース
| VK_END || Endキー
|-
|-
|VK_RETURN || [Enter]
| VK_PRIOR || Page Upキー
|-
|-
|VK_CLEAR || [Clear]とは[Num Lock]を外した状態のテン[5]
| VK_NEXT || Page Downキー
|}
</center>
<br>
<center>
{| class="wikitable"
|+ 特殊キー
! 定数 !! 説明
|-
|-
|VK_PAUSE || [Pause]
| VK_RETURN || Enterキー
|-
|-
|VK_PRINT || [Print]
| VK_ESCAPE || Escapeキー
|-
|-
|VK_SELECT || [Select]
| VK_SPACE || Spaceキー
|-
|-
|VK_EXECUTE || [Execute]
| VK_TAB || Tabキー
|-
|-
|VK_SNAPSHOT || [Print Screen]
| VK_BACK || Backspaceキー
|-
|-
|VK_LWIN || 左のSuperキー
| VK_CAPITAL || CapsLockキー
|-
|-
|VK_RWIN || 右のSuperキー
| VK_NUMLOCK || NumLockキー
|-
|-
|VK_APPS || Applicationsキー(アプリケーション・キー)ポップアップ・メニューが開く
| VK_SCROLL || ScrollLockキー
|}
</center>
<br>
<center>
{| class="wikitable"
|+ ファンクションキー
! 定数 !! 説明
|-
|-
|VK_SLEEP || スリープキー
| VK_F1 || F1キー
|-
|-
! style="background-color:#66CCFF;" colspan="2" |移動キー
| VK_F2 || F2キー
|-
|-
|VK_UP || [↑]
| VK_F3 || F3キー
|-
|-
|VK_DOWN || [↓]
| VK_F4 || F4キー
|-
|-
|VK_LEFT || [←]
| VK_F5 || F5キー
|-
|-
|VK_RIGHT || [→]
| VK_F6 || F6キー
|-
|-
|VK_PRIOR || [Page Up]
| VK_F7 || F7キー
|-
|-
|VK_NEXT || [Page Down]
| VK_F8 || F8キー
|-
|-
|VK_HOME || [Home]
| VK_F9 || F9キー
|-
|-
|VK_END || [End]
| VK_F10 || F10キー
|-
|-
|VK_HELP || [Help]
| VK_F11 || F11キー
|-
|-
|VK_INSERT || [Insert]
| VK_F12 || F12キー
|}
</center>
<br>
<code>GetKeyState</code> 関数は、現在のスレッドのメッセージキュー内の最後のメッセージの時点でのキー状態を返す。<br>
リアルタイムのキー状態を取得する場合は、<code>GetAsyncKeyState</code> 関数を使用する。<br>
<br><br>
 
== GetAsyncKeyState関数 ==
<code>GetAsyncKeyState</code> 関数は、リアルタイムのキー状態を取得する関数である。<br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
SHORT GetAsyncKeyState(int vKey);
</syntaxhighlight>
<br>
パラメータと戻り値は、<code>GetKeyState</code> 関数と同様である。<br>
<br>
==== GetKeyState関数との違い ====
GetKeyState関数とGetAsyncKeyState関数の違いを以下に示す。<br>
<br>
<center>
{| class="wikitable"
|+ GetKeyState関数 と GetAsyncKeyState関数 の比較
! 項目 !! GetKeyState関数 !! GetAsyncKeyState関数
|-
|-
|VK_DELETE || [Delete]
| 取得タイミング || メッセージキュー内の最後のメッセージ時点 || 関数呼び出し時点 (リアルタイム)
|-
|-
! style="background-color:#66CCFF;" colspan="2" |テンキー
| 用途 || メッセージハンドラ内での使用 || ゲームループや非同期処理での使用
|-
|-
|VK_NUMPAD0 || テンキーの[0]
| 同期性 || 同期的 (メッセージキューと同期) || 非同期的 (現在の物理的なキー状態)
|-
|-
|VK_NUMPAD1 || テンキーの[1]
| マルチスレッド || スレッドごとに独立 || システム全体で共有
|}
</center>
<br>
* GetKeyState関数
*: メッセージドリブンな処理に適している。
*: メッセージハンドラ内で使用することにより、メッセージが生成された時点のキー状態を正確に取得できる。
*: <br>
* GetAsyncKeyState関数
*: リアルタイムなキー状態の検出に適している。
*: ゲームのメインループや、定期的なタイマー処理でキー状態をポーリングする場合に使用する。
<br>
==== 使用例 ====
GetAsyncKeyState関数の使用例を以下に示す。<br>
<br>
<syntaxhighlight lang="c++">
// ゲームループ内でのキー状態チェック
void CGameView::OnTimer(UINT_PTR nIDEvent)
{
    // 矢印キーで移動制御
    if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
      m_playerX -= 5;  // 左に移動
    }
    if (GetAsyncKeyState(VK_RIGHT) & 0x8000) {
      m_playerX += 5;  // 右に移動
    }
    if (GetAsyncKeyState(VK_UP) & 0x8000) {
      m_playerY -= 5;  // 上に移動
    }
    if (GetAsyncKeyState(VK_DOWN) & 0x8000) {
      m_playerY += 5;  // 下に移動
    }
    // [Space]キーでジャンプ
    if (GetAsyncKeyState(VK_SPACE) & 0x8000) {
      Jump();
    }
    Invalidate();  // 再描画
    CView::OnTimer(nIDEvent);
}
</syntaxhighlight>
<br>
この例では、タイマーイベント内で定期的にキー状態をチェックし、プレイヤーの位置を更新している。<br>
<br>
GetAsyncKeyState関数は、複数のキーの同時押しを検出する場合にも便利である。<br>
<syntaxhighlight lang="c++">
// [Ctrl] + [S]キーの同時押下を検出
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && (GetAsyncKeyState('S') & 0x8000)) {
    SaveDocument();
}
</syntaxhighlight>
<br><br>
 
== MFCでのキーボードイベントハンドラ ==
MFCは、キーボードメッセージを処理するための便利なメッセージハンドラを提供している。<br>
<br>
<center>
{| class="wikitable"
|+ 主要なキーボードイベントハンドラ
|-
|-
|VK_NUMPAD2 || テンキーの[2]
! メソッド !! 説明
|-
|-
|VK_NUMPAD3 || テンキーの[3]
| OnKeyDown / OnKeyUp || WM_KEYDOWN / WM_KEYUPメッセージの処理
|-
|-
|VK_NUMPAD4 || テンキーの[4]
| OnChar || WM_CHARメッセージの処理
|-
|-
|VK_NUMPAD5 || テンキーの[5]
| PreTranslateMessage || メッセージの前処理 (メッセージがDispatchされる前に介入)
|}
</center>
<br>
これらのハンドラを適切に使用することにより、柔軟なキーボード処理を実装できる。<br>
<br>
==== OnKeyDown / OnKeyUp ====
<code>OnKeyDown</code> イベントハンドラ と <code>OnKeyUp</code> イベントハンドラ は、キーの押下と解放を処理するハンドラである。<br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
</syntaxhighlight>
<br>
<center>
{| class="wikitable"
|+ 引数 (パラメータ)
|-
|-
|VK_NUMPAD6 || テンキーの[6]
! 項目 !! 説明
|-
|-
|VK_NUMPAD7 || テンキーの[7]
| nChar || 仮想キーコード
|-
|-
|VK_NUMPAD8 || テンキーの[8]
| nRepCnt || リピートカウント (キーが押下され続けている場合の繰り返し回数)
|-
|-
|VK_NUMPAD9 || テンキーの[9]
| nFlags || キーの詳細情報 (拡張キーフラグ、コンテキストコード等)
|}
</center>
<br>
OnKeyDownハンドラを使用することにより、特定のキーに対する処理を簡潔に実装できる。<br>
<br>
使用例を以下に示す。<br>
<syntaxhighlight lang="c++">
void CMyView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    switch (nChar)
    {
    case VK_LEFT:
      // [左矢印]キーが押下された場合
      MoveLeft();
      break;
    case VK_RIGHT:
      // [右矢印]キーが押下された場合
      MoveRight();
      break;
    case VK_ESCAPE:
      // [Esc]キーが押下された場合
      CancelOperation();
      break;
    case VK_F1:
      // [F1]キーが押下された場合 (ヘルプ表示)
      ShowHelp();
      break;
    default:
      CView::OnKeyDown(nChar, nRepCnt, nFlags);
      break;
    }
}
</syntaxhighlight>
<br>
==== OnChar ====
<code>OnChar</code> イベントハンドラは、文字入力を処理するハンドラである。<br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
</syntaxhighlight>
<br>
<center>
{| class="wikitable"
|+ パラメータ
|-
|-
|VK_MULTIPLY || テンキーの[*]
! 項目 !! 説明
|-
|-
|VK_ADD || テンキーの[+]
| nChar || 文字コード (ASCII文字やUnicode文字)
|-
|-
|VK_SEPARATOR || テンキーの[,]
| nRepCnt || リピートカウント
|-
|-
|VK_SUBTRACT || テンキーの[-]
| nFlags || キーの詳細情報
|}
</center>
<br>
OnCharハンドラは、テキスト入力を処理する場合に使用する。<br>
Shift、Ctrl、Altキーの状態を考慮した文字コードが提供されるため、文字入力処理が簡潔になる。<br>
<br>
使用例を以下に示す。<br>
<syntaxhighlight lang="c++">
void CMyView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (nChar == VK_RETURN) {
      // [Enter]キーが押下された場合
      ProcessInput();
    }
    else if (nChar == VK_ESCAPE) {
      // [Esc]キーが押下された場合
      CancelInput();
    }
    else if (nChar >= 32) {
      // 印字可能文字の場合
      // 文字を入力バッファに追加
      m_inputBuffer += (TCHAR)nChar;
      Invalidate();
    }
    CView::OnChar(nChar, nRepCnt, nFlags);
}
</syntaxhighlight>
<br>
==== PreTranslateMessageメソッド ====
<code>PreTranslateMessage</code> メソッドは、メッセージがディスパッチされる前に呼び出されるメソッドである。<br>
このメソッドをオーバーライドすることにより、メッセージの前処理やフィルタリングを実装できる。<br>
<br>
ディスパッチ (Dispatch) とは、メッセージを適切なウィンドウプロシージャに転送することを意味する。<br>
Windowsのメッセージループでは、一般的に以下に示す流れで処理される。<br>
# GetMessage関数
#: メッセージキューからメッセージを取得する。
# TranslateMessage関数
#: キーボードメッセージを文字メッセージに変換する。
# DispatchMessage関数
#: メッセージを対象ウィンドウのウィンドウプロシージャ (WndProc) に転送する。
<br>
<u>PreTranslateMessageメソッドは、このDispatchMessageが呼ばれる前の段階で介入できるメソッドである。</u><br>
<u>このメソッドがTRUEを返す場合、メッセージはウィンドウプロシージャに転送されずに、破棄または独自処理される。</u><br>
<u>これにより、特定のキー入力を横取りしてショートカットキーとして処理する等の実装が可能になる。</u><br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
virtual BOOL PreTranslateMessage(MSG* pMsg);
</syntaxhighlight>
<br>
<center>
{| class="wikitable"
|+ 引数 (パラメータ) と 戻り値
|-
|-
|VK_DECIMAL || テンキーの[.]
! 種別 !! 項目 !! 説明
|-
|-
|VK_DIVIDE || テンキーの[/]
| パラメータ || pMsg || メッセージ構造体へのポインタ
|-
|-
! style="background-color:#66CCFF;" colspan="2" |ファンクションキー
| rowspan="2" | 戻り値 || TRUE || メッセージを処理済み (DispatchMessageに渡さない)
|-
|-
|VK_F1 || [F1]
| FALSE || メッセージを通常通り処理 (DispatchMessageに渡す)
|}
</center>
<br>
使用例を以下に示す。<br>
<syntaxhighlight lang="c++">
BOOL CMyDialog::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message == WM_KEYDOWN) {
      // [Enter]キーの処理を無効化 (ダイアログを閉じないようにする)
      if (pMsg->wParam == VK_RETURN) {
          // カスタム処理
          OnEnterKey();
          return TRUE;  // メッセージを消費
      }
      // [Esc]キーの処理を無効化
      if (pMsg->wParam == VK_ESCAPE) {
          // カスタム処理
          OnEscapeKey();
          return TRUE;  // メッセージを消費
      }
      // [Tab]キーで次のコントロールに移動 (カスタム順序)
      if (pMsg->wParam == VK_TAB) {
          FocusNextControl();
          return TRUE;  // メッセージを消費
      }
    }
    return CDialog::PreTranslateMessage(pMsg);
}
</syntaxhighlight>
<br>
<u>PreTranslateMessageメソッドは、ダイアログのデフォルト動作を変更する場合に特に有用である。</u><br>
<br>
例えば、ダイアログでは、[Enter]キーを押下するとOKボタンが押下され、[Esc]キーを押下するとキャンセルボタンが押下されるデフォルト動作がある。<br>
PreTranslateMessageメソッドをオーバーライドすることにより、この動作をカスタマイズできる。<br>
<br><br>
 
== アクセラレータキー ==
アクセラレータキー (ショートカットキー) は、メニューコマンドをキーボードから直接実行する機能である。<br>
<br>
MFCでは、リソースを使用した実装と、動的な実装の2つの方法がある。<br>
<br>
==== リソースを使用した実装 ====
リソースエディタを使用してアクセラレータテーブルを定義する方法を以下に示す。<br>
<br>
===== ステップ 1 : リソースファイルの編集 =====
リソースファイル (.rc) にアクセラレータテーブルを追加する。<br>
<br>
<syntaxhighlight lang="c++">
IDR_MAINFRAME ACCELERATORS
BEGIN
    "N",      ID_FILE_NEW,      VIRTKEY, CONTROL  // Ctrl+N
    "O",      ID_FILE_OPEN,      VIRTKEY, CONTROL  // Ctrl+O
    "S",      ID_FILE_SAVE,      VIRTKEY, CONTROL  // Ctrl+S
    VK_F1,    ID_HELP,          VIRTKEY          // F1
    VK_F5,    ID_VIEW_REFRESH,  VIRTKEY          // F5
    "Q",      ID_APP_EXIT,      VIRTKEY, CONTROL  // Ctrl+Q
END
</syntaxhighlight>
<br>
===== ステップ 2 : アクセラレータテーブルのロード =====
アプリケーションクラスでアクセラレータテーブルをロードする。<br>
<br>
<syntaxhighlight lang="c++">
BOOL CMyApp::InitInstance()
{
    // アクセラレータテーブルのロード
    m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(),
                                    MAKEINTRESOURCE(IDR_MAINFRAME));
    // メインウィンドウの作成など
    // ...
    return TRUE;
}
</syntaxhighlight>
<br>
===== ステップ 3 : メッセージループでの処理 =====
メッセージループでTranslateAccelerator関数を呼び出す。<br>
<br>
MFCでは、CWinAppクラスが自動的にアクセラレータテーブルを処理するため、通常は追加のコードは不要である。<br>
<br>
===== ステップ 4 : コマンドハンドラの実装 =====
各コマンドIDに対するハンドラを実装する。<br>
<br>
<syntaxhighlight lang="c++">
void CMainFrame::OnFileNew()
{
    // [Ctrl] + [N]キーが押下された時の処理
    AfxMessageBox(_T("新規ファイル作成"));
}
void CMainFrame::OnFileOpen()
{
    // [Ctrl] + [O]キーが押下された時の処理
    CFileDialog dlg(TRUE);
    if (dlg.DoModal() == IDOK) {
      OpenFile(dlg.GetPathName());
    }
}
</syntaxhighlight>
<br>
==== 動的なアクセラレータ作成 ====
実行時に動的にアクセラレータテーブルを作成する方法を以下に示す。<br>
<br>
<syntaxhighlight lang="c++">
void CMainFrame::CreateDynamicAccelerators()
{
    // アクセラレータテーブルの定義
    ACCEL accel[3];
    // [Ctrl] + [N]キー
    accel[0].fVirt = FCONTROL | FVIRTKEY;
    accel[0].key = 'N';
    accel[0].cmd = ID_FILE_NEW;
    // [Ctrl] + [O]キー
    accel[1].fVirt = FCONTROL | FVIRTKEY;
    accel[1].key = 'O';
    accel[1].cmd = ID_FILE_OPEN;
    // [F5]キー
    accel[2].fVirt = FVIRTKEY;
    accel[2].key = VK_F5;
    accel[2].cmd = ID_VIEW_REFRESH;
    // アクセラレータテーブルの作成
    m_hAccelTable = CreateAcceleratorTable(accel, 3);
    // 既存のアクセラレータテーブルを破棄
    if (m_hOldAccelTable != NULL) {
      DestroyAcceleratorTable(m_hOldAccelTable);
    }
}
</syntaxhighlight>
<br>
動的なアクセラレータ作成は、ユーザがキーバインディングをカスタマイズできる機能を実装する場合に有用である。<br>
<br><br>
 
== ホットキーの登録 ==
ホットキーは、アプリケーションがフォーカスを持っていない場合でも検出できるグローバルなキーボードショートカットである。<br>
<br>
<code>RegisterHotKey</code> 関数を使用して、システム全体で有効なホットキーを登録できる。<br>
<br>
==== RegisterHotKey関数 ====
RegisterHotKey関数は、ホットキーを登録する関数である。<br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
BOOL RegisterHotKey(
    HWND hWnd,
    int id,
    UINT fsModifiers,
    UINT vk
);
</syntaxhighlight>
<br>
<center>
{| class="wikitable"
|+ 引数 (パラメータ)
|-
|-
|VK_F2 || [F2]
! 項目 !! 説明
|-
|-
|VK_F3 || [F3]
| hWnd || ホットキーが押下された時にWM_HOTKEYメッセージを受け取るウィンドウのハンドル
|-
|-
|VK_F4 || [F4]
| id || ホットキーの識別子 (0x0000〜0xBFFFの範囲)
|-
|-
|VK_F5 || [F5]
| fsModifiers || 修飾キー (MOD_ALT、MOD_CONTROL、MOD_SHIFT、MOD_WIN)
|-
|-
|VK_F6 || [F6]
| vk || 仮想キーコード
|}
</center>
<br>
使用例を以下に示す。<br>
<syntaxhighlight lang="c++">
// [Ctrl] + [Alt] + [F12]キーをホットキーとして登録
BOOL CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1) {
      return -1;
    }
    // ホットキーの登録 (ID: 1)
    if (!RegisterHotKey(m_hWnd, 1, MOD_CONTROL | MOD_ALT, VK_F12)) {
      AfxMessageBox(_T("ホットキーの登録に失敗しました"));
    }
    return 0;
}
// ホットキーの解除
void CMainFrame::OnDestroy()
{
    UnregisterHotKey(m_hWnd, 1);
    CFrameWnd::OnDestroy();
}
</syntaxhighlight>
<br>
==== WM_HOTKEYメッセージの処理 ====
ホットキーが押下されると、WM_HOTKEYメッセージが送信される。<br>
<br>
メッセージハンドラの実装を以下に示す。<br>
<syntaxhighlight lang="c++">
// ヘッダファイルでメッセージマップを宣言
class CMainFrame : public CFrameWnd
{
protected:
    afx_msg LRESULT OnHotKey(WPARAM wParam, LPARAM lParam);
    DECLARE_MESSAGE_MAP()
};
// メッセージマップの定義
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_HOTKEY()
END_MESSAGE_MAP()
// ハンドラの実装
LRESULT CMainFrame::OnHotKey(WPARAM wParam, LPARAM lParam)
{
    int nHotKeyId = (int)wParam;
    UINT fsModifiers = (UINT)LOWORD(lParam);
    UINT vk = (UINT)HIWORD(lParam);
    if (nHotKeyId == 1) {
      // [Ctrl] + [Alt] + [F12]キーが押下された場合
      AfxMessageBox(_T("ホットキーが押されました"));
    }
    return 0;
}
</syntaxhighlight>
<br>
<u>※注意</u><br>
<u>ホットキーは、システム全体で有効なため、他のアプリケーションと競合しないように注意する必要がある。</u><br>
<u>また、アプリケーション終了時には、必ずUnregisterHotKey関数でホットキーを解除する。</u><br>
<br><br>
 
== キーボード入力のシミュレーション ==
プログラムから仮想的にキーボード入力を生成する技術を、キーボード入力のシミュレーションという。<br>
<br>
Windowsでは、<code>SendInput</code> 関数 と <code>keybd_event</code> 関数の2つの方法がある。<br>
<br>
* SendInput関数
*: Windows 2000以降で推奨される方法である。
* keybd_event関数
*: レガシーな方法であり、互換性のために残されている。
<br>
==== SendInput関数 ====
SendInput関数は、キーボードやマウスの入力イベントを合成する関数である。<br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
UINT SendInput(
    UINT nInputs,
    LPINPUT pInputs,
    int cbSize
);
</syntaxhighlight>
<br>
<center>
{| class="wikitable"
|+ 引数 (パラメータ)
|-
|-
|VK_F7 || [F7]
! 項目 !! 説明
|-
|-
|VK_F8 || [F8]
| nInputs || 入力イベントの数
|-
|-
|VK_F9 || [F9]
| pInputs || INPUT構造体の配列
|-
|-
|VK_F10 || [F10]
| cbSize || INPUT構造体のサイズ
|-
|}
|VK_F11 || [F11]
</center>
|-
<br>
|VK_F12 || [F12]
使用例を以下に示す。<br>
|-
<syntaxhighlight lang="c++">
|VK_F13 || [F13]
void SendKeyPress(WORD vkCode)
|-
{
|VK_F14 || [F14]
    INPUT inputs[2] = {};
|-
|VK_F15 || [F15]
    // キー押下イベント
|-
    inputs[0].type = INPUT_KEYBOARD;
|VK_F16 || [F16]
    inputs[0].ki.wVk = vkCode;
|-
    inputs[0].ki.dwFlags = 0;  // キーダウン
|VK_F17 || [F17]
|-
    // キー解放イベント
|VK_F18 || [F18]
    inputs[1].type = INPUT_KEYBOARD;
|-
    inputs[1].ki.wVk = vkCode;
|VK_F19 || [F19]
    inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
|-
|VK_F20 || [F20]
    // 入力イベントの送信
|-
    UINT uSent = SendInput(2, inputs, sizeof(INPUT));
|VK_F21 || [F21]
    if (uSent != 2) {
|-
      AfxMessageBox(_T("SendInputに失敗しました"));
|VK_F22 || [F22]
    }
|-
}
|VK_F23 || [F23]
|-
// 使用例 : [Enter]キーを送信
|VK_F24 || [F24]
void CMyView::SimulateEnterKey()
|-
{
! style="background-color:#66CCFF;" colspan="2" | ブラウザキー
    SendKeyPress(VK_RETURN);
|-
}
|VK_BROWSER_BACK || ブラウザの[戻る]キー
</syntaxhighlight>
|-
<br>
|VK_BROWSER_FORWARD || ブラウザの[進む]キー
修飾キー付きのキー入力をシミュレートする例を以下に示す。<br>
|-
<syntaxhighlight lang="c++">
|VK_BROWSER_REFRESH || ブラウザの[更新]キー
void SendCtrlC()
|-
{
|VK_BROWSER_STOP || ブラウザの[中止]キー
    INPUT inputs[4] = {};
|-
|VK_BROWSER_SEARCH || ブラウザの[検索]キー
    // [Ctrl]キー押下
|-
    inputs[0].type = INPUT_KEYBOARD;
|VK_BROWSER_FAVORITES || ブラウザの[お気に入り]キー
    inputs[0].ki.wVk = VK_CONTROL;
|-
    inputs[0].ki.dwFlags = 0;
|VK_BROWSER_HOME || ブラウザの[起動/ホーム]キー
|-
    // [C]キー押下
! style="background-color:#66CCFF;" colspan="2" | 音量キー
    inputs[1].type = INPUT_KEYBOARD;
|-
    inputs[1].ki.wVk = 'C';
|VK_VOLUME_MUTE || 音量ミュート・キー
    inputs[1].ki.dwFlags = 0;
    // [C]キー解放
    inputs[2].type = INPUT_KEYBOARD;
    inputs[2].ki.wVk = 'C';
    inputs[2].ki.dwFlags = KEYEVENTF_KEYUP;
    // [Ctrl]キー解放
    inputs[3].type = INPUT_KEYBOARD;
    inputs[3].ki.wVk = VK_CONTROL;
    inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
    SendInput(4, inputs, sizeof(INPUT));
}
</syntaxhighlight>
<br>
SendInput関数は、UIPI (User Interface Privilege Isolation) の制約を受ける。<br>
低い整合性レベルのアプリケーションから、高い整合性レベルのアプリケーションに入力を送信することはできない。<br>
<br>
==== keybd_event関数 ====
keybd_event関数は、レガシーなキーボード入力シミュレーション関数である。<br>
<br>
関数のプロトタイプを以下に示す。<br>
<syntaxhighlight lang="c++">
VOID keybd_event(
    BYTE bVk,
    BYTE bScan,
    DWORD dwFlags,
    ULONG_PTR dwExtraInfo
);
</syntaxhighlight>
<br>
<center>
{| class="wikitable"
|+ 引数 (パラメータ)
|-
|-
|VK_VOLUME_DOWN || 音量ダウン・キー
! 項目 !! 説明
|-
|-
|VK_VOLUME_UP || 音量アップ・キー
| bVk || 仮想キーコード
|-
|-
! style="background-color:#66CCFF;" colspan="2" | メディアキー
| bScan || スキャンコード<br>(通常は0)
|-
|-
|VK_MEDIA_NEXT_TRACK || メディアの[次のトラック]キー
| dwFlags || フラグ<br>(KEYEVENTF_KEYUP等)
|-
|-
|VK_MEDIA_PREV_TRACK || メディアの[前のトラック]キー
| dwExtraInfo || 追加情報<br>(通常は0)
|-
|VK_MEDIA_STOP || メディアの[メディア停止]キー
|-
|VK_MEDIA_PLAY_PAUSE || メディアの[メディア再生/一時停止]キー
|-
! style="background-color:#66CCFF;" colspan="2" | ランチャーキー
|-
|VK_LAUNCH_MAIL || ランチャの[メール起動]キー
|-
|VK_LAUNCH_MEDIA_SELECT || ランチャの[メディア選択]キー
|-
|VK_LAUNCH_APP1 || ランチャの[アプリケーション(1)起動]キー
|-
|VK_LAUNCH_APP2 || ランチャの[アプリケーション(2)起動]キー
|-
! style="background-color:#66CCFF;" colspan="2" | その他
|-
|VK_OEM_1 || [:][*]※米国標準キーボードでは[;][:]キー<br>(様々な文字に使用されてキーボードによって異なる)
|-
|VK_OEM_2 || [/][?]※様々な文字のために使用できる。<br>(米国標準キーボードでは[/][?])
|-
|VK_OEM_3 || [@][`]※様々な文字のために使用できる。<br>(米国標準キーボードでは[`][~])
|-
|VK_OEM_4 || [[][{]※様々な文字のために使用できる。<br>(米国標準キーボードでは[[][{])
|-
|VK_OEM_5 || [\][|]※様々な文字のために使用できる。<br>(米国標準キーボードでは[\][|])
|-
|VK_OEM_6 || []][}]※様々な文字のために使用できる。<br>(米国標準キーボードでは[]][}])
|-
|VK_OEM_7 || [^][~]※様々な文字のために使用できる。<br>(米国標準キーボードでは[']["])
|-
|VK_OEM_8 || 様々な文字のために使用できる。
|-
|VK_OEM_PLUS || [;][+]<br>※任意の国/地域のキーボードで[+]キー
|-
|VK_OEM_MINUS || [-][=]<br>※任意の国/地域のキーボードで[-]キー
|-
|VK_OEM_COMMA || [,][<]<br>※任意の国/地域のキーボードで[,]キー
|-
|VK_OEM_PERIOD || [.][>]<br>※任意の国/地域のキーボードで[.]キー
|}
|}
</center>
</center>
<br>
使用例を以下に示す。<br>
<syntaxhighlight lang="c++">
void SendKeyPressLegacy(BYTE vkCode)
{
    // キー押下
    keybd_event(vkCode, 0, 0, 0);
    // キー解放
    keybd_event(vkCode, 0, KEYEVENTF_KEYUP, 0);
}
// [Ctrl] + [V]キーの送信
void SendCtrlV()
{
    // [Ctrl]キー押下
    keybd_event(VK_CONTROL, 0, 0, 0);
    // [V]キー押下
    keybd_event('V', 0, 0, 0);
    // [V]キー解放
    keybd_event('V', 0, KEYEVENTF_KEYUP, 0);
    // [Ctrl]キー解放
    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
}
</syntaxhighlight>
<br>
<u>※注意</u><br>
<u>keybd_event関数は、Windows 2000以降では非推奨とされている。</u><br>
<u>新規開発では、SendInput関数の使用を推奨する。</u><br>
<br>
キーボード入力のシミュレーションは、自動化ツール、テストツール、マクロツールなどの実装に利用される。<br>
ただし、セキュリティ上の理由から、使用には注意が必要である。<br>
<br><br>
<br><br>
== 関連リンク ==
* [https://learn.microsoft.com/en-us/windows/win32/inputdev/keyboard-input Microsoft Docs - Keyboard Input]
* [https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes Microsoft Docs - Virtual-Key Codes]
* [https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput Microsoft Docs - SendInput function]
<br><br>
{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,MFC,C++,Keyboard,キーボード,Windows,Programming,GetKeyState,GetAsyncKeyState,SendInput,Accelerator,HotKey,仮想キー,Virtual Key
|description={{PAGENAME}} - MFC/C++におけるキーボード処理の包括的なガイド
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
__FORCETOC__
[[カテゴリ:MFC]]
[[カテゴリ:MFC]]

2026年1月31日 (土) 20:04時点における最新版

概要

MFCアプリケーションにおけるキーボード処理は、Windowsのメッセージドリブン設計に基づいて実装される。

キーボード入力は、WM_KEYDOWN、WM_KEYUP、WM_CHARなどのWindowsメッセージとして処理される。
MFCは、これらのメッセージを処理するための便利なメッセージハンドラ (OnKeyDown、OnKeyUp、OnChar等) を提供している。

  • キーボードメッセージの種類と処理フロー
  • 仮想キーコードと文字コードの違い
  • GetKeyState関数とGetAsyncKeyState関数によるキー状態の取得
  • MFCのキーボードイベントハンドラ
  • アクセラレータキーとホットキーの登録
  • キーボード入力のシミュレーション


仮想キーコードは、キーボードのキー自体を識別するためのコードであり、VK_RETURNVK_SHIFT 等の定数で表現される。
一方、文字コードは、キー入力によって生成される文字を表すコードであり、WM_CHAR メッセージで取得できる。

MFCのキーボード処理を理解することにより、以下に示す機能を実装できる。

  • ショートカットキーの実装
  • ゲームや対話型アプリケーションのキー入力処理
  • カスタムキーバインディング
  • キーボードマクロとオートメーション



キーボードメッセージ

Windowsは、キーボード入力を複数のメッセージとして処理する。

主要なキーボードメッセージ
メッセージ 説明
WM_KEYDOWN / WM_KEYUP キーが押下された時と離された時に送信される。
WM_CHAR 文字入力時に送信される。
(TranslateMessage関数による変換後)
WM_SYSKEYDOWN / WM_SYSKEYUP Altキーと組み合わせたシステムキー入力時に送信される。


キーボードメッセージの処理フローを理解することは、適切なキーボード処理の実装に不可欠である。

WM_KEYDOWN / WM_KEYUP

  • WM_KEYDOWNメッセージ
    キーが押下された時に送信される。
  • WM_KEYUPメッセージ
    キーが離された時に送信される。


※注意
WM_KEYDOWN と WM_KEYUP は、文字入力ではなく、キーの物理的な押下と解放を検出する場合に使用する。

メッセージパラメータ
パラメータ 説明
wParam 仮想キーコード (VK_RETURN、VK_ESCAPE等)
lParam キーの詳細情報 (リピートカウント、スキャンコード、拡張キーフラグ等)


lParamの構造
ビット 説明
0〜15 リピートカウント
16〜23 スキャンコード
24 拡張キーフラグ (1の場合、拡張キー)
29 コンテキストコード (Altキーが押されている場合は1)
30 前のキー状態 (既に押されていた場合は1)
31 遷移状態 (WM_KEYUPの場合は1)


WM_CHAR

  • WM_CHARメッセージ
    文字入力時に送信される。


WM_CHARメッセージは、TranslateMessage 関数により WM_KEYDOWN メッセージから生成される。
TranslateMessage 関数は、仮想キーコードを文字コードに変換する。

メッセージパラメータ
パラメータ 説明
wParam 文字コード (ASCII文字やUnicode文字)
lParam キーの詳細情報 (WM_KEYDOWNと同様)


WM_CHARメッセージは、テキスト入力を処理する場合に使用する。
例えば、エディットコントロールやカスタムテキストエディタの実装に利用する。

また、WM_CHARメッセージは、Shift、Ctrl、Altキーの状態を考慮した文字コードを提供する。
例えば、[Shift]キー + [A]キーを同時押下する時、wParamは"A"の文字コードとなる。

WM_SYSKEYDOWN / WM_SYSKEYUP

WM_SYSKEYDOWNメッセージ と WM_SYSKEYUPメッセージは、[Alt]キーと組み合わせたキー入力時に送信される。

これらのメッセージは、システムキーの処理に使用される。
システムキーには、[Alt] + [F4] (ウィンドウを閉じる)、[Alt] + [Tab] (タスク切り替え) 等が含まれる。

メッセージパラメータは、WM_KEYDOWN と WM_KEYUPと同様である。

WM_SYSKEYDOWN と WM_SYSKEYUPを処理する場合、通常は DefWindowProc 関数に渡して、デフォルトのシステム動作を維持する。
カスタム処理が必要な場合のみ、これらのメッセージを独自に処理する。

メッセージの処理フロー

キーボードメッセージの処理フローを以下に示す。

  1. ユーザがキーを押下する。
  2. Windowsは、WM_KEYDOWNメッセージを生成する。
  3. アプリケーションのメッセージループは、TranslateMessage関数を呼び出す。
  4. TranslateMessage関数は、WM_KEYDOWNからWM_CHARメッセージを生成する。(文字キーの場合)
  5. DispatchMessage関数は、メッセージをウィンドウプロシージャに送信する。
  6. ウィンドウプロシージャは、メッセージハンドラを呼び出す。


この処理フローを理解することにより、適切なメッセージハンドラを選択できる。

文字入力を処理する場合は、WM_CHARメッセージを使用する。
キーの物理的な押下を検出する場合は、WM_KEYDOWNメッセージを使用する。


仮想キーの取得

GetKeyState 関数は、特定の仮想キーの状態を取得する関数である。

関数のプロトタイプを以下に示す。

 SHORT GetKeyState(int nVirtKey);


引数 (パラメータ) と 戻り値
種別 項目 説明
引数 (パラメータ) nVirtKey 仮想キーコード (VK_SHIFT、VK_CONTROL等)
戻り値 最上位ビット (ビット15) キーが押されている場合は1、それ以外は0
最下位ビット (ビット0) キーのトグル状態 (CapsLockやNumLockの場合、ONの時は1)


使用例を以下に示す。

 // Shiftキーが押されているか確認
 if (GetKeyState(VK_SHIFT) & 0x8000) {
    // [Shift]キーが押下されている場合
 }
 
 // CapsLockがONか確認
 if (GetKeyState(VK_CAPITAL) & 0x0001) {
    // [CapsLock]がONの状態
 }


下表に、主要な仮想キー定数を示す。

主要な仮想キー定数
定数 説明
VK_SHIFT Shiftキー
VK_CONTROL Ctrlキー
VK_MENU Altキー
VK_LSHIFT 左Shiftキー
VK_RSHIFT 右Shiftキー
VK_LCONTROL 左Ctrlキー
VK_RCONTROL 右Ctrlキー
VK_LMENU 左Altキー
VK_RMENU 右Altキー


矢印キーと編集キー
定数 説明
VK_LEFT 左矢印キー
VK_UP 上矢印キー
VK_RIGHT 右矢印キー
VK_DOWN 下矢印キー
VK_INSERT Insertキー
VK_DELETE Deleteキー
VK_HOME Homeキー
VK_END Endキー
VK_PRIOR Page Upキー
VK_NEXT Page Downキー


特殊キー
定数 説明
VK_RETURN Enterキー
VK_ESCAPE Escapeキー
VK_SPACE Spaceキー
VK_TAB Tabキー
VK_BACK Backspaceキー
VK_CAPITAL CapsLockキー
VK_NUMLOCK NumLockキー
VK_SCROLL ScrollLockキー


ファンクションキー
定数 説明
VK_F1 F1キー
VK_F2 F2キー
VK_F3 F3キー
VK_F4 F4キー
VK_F5 F5キー
VK_F6 F6キー
VK_F7 F7キー
VK_F8 F8キー
VK_F9 F9キー
VK_F10 F10キー
VK_F11 F11キー
VK_F12 F12キー


GetKeyState 関数は、現在のスレッドのメッセージキュー内の最後のメッセージの時点でのキー状態を返す。
リアルタイムのキー状態を取得する場合は、GetAsyncKeyState 関数を使用する。


GetAsyncKeyState関数

GetAsyncKeyState 関数は、リアルタイムのキー状態を取得する関数である。

関数のプロトタイプを以下に示す。

 SHORT GetAsyncKeyState(int vKey);


パラメータと戻り値は、GetKeyState 関数と同様である。

GetKeyState関数との違い

GetKeyState関数とGetAsyncKeyState関数の違いを以下に示す。

GetKeyState関数 と GetAsyncKeyState関数 の比較
項目 GetKeyState関数 GetAsyncKeyState関数
取得タイミング メッセージキュー内の最後のメッセージ時点 関数呼び出し時点 (リアルタイム)
用途 メッセージハンドラ内での使用 ゲームループや非同期処理での使用
同期性 同期的 (メッセージキューと同期) 非同期的 (現在の物理的なキー状態)
マルチスレッド スレッドごとに独立 システム全体で共有


  • GetKeyState関数
    メッセージドリブンな処理に適している。
    メッセージハンドラ内で使用することにより、メッセージが生成された時点のキー状態を正確に取得できる。

  • GetAsyncKeyState関数
    リアルタイムなキー状態の検出に適している。
    ゲームのメインループや、定期的なタイマー処理でキー状態をポーリングする場合に使用する。


使用例

GetAsyncKeyState関数の使用例を以下に示す。

 // ゲームループ内でのキー状態チェック
 
 void CGameView::OnTimer(UINT_PTR nIDEvent)
 {
    // 矢印キーで移動制御
    if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
       m_playerX -= 5;  // 左に移動
    }
 
    if (GetAsyncKeyState(VK_RIGHT) & 0x8000) {
       m_playerX += 5;  // 右に移動
    }
 
    if (GetAsyncKeyState(VK_UP) & 0x8000) {
       m_playerY -= 5;  // 上に移動
    }
 
    if (GetAsyncKeyState(VK_DOWN) & 0x8000) {
       m_playerY += 5;  // 下に移動
    }
 
    // [Space]キーでジャンプ
    if (GetAsyncKeyState(VK_SPACE) & 0x8000) {
       Jump();
    }
 
    Invalidate();  // 再描画
    CView::OnTimer(nIDEvent);
 }


この例では、タイマーイベント内で定期的にキー状態をチェックし、プレイヤーの位置を更新している。

GetAsyncKeyState関数は、複数のキーの同時押しを検出する場合にも便利である。

 // [Ctrl] + [S]キーの同時押下を検出
 
 if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && (GetAsyncKeyState('S') & 0x8000)) {
    SaveDocument();
 }



MFCでのキーボードイベントハンドラ

MFCは、キーボードメッセージを処理するための便利なメッセージハンドラを提供している。

主要なキーボードイベントハンドラ
メソッド 説明
OnKeyDown / OnKeyUp WM_KEYDOWN / WM_KEYUPメッセージの処理
OnChar WM_CHARメッセージの処理
PreTranslateMessage メッセージの前処理 (メッセージがDispatchされる前に介入)


これらのハンドラを適切に使用することにより、柔軟なキーボード処理を実装できる。

OnKeyDown / OnKeyUp

OnKeyDown イベントハンドラ と OnKeyUp イベントハンドラ は、キーの押下と解放を処理するハンドラである。

関数のプロトタイプを以下に示す。

 afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
 afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);


引数 (パラメータ)
項目 説明
nChar 仮想キーコード
nRepCnt リピートカウント (キーが押下され続けている場合の繰り返し回数)
nFlags キーの詳細情報 (拡張キーフラグ、コンテキストコード等)


OnKeyDownハンドラを使用することにより、特定のキーに対する処理を簡潔に実装できる。

使用例を以下に示す。

 void CMyView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
 {
    switch (nChar)
    {
    case VK_LEFT:
       // [左矢印]キーが押下された場合
       MoveLeft();
       break;
    case VK_RIGHT:
       // [右矢印]キーが押下された場合
       MoveRight();
       break;
    case VK_ESCAPE:
       // [Esc]キーが押下された場合
       CancelOperation();
       break;
    case VK_F1:
       // [F1]キーが押下された場合 (ヘルプ表示)
       ShowHelp();
       break;
    default:
       CView::OnKeyDown(nChar, nRepCnt, nFlags);
       break;
    }
 }


OnChar

OnChar イベントハンドラは、文字入力を処理するハンドラである。

関数のプロトタイプを以下に示す。

 afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);


パラメータ
項目 説明
nChar 文字コード (ASCII文字やUnicode文字)
nRepCnt リピートカウント
nFlags キーの詳細情報


OnCharハンドラは、テキスト入力を処理する場合に使用する。
Shift、Ctrl、Altキーの状態を考慮した文字コードが提供されるため、文字入力処理が簡潔になる。

使用例を以下に示す。

 void CMyView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
 {
    if (nChar == VK_RETURN) {
       // [Enter]キーが押下された場合
       ProcessInput();
    }
    else if (nChar == VK_ESCAPE) {
       // [Esc]キーが押下された場合
       CancelInput();
    }
    else if (nChar >= 32) {
       // 印字可能文字の場合
       // 文字を入力バッファに追加
       m_inputBuffer += (TCHAR)nChar;
       Invalidate();
    }
 
    CView::OnChar(nChar, nRepCnt, nFlags);
 }


PreTranslateMessageメソッド

PreTranslateMessage メソッドは、メッセージがディスパッチされる前に呼び出されるメソッドである。
このメソッドをオーバーライドすることにより、メッセージの前処理やフィルタリングを実装できる。

ディスパッチ (Dispatch) とは、メッセージを適切なウィンドウプロシージャに転送することを意味する。
Windowsのメッセージループでは、一般的に以下に示す流れで処理される。

  1. GetMessage関数
    メッセージキューからメッセージを取得する。
  2. TranslateMessage関数
    キーボードメッセージを文字メッセージに変換する。
  3. DispatchMessage関数
    メッセージを対象ウィンドウのウィンドウプロシージャ (WndProc) に転送する。


PreTranslateMessageメソッドは、このDispatchMessageが呼ばれる前の段階で介入できるメソッドである。
このメソッドがTRUEを返す場合、メッセージはウィンドウプロシージャに転送されずに、破棄または独自処理される。
これにより、特定のキー入力を横取りしてショートカットキーとして処理する等の実装が可能になる。

関数のプロトタイプを以下に示す。

 virtual BOOL PreTranslateMessage(MSG* pMsg);


引数 (パラメータ) と 戻り値
種別 項目 説明
パラメータ pMsg メッセージ構造体へのポインタ
戻り値 TRUE メッセージを処理済み (DispatchMessageに渡さない)
FALSE メッセージを通常通り処理 (DispatchMessageに渡す)


使用例を以下に示す。

 BOOL CMyDialog::PreTranslateMessage(MSG* pMsg)
 {
    if (pMsg->message == WM_KEYDOWN) {
       // [Enter]キーの処理を無効化 (ダイアログを閉じないようにする)
       if (pMsg->wParam == VK_RETURN) {
          // カスタム処理
          OnEnterKey();
          return TRUE;  // メッセージを消費
       }
 
       // [Esc]キーの処理を無効化
       if (pMsg->wParam == VK_ESCAPE) {
          // カスタム処理
          OnEscapeKey();
          return TRUE;  // メッセージを消費
       }
 
       // [Tab]キーで次のコントロールに移動 (カスタム順序)
       if (pMsg->wParam == VK_TAB) {
          FocusNextControl();
          return TRUE;  // メッセージを消費
       }
    }
 
    return CDialog::PreTranslateMessage(pMsg);
 }


PreTranslateMessageメソッドは、ダイアログのデフォルト動作を変更する場合に特に有用である。

例えば、ダイアログでは、[Enter]キーを押下するとOKボタンが押下され、[Esc]キーを押下するとキャンセルボタンが押下されるデフォルト動作がある。
PreTranslateMessageメソッドをオーバーライドすることにより、この動作をカスタマイズできる。


アクセラレータキー

アクセラレータキー (ショートカットキー) は、メニューコマンドをキーボードから直接実行する機能である。

MFCでは、リソースを使用した実装と、動的な実装の2つの方法がある。

リソースを使用した実装

リソースエディタを使用してアクセラレータテーブルを定義する方法を以下に示す。

ステップ 1 : リソースファイルの編集

リソースファイル (.rc) にアクセラレータテーブルを追加する。

 IDR_MAINFRAME ACCELERATORS
 BEGIN
    "N",      ID_FILE_NEW,       VIRTKEY, CONTROL  // Ctrl+N
    "O",      ID_FILE_OPEN,      VIRTKEY, CONTROL  // Ctrl+O
    "S",      ID_FILE_SAVE,      VIRTKEY, CONTROL  // Ctrl+S
    VK_F1,    ID_HELP,           VIRTKEY           // F1
    VK_F5,    ID_VIEW_REFRESH,   VIRTKEY           // F5
    "Q",      ID_APP_EXIT,       VIRTKEY, CONTROL  // Ctrl+Q
 END


ステップ 2 : アクセラレータテーブルのロード

アプリケーションクラスでアクセラレータテーブルをロードする。

 BOOL CMyApp::InitInstance()
 {
    // アクセラレータテーブルのロード
    m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(),
                                     MAKEINTRESOURCE(IDR_MAINFRAME));
 
    // メインウィンドウの作成など
    // ...
 
    return TRUE;
 }


ステップ 3 : メッセージループでの処理

メッセージループでTranslateAccelerator関数を呼び出す。

MFCでは、CWinAppクラスが自動的にアクセラレータテーブルを処理するため、通常は追加のコードは不要である。

ステップ 4 : コマンドハンドラの実装

各コマンドIDに対するハンドラを実装する。

 void CMainFrame::OnFileNew()
 {
    // [Ctrl] + [N]キーが押下された時の処理
    AfxMessageBox(_T("新規ファイル作成"));
 }
 
 void CMainFrame::OnFileOpen()
 {
    // [Ctrl] + [O]キーが押下された時の処理
    CFileDialog dlg(TRUE);
    if (dlg.DoModal() == IDOK) {
       OpenFile(dlg.GetPathName());
    }
 }


動的なアクセラレータ作成

実行時に動的にアクセラレータテーブルを作成する方法を以下に示す。

 void CMainFrame::CreateDynamicAccelerators()
 {
    // アクセラレータテーブルの定義
    ACCEL accel[3];
 
    // [Ctrl] + [N]キー
    accel[0].fVirt = FCONTROL | FVIRTKEY;
    accel[0].key = 'N';
    accel[0].cmd = ID_FILE_NEW;
 
    // [Ctrl] + [O]キー
    accel[1].fVirt = FCONTROL | FVIRTKEY;
    accel[1].key = 'O';
    accel[1].cmd = ID_FILE_OPEN;
 
    // [F5]キー
    accel[2].fVirt = FVIRTKEY;
    accel[2].key = VK_F5;
    accel[2].cmd = ID_VIEW_REFRESH;
 
    // アクセラレータテーブルの作成
    m_hAccelTable = CreateAcceleratorTable(accel, 3);
 
    // 既存のアクセラレータテーブルを破棄
    if (m_hOldAccelTable != NULL) {
       DestroyAcceleratorTable(m_hOldAccelTable);
    }
 }


動的なアクセラレータ作成は、ユーザがキーバインディングをカスタマイズできる機能を実装する場合に有用である。


ホットキーの登録

ホットキーは、アプリケーションがフォーカスを持っていない場合でも検出できるグローバルなキーボードショートカットである。

RegisterHotKey 関数を使用して、システム全体で有効なホットキーを登録できる。

RegisterHotKey関数

RegisterHotKey関数は、ホットキーを登録する関数である。

関数のプロトタイプを以下に示す。

 BOOL RegisterHotKey(
    HWND hWnd,
    int id,
    UINT fsModifiers,
    UINT vk
 );


引数 (パラメータ)
項目 説明
hWnd ホットキーが押下された時にWM_HOTKEYメッセージを受け取るウィンドウのハンドル
id ホットキーの識別子 (0x0000〜0xBFFFの範囲)
fsModifiers 修飾キー (MOD_ALT、MOD_CONTROL、MOD_SHIFT、MOD_WIN)
vk 仮想キーコード


使用例を以下に示す。

 // [Ctrl] + [Alt] + [F12]キーをホットキーとして登録
 BOOL CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1) {
       return -1;
    }
 
    // ホットキーの登録 (ID: 1)
    if (!RegisterHotKey(m_hWnd, 1, MOD_CONTROL | MOD_ALT, VK_F12)) {
       AfxMessageBox(_T("ホットキーの登録に失敗しました"));
    }
 
    return 0;
 }
 
 // ホットキーの解除
 void CMainFrame::OnDestroy()
 {
    UnregisterHotKey(m_hWnd, 1);
    CFrameWnd::OnDestroy();
 }


WM_HOTKEYメッセージの処理

ホットキーが押下されると、WM_HOTKEYメッセージが送信される。

メッセージハンドラの実装を以下に示す。

 // ヘッダファイルでメッセージマップを宣言
 class CMainFrame : public CFrameWnd
 {
 protected:
    afx_msg LRESULT OnHotKey(WPARAM wParam, LPARAM lParam);
    DECLARE_MESSAGE_MAP()
 };
 
 // メッセージマップの定義
 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_HOTKEY()
 END_MESSAGE_MAP()
 
 // ハンドラの実装
 LRESULT CMainFrame::OnHotKey(WPARAM wParam, LPARAM lParam)
 {
    int nHotKeyId = (int)wParam;
    UINT fsModifiers = (UINT)LOWORD(lParam);
    UINT vk = (UINT)HIWORD(lParam);
 
    if (nHotKeyId == 1) {
       // [Ctrl] + [Alt] + [F12]キーが押下された場合
       AfxMessageBox(_T("ホットキーが押されました"));
    }
 
    return 0;
 }


※注意
ホットキーは、システム全体で有効なため、他のアプリケーションと競合しないように注意する必要がある。
また、アプリケーション終了時には、必ずUnregisterHotKey関数でホットキーを解除する。


キーボード入力のシミュレーション

プログラムから仮想的にキーボード入力を生成する技術を、キーボード入力のシミュレーションという。

Windowsでは、SendInput 関数 と keybd_event 関数の2つの方法がある。

  • SendInput関数
    Windows 2000以降で推奨される方法である。
  • keybd_event関数
    レガシーな方法であり、互換性のために残されている。


SendInput関数

SendInput関数は、キーボードやマウスの入力イベントを合成する関数である。

関数のプロトタイプを以下に示す。

 UINT SendInput(
    UINT nInputs,
    LPINPUT pInputs,
    int cbSize
 );


引数 (パラメータ)
項目 説明
nInputs 入力イベントの数
pInputs INPUT構造体の配列
cbSize INPUT構造体のサイズ


使用例を以下に示す。

 void SendKeyPress(WORD vkCode)
 {
    INPUT inputs[2] = {};
 
    // キー押下イベント
    inputs[0].type = INPUT_KEYBOARD;
    inputs[0].ki.wVk = vkCode;
    inputs[0].ki.dwFlags = 0;  // キーダウン
 
    // キー解放イベント
    inputs[1].type = INPUT_KEYBOARD;
    inputs[1].ki.wVk = vkCode;
    inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
 
    // 入力イベントの送信
    UINT uSent = SendInput(2, inputs, sizeof(INPUT));
    if (uSent != 2) {
       AfxMessageBox(_T("SendInputに失敗しました"));
    }
 }
 
 // 使用例 : [Enter]キーを送信
 void CMyView::SimulateEnterKey()
 {
    SendKeyPress(VK_RETURN);
 }


修飾キー付きのキー入力をシミュレートする例を以下に示す。

 void SendCtrlC()
 {
    INPUT inputs[4] = {};
 
    // [Ctrl]キー押下
    inputs[0].type = INPUT_KEYBOARD;
    inputs[0].ki.wVk = VK_CONTROL;
    inputs[0].ki.dwFlags = 0;
 
    // [C]キー押下
    inputs[1].type = INPUT_KEYBOARD;
    inputs[1].ki.wVk = 'C';
    inputs[1].ki.dwFlags = 0;
 
    // [C]キー解放
    inputs[2].type = INPUT_KEYBOARD;
    inputs[2].ki.wVk = 'C';
    inputs[2].ki.dwFlags = KEYEVENTF_KEYUP;
 
    // [Ctrl]キー解放
    inputs[3].type = INPUT_KEYBOARD;
    inputs[3].ki.wVk = VK_CONTROL;
    inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
 
    SendInput(4, inputs, sizeof(INPUT));
 }


SendInput関数は、UIPI (User Interface Privilege Isolation) の制約を受ける。
低い整合性レベルのアプリケーションから、高い整合性レベルのアプリケーションに入力を送信することはできない。

keybd_event関数

keybd_event関数は、レガシーなキーボード入力シミュレーション関数である。

関数のプロトタイプを以下に示す。

 VOID keybd_event(
    BYTE bVk,
    BYTE bScan,
    DWORD dwFlags,
    ULONG_PTR dwExtraInfo
 );


引数 (パラメータ)
項目 説明
bVk 仮想キーコード
bScan スキャンコード
(通常は0)
dwFlags フラグ
(KEYEVENTF_KEYUP等)
dwExtraInfo 追加情報
(通常は0)


使用例を以下に示す。

 void SendKeyPressLegacy(BYTE vkCode)
 {
    // キー押下
    keybd_event(vkCode, 0, 0, 0);
 
    // キー解放
    keybd_event(vkCode, 0, KEYEVENTF_KEYUP, 0);
 }
 
 // [Ctrl] + [V]キーの送信
 void SendCtrlV()
 {
    // [Ctrl]キー押下
    keybd_event(VK_CONTROL, 0, 0, 0);
 
    // [V]キー押下
    keybd_event('V', 0, 0, 0);
 
    // [V]キー解放
    keybd_event('V', 0, KEYEVENTF_KEYUP, 0);
 
    // [Ctrl]キー解放
    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
 }


※注意
keybd_event関数は、Windows 2000以降では非推奨とされている。
新規開発では、SendInput関数の使用を推奨する。

キーボード入力のシミュレーションは、自動化ツール、テストツール、マクロツールなどの実装に利用される。
ただし、セキュリティ上の理由から、使用には注意が必要である。


関連リンク