HOWTO · C++

C++ を使用して Windows でキープレスを検出する

キーの状態の確認と低レベルのキーボードイベントの処理に使う Windows の C++ API を比較します。

このページの内容

Windows の C++ アプリケーションでキーボード入力を検出するには、メッセージ処理中に使う GetKeyState、現在のキー状態をポーリングするときに使う GetAsyncKeyState、低レベルのキーボードイベントが必要な場合に使う WH_KEYBOARD_LL フックから選びます。それぞれ適した用途は異なります。テキスト入力やショートカットは通常ウィンドウのメッセージで処理し、ポーリングやフックはより限定的な監視に使います。

Windows アプリケーションでのキーボード入力

キーボード入力には、文字の入力、Ctrl+O のようなアプリケーションのショートカット、Alt+Tab のようなシステムコマンドがあります。キー状態 API で分かるのはキーの状態であり、キーボード入力をテキストに変換するわけではありません。テキストを扱う場合は、ウィンドウの通常の文字メッセージ処理に任せ、キーボードレイアウトや修飾キーを反映させます。

仮想キーコード

スキャンコードは、キーボードから報告される物理キーの位置を識別します。Windows はキーボード入力を仮想キーコードに対応付け、API はそのコードでキーの意味を識別します。英字と数字には文字値に対応する仮想キーコードが使われます。A から Z は 0x41 から 0x5A、0 から 9 は 0x30 から 0x39 に対応します。その他のキーには WinUser.h で名前付き定数が定義されています。

仮想キー定数 キー
VK_SPACE スペースバー
VK_SHIFT Shift
VK_ESCAPE Escape
VK_LEFT 左矢印
VK_F1 F1

すべての仮想キーコードについては、Microsoft の仮想キーコード一覧を参照してください。

GetKeyState でメッセージキューに関連付けられた状態を確認する

GetKeyState は、呼び出し元スレッドのメッセージキューに関連付けられたキー状態を返します。処理中の入力メッセージに対応する状態を確認するのに適していますが、任意の時点における物理キーボードの状態を一般的に取得するものではありません。上位ビットはキーが押されているかどうかを示します。たとえばウィンドウプロシージャでは、キー押下メッセージの処理中に Shift キーを調べるため、if (message == WM_KEYDOWN && (GetKeyState(VK_SHIFT) & 0x8000)) { /* handle Shift+key */ } のように確認できます。この確認はメッセージハンドラー内で行い、ループで回し続けないでください。

この違いは、ゲームでスペースバーの操作を扱う場合にも関係します。すでにウィンドウメッセージを処理しているアプリケーションなら、そのメッセージを使って入力を処理できます。ゲームに適した入力設計は、使っている入力フレームワークや更新ループによって異なり、必ずしもポーリングが必要とは限りません。

GetAsyncKeyState でキーをポーリングする

呼び出し元スレッドのメッセージキューの状態に依存せず、ループ内で現在キーが押されているかを調べるには GetAsyncKeyState を使います。キーが押されている間は上位ビットがセットされます。信頼できる検出のために、前回の呼び出し以降に押されたことを示す下位ビットに頼るのは避けてください。別のアプリケーションがその状態を消費することがあるためです。ポーリングには間隔を設け、連続する状態を比較して、押された瞬間を一度だけ報告します。

次の移植可能な例では、短いキー状態の列を使って遷移判定の考え方を示します。Windows のポーリングループでは、GetAsyncKeyState(VK_SPACE) から各状態を取得して上位ビットを調べます。ループに間隔を設け、終了用に Escape キーを別途確認してください。

#include <array>
#include <iostream>

int main() {
    bool spaceWasDown = false;
    const std::array<bool, 4> spaceStates{false, true, true, false};

    for (bool spaceIsDown : spaceStates) {
        if (spaceIsDown && !spaceWasDown) {
            std::cout << "Space pressed\n";
        }
        spaceWasDown = spaceIsDown;
    }
    return 0;
}
Space pressed

SetWindowsHookEx で低レベルのキーボードイベントを取得する

WH_KEYBOARD_LL フックは、インストールされたデスクトップ上の低レベルキーボードイベントを受け取ります。コールバックはフックをインストールしたプロセスのコンテキストで実行されるため、そのスレッドはメッセージループを維持する必要があります。フックはシステムの入力処理に影響するので、イベント単位の監視が必要な場合に限って使ってください。コールバックは短く保ち、処理しないイベントは CallNextHookEx で次のフックに渡します。

Windows で実装する場合は、SetWindowsHookExW(WH_KEYBOARD_LL, ...) でコールバックを登録し、メッセージループに入る前に返されたフックハンドルが null でないことを確認します。コールバックでは、code >= 0 かつメッセージが WM_KEYDOWN または WM_SYSKEYDOWN の場合に、対象キーを KBDLLHOOKSTRUCT::vkCode で調べます。アプリケーションが意図的に抑止しないイベントには CallNextHookEx を呼び出してください。フックをインストールしたスレッドでは GetMessageW ループを実行し、ループ終了後に UnhookWindowsHookEx を呼び出します。通常終了時だけでなく、GetMessageW がエラーを返した場合にも後始末を行ってください。この Windows API の概要はプラットフォーム固有であり、移植可能な実行例ではありません。低レベルフックのコールバックは速やかに戻る必要があります。タイムアウトを超えると、Windows がフックを削除することがあります。イベントを意図的に抑止する場合を除き、次のフックの戻り値を返してください。

用途に合ったキー検出方法を選ぶ

ウィンドウメッセージを処理し、そのメッセージキューに関連付けられた状態を確認する場合は GetKeyState を使います。キーが現在押されているかを間隔を設けて調べるには GetAsyncKeyState を使います。SetWindowsHookEx と WH_KEYBOARD_LL を選ぶのは、低レベルイベントの監視が必要で、コールバックとメッセージループを適切に維持できる場合に限ります。ゲームでスペースバーの操作を扱うなら、まずゲーム既存の入力システムを確認し、そのイベント処理や更新方式に合う API を使ってください。