キーボードとマウスの入力の処理(Win API)


11

Windowsでマウスやキーボードをキャッチする方法はいくつかあります。いくつか試してみましたが、それぞれに長所と短所があります。私はあなたに尋ねたいです:どの方法を使用しますか?

私はこれらを試しました:

  1. WM_KEYDOWN / WM_KEYUP-主な欠点は、ALT、CONTROL、SHIFTのような左利きのキーと右利きのキーを区別できないことです。

  2. GetKeyboardState-これは最初の方法の問題を解決しますが、新しい方法があります。右ALTキーが押されていることを確認すると、左Ctrlキーが押されていることも確認できます。この動作は、ローカライズされたキーボードレイアウト(チェコ語-CS)を使用している場合にのみ発生します。

  3. WM_INPUT(生の入力)-このメソッドは、左利きのキーと右利きのキーを区別しません(覚えている場合)。マウスの動きに対して、マウス位置のデルタ値がゼロのメッセージが生成されることがあります。

回答:


10

これを行うための最良かつ最も簡単な方法は、最初のアイデアを使用して、WM_KEYUP / WM_KEYDOWNメッセージとWM_SYSKEYUP / WM_SYSKEYDOWNメッセージを処理することです。これらは、左と右のシフト/コントロール/ Altキーの違いの検出を処理できます。適切な仮想キーコードが必要です。これらは、VK_LSHIFT / VK_RSHIFT、VK_LCONTROL / VK_RCONTROL、およびVK_LMENU / VK_RMENU(ALTキー用)です。

私はこれをどのように行ったかについての記事を書き、同じハンドラーでWM_KEYUP / WM_KEYDOWNとWM_SYSKEYUP / WM_SYSKEYDOWNの両方を処理していました。(残念ながら、ブログはもう利用できません。)

私が見ることができる唯一の複雑さは、US以外のキーボードを使用しているため、MSDNのWM_SYSKEYUPの記事で説明されているシーケンスを処理するために、いくつかの追加ロジックを追加する必要があるということです。しかし、私はおそらくマスタヨーダよりも単純なものを作ろうとするでしょう。


WM_メッセージは確かに最も単純ですが、イベントの欠落を気にしない場合にのみ「最適」です。私はそれが扱いにくい問題であることに気付いたとき、私はその解決策を放棄しました。キーが押されている間にアプリケーションがフォーカスを失うと、フォーカスをもう一度押すまでそのキーは「スタック」されます。
ダッシュトムバン

1
確かに入力がないことが問題ですが、最も簡単な解決策は、フォーカス/アクティブ化メッセージを適切に処理し、それを回避することです。実際には、ユーザーがフォーカスを失ったときにゲームを一時停止することをお勧めします。ユーザーが別の緊急アプリケーションに切り替える必要がない場合や、誤ってWindowsキーを押してしまう場合があるためです。
Daemin

3

それらを組み合わせることができない理由はありますか?たとえば、WM_KEYDOWNを使用してCtrl / Alt / Shiftキーの押下を検出し、その呼び出し内でGetKeyboardState()を使用して左から右を区別しますか?


はい、できます。私はおそらくこのソリューションで終了します(おそらくGetAsyncKeyStateを使用する方が良いでしょう)。しかし、もしあれば、もっと良い解決策を探しています。また、Right-ATLキーは2つのWM_KEYDOWNメッセージも生成します(キーボードレイアウトのため)。したがって、WM_INPUTまたはDirectInputのみが残ります。
デラックス

3

WM_INPUTはいいです。私は考えてあなたが使用して左/右キーを区別することができRAWKEYBOARD構造体を。難しい部分は、キー識別子(つまり、スキャンコード)の扱い方を理解することかもしれませんが、これをキーボード入力に使用したことがないので、言うことはできません。WM_KEYDOWNはとても簡単です:)

ただし、マウス入力にはWM_INPUTを使用しました。それは非常に低レベルです。加速が適用されていないため、非常に優れています(IMO)。以前はWM_INPUTが高dpiのマウスの動きを利用する唯一の方法でしたが、それがまだ当てはまるかどうかはわかりません。2006年のこのMSDNの記事を参照してください。

マウス/キーボードのDirectInputは、マイクロソフトによって明示的に推奨されていません。以前にリンクされたMSDNの記事を参照してください。ジョイスティックが必要な場合は、XInputが適しています。

編集:これに関する私の情報は古すぎるかもしれません。


3

実際には、WM_KEYDOWN / WM_KEYUPをキャッチするときにL / R Ctrl / Altを区別してください。簡単です、それはそうではありませんが、私が使用するコードは、ここで使用できます。

これでうまくいくことを願っています。

// Receives a WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN or WM_SYSKEYUP message and 
// returns a virtual key of the key that triggered the message.
// 
// If the key has a common virtual key code, that code is returned. 
// For Alt's and Ctrl's, the values from the KeyCodes enumeration are used.
int translateKeyMessage (MSG& Msg);

// Virtual key codes for keys that aren't defined in the windows headers.
enum KeyCodes
{
    VK_LEFTCTRL = 162,
    VK_RIGHTCTRL = 163,
    VK_LEFTALT = 164,
    VK_RIGHTALT = 165
};

// ======================================================================================

int translateKeyMessage (MSG& Msg)
{
    // Determine the virtual key code.
    int VirtualKeyCode = Msg.wParam;

    // Determine whether the key is an extended key, e.g. a right 
    // hand Alt or Ctrl.
    bool Extended = (Msg.lParam & (1 << 24)) != 0;

    // If this is a system message, is the Alt bit of the message on?
    bool AltBit = false;    
    if (Msg.message == WM_SYSKEYDOWN || Msg.message == WM_SYSKEYUP)
        AltBit = (Msg.lParam & (1 << 29)) != 0;

    if ((Msg.message == WM_SYSKEYUP || Msg.message == WM_KEYUP) && !Extended && !AltBit && VirtualKeyCode == 18)
    {
        // Left Alt
        return KeyCodes::VK_LEFTALT;
    }

    // Left Ctrl
    if (!Extended && !AltBit && VirtualKeyCode == 17)
    {
        // Peek for the next message.
        MSG nextMsg;
        BOOL nextMessageFound = PeekMessage(&nextMsg, NULL, 0, 0, PM_NOREMOVE);

        // If the next message is for the right Alt:
        if (nextMessageFound && nextMsg.message == Msg.message && nextMsg.wParam == 18)
        {
            //
            bool nextExtended = (nextMsg.lParam & (1 << 24)) != 0;

            //
            bool nextAltBit = false;    
            if (nextMsg.message == WM_SYSKEYDOWN || nextMsg.message == WM_SYSKEYUP)
                nextAltBit = (nextMsg.lParam & (1 << 29)) != 0;

            // If it is really for the right Alt
            if (nextExtended && !nextAltBit)
            {
                // Remove the next message
                PeekMessage(&nextMsg, NULL, 0, 0, PM_REMOVE);

                // Right Alt
                return KeyCodes::VK_RIGHTALT;
            }
        }

        // Left Ctrl
        return KeyCodes::VK_LEFTCTRL;
    }

    if (Msg.message == WM_SYSKEYUP && !Extended && AltBit && VirtualKeyCode == 17)
    {
        // Peek for the next message.
        MSG nextMsg;
        BOOL nextMessageFound = PeekMessage(&nextMsg, NULL, 0, 0, PM_NOREMOVE);

        // If the next message is for the right Alt:
        if (nextMessageFound && nextMsg.message == WM_KEYUP && nextMsg.wParam == 18)
        {
            //
            bool nextExtended = (nextMsg.lParam & (1 << 24)) != 0;

            //
            bool nextAltBit = false;    
            if (nextMsg.message == WM_SYSKEYDOWN || nextMsg.message == WM_SYSKEYUP)
                nextAltBit = (nextMsg.lParam & (1 << 29)) != 0;

            // If it is really for the right Alt
            if (nextExtended && !nextAltBit)
            {
                // Remove the next message
                PeekMessage(&nextMsg, NULL, 0, 0, PM_REMOVE);

                // Right Alt
                return KeyCodes::VK_RIGHTALT;
            }
        }
    }

    // Right Ctrl
    if (Extended && !AltBit && VirtualKeyCode == 17)
        return KeyCodes::VK_RIGHTCTRL;

    // Left Alt
    if (!Extended && AltBit && VirtualKeyCode == 18)
        return KeyCodes::VK_LEFTALT;

    // Default
    return VirtualKeyCode;
}

1
コードは+1、ヨーダのように話す場合は-1。それは煩わしく、あなたの答えを読みにくくします。
Anthony

確かに、これはジョークアカウントの場所ではありません。
coderanger、2010

2

DirectInput APIを試すか、最近ではXInput APIを試すことができます


1
XImputはPCに接続されたXBox 360コントローラー専用ではありませんか?私は、DirectInputが少し時代遅れであることを読んだので、それを使用しないようにしました。しかし、私もDirectInputを試してみました。
デラックス

XInputは、ゲームパッド専用です。Windows 10の時点で、XInputは非推奨になり、 'IGamepad'インターフェイスが採用されました。さらに、制限があるため、他のメカニズムよりもRAW_INPUTを使用する必要があります。
LaVolpe
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.