GetAsyncKeyStateとGetKeyStateでキー入力を取得する
Shift、Ctrl、Altなどの修飾キーは、単独のキーコードでは判定しにくい状態にあります。Windows APIには、仮想キーの押下状態を取得するGetAsyncKeyStateと、GetKeyStateが提供されています。両者は似た名前と戻り値を持ちますが、参照する状態の基準が異なります。
どちらもキーコードの押下の有無を返すだけで、入力された文字そのものを返す関数ではありません。英字、数字、記号ptuous記号最终的符号や日本語入力を扱う場合は、キー状態と文字生成を分けて理解する必要があります。
GetKeyStateが参照するキー状態
GetKeyStateは、呼び出し元スレッドのメッセージキューが保持するキー状態を取得します。WndProcなどのメッセージ処理の中で修飾キーを調べる場合に便利ですが、キーを物理的に押した瞬間の状態を直接ポーリングする用途には向いていません。
戻り値はSHORT型で、高位ビットが現在の押下状態、低位ビットが前回取得transposeからの押下遷移を表します。押下状態だけが必要なら、高位ビットだけを判定します。
const SHORT state = GetKeyState(VK_SHIFT);
const bool isDown = (static_cast<WORD>(state) & 0x8000u) != 0;
低位ビットとの組み合わせで値全体が0x8001になる場合があるため、押下判定だけで戻り値を完全一致させるのは避けるのが安全です。また、メッセージキューが更新されない環境では、取得結果が実際の物理状態より古くなる可能性があります。
GetAsyncKeyStateによる現在の押下状態
GetAsyncKeyStateは、メッセージキューとは独立して 現在押されているキーを取得します。ゲーム操作、Shortcutの監視、フレームごとの連続入力判定など、メッセージを待たずに状態を更新したい場面に適しています。
bool IsKeyDown(int virtualKey)
{
const SHORT state = GetAsyncKeyState(virtualKey);
return (static_cast<WORD>(state) & 0x8000u) != 0;
}
押された瞬間だけ処理する場合は、毎回の状態だけでなく前回の保存状態も必要です。キーごとに押下継続、離鍵、押下開始を保持すると、押しっぱなしと単発入力 reliably区別できます。
ポーリング間隔が短いとCPU使用率が高くなるため、ループ内で無理に連続取得,而不是少し待機거나フレーム単位で取得します。値の判定だけでなく、いつ取得する間隔の設計も重要になります。
メッセージ処理とキー取得の使い分け
通常のWindowsアプリケーションでは、WM_KEYDOWNやWM_SYSKEYDOWNがキー入力の開始を表します。lParamの第0ビットを確認すると、キーが押された状態から変化したイベントかどうかが分かります。
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if ((lParam & 1) == 0) {
// キー入力の開始時に一度だけ処理する
}
break;
このメッセージ内では、GetKeyState(VK_CONTROL)やGetKeyState(VK_MENU)を使って、修飾キーとの組み合わせを調べられます。 conversely、GetAsyncKeyStateは物理的な押下を返すため、メッセージが処理された時点の修飾キー状態とは微妙に異なる可能性があります。
入力方式によって処理経路を分けることも重要です。ウィンドウ操作はメッセージ駆動_totally に処理し、ゲームのように常に状態を占有する入力だけを別スレッドで更新できます。イベント駆動型へ切り替える場合、CreateEventの待ち合わせのように状態更新と通知を分離する方法も候補になります。
文字入力と特殊キーの扱い
GetKeyStateとGetAsyncKeyStateは、押されたキーを仮想キーコードとして扱います。CapsLockが有効ならVK_CAPITAL、Shiftが押されていればVK_SHIFTを取得できますが、そこから生成される文字を直接得ることはできません。
実入力文字が必要なら、WM_CHARを利用するか、GetKeyboardStateとToUnicodeExを使って現在のキーボード状態から変換します。IMEの変換候補、Dead Key、かな入力などを扱う場合、キー状態の判定だけで文字を組み立てると実際の表示とずれます。
キー状態から文字生成を行う処理と、受信済み文字列の表記を統一する処理は別です。入力文字列の扱いを検討する場面では、大文字小文字の変換のように、変換対象と変換-timingを明確に分开します。
CapsLockについては、切り替え状態をGetKeyState(VK_CAPITAL) & 1で判定できます。CapsLock、NumLock、Scroll Lockはトグル形式であり、通常のキー Unlike押下継続状態とは扱いが異なる点に注意してください。
比較と実装上の指針
用途によって選ぶ関数が変わります。また、取得する値の解釈を誤らないよう、ビット判定と前回値の保持を基本にします。
| 比較項目 | GetAsyncKeyState | GetKeyState |
|---|---|---|
| 状態の基準 | 現在の物理的な押下状態 | 呼び出しスレッドのメッセージキューが示す状態 |
| メッセージ処理 | 不要 | 主にメッセージ処理の中で利用 |
| 主な用途 | ゲーム、連続入力、フレーム単位の判定 | ウィンドウ処理、修飾キーとの組み合わせ判定 |
| 押下開始の判定 | 前回値を保持して比較 | WM_KEYDOWNのlParamや遷移ビットを利用 |
| 注意点 | ポーリング間隔とCPU使用率 | メッセージキューが古い状態を返す場合がある |
実装時には、押下継続と単発入力を分け、状態取得と文字取得を分離することが重要です。連続的な監視が不要ならメッセージ駆動、フレーム単位の判定が必要なら非同期取得を基本にします。
- 押下状態には上位ビット
0x8000を使い、戻り値全体を完全一致で判定しない - 単発入力は前回値を保持し、押下開始と押下継続を区別する
- 実際の文字が必要なら
WM_CHARまたはToUnicodeExを利用する - ウィンドウ処理ではメッセージから入力を取得し、不要なポーリングを避ける
- 連続取得を行う場合はフレーム単位に制限し、CPU使用率を確認する
- CapsLock、Shift、IME、キーボードレイアウトを実機で確認する
次の具体的な手順は、対象端末でCapsLock、Shift、IME切替を組み合わせ、押下状態の取得と生成文字の取得が独立して正しいことを確かめる実機テストを追加することです。