处理键盘和鼠标输入(Win API)


11

在Windows下有多种捕获鼠标或键盘的方法。所以我尝试了其中的一些,但是每个都有优点和缺点。我想问你:使用哪种方法?

我已经尝试过这些:

  1. WM_KEYDOWN / WM_KEYUP-主要缺点是,我无法区分ALT,CONTROL或SHIFT之类的左右手键。

  2. GetKeyboardState-这解决了第一种方法的问题,但是有一个新方法。当我得到按下Right-ALT键时,我也得到了Left-Control键已按下。仅当使用本地化的键盘布局(捷克语-CS)时,才会发生此行为。

  3. WM_INPUT(原始输入)-此方法也不区分左手和右手键(如果我还记得的话),并且对于鼠标移动,有时会生成鼠标位置的增量值为零的消息。

Answers:


10

最好,最简单的方法是使用您的第一个想法并处理WM_KEYUP / WM_KEYDOWN消息以及WM_SYSKEYUP / WM_SYSKEYDOWN消息。这些可以处理检测左和右Shift / Control / Alt键之间的差异,您只需要适当的虚拟键代码即可。它们是VK_LSHIFT / VK_RSHIFT,VK_LCONTROL / VK_RCONTROL和VK_LMENU / VK_RMENU(用于ALT键)。

我写了一篇有关如何执行此操作的文章,并且我在同一处理程序中同时处理WM_KEYUP / WM_KEYDOWN和WM_SYSKEYUP / WM_SYSKEYDOWN。(不幸的是,该博客不再可用。)

我能看到的唯一复杂之处在于,因为您使用的是非美国键盘,所以您需要添加一些其他逻辑来处理MSDN上WM_SYSKEYUP文章中描述的顺序。但是,我可能会尝试使事情比masteryoda的事情简单。


WM_消息当然是最简单的消息,但仅当您不关心丢失的事件时才是“最佳”消息。一旦意识到这是一个棘手的问题,我便放弃了该解决方案。如果您的应用程序在按下某个键时失去焦点,则该键将被“卡住”,直到您再次按下它。
dash-tom-bang 2010年

1
确实缺少输入是一个问题,但是最简单的解决方案是适当处理焦点/激活消息并解决它。实际上,您想要做的是在失去焦点时暂停游戏,因为用户可能不需要切换到另一个更紧急的应用程序,或者他们只是偶然地按下了Windows键。
敏2010年

3

您无法合并它们是有原因的吗?例如,使用WM_KEYDOWN来检测是否按下了Ctrl / Alt / Shift键,然后在该调用中使用GetKeyboardState()来区分左与右?


我可以。我可能会以这种解决方案结束(使用GetAsyncKeyState可能会更好)。但我正在寻找任何更好的解决方案(如果存在)。并且Right-ATL键也生成两个WM_KEYDOWN消息(由于键盘布局)。因此,仅保留WM_INPUT或DirectInput。
豪华

3

WM_INPUT很好。我认为您可以使用RAWKEYBOARD结构区分左/右键。困难的部分可能是弄清楚如何处理键标识符(即扫描码),但是我不能说,因为我从未尝试过将其用于键盘输入。WM_KEYDOWN很简单:)

我已经将WM_INPUT用于鼠标输入。这是非常低级的。它没有应用加速度,这非常好(IMO)。WM_INPUT曾经是利用高dpi鼠标移动的唯一方法,但是我不确定是否仍然如此。请参阅2006年的这篇MSDN文章

Microsoft明确不建议使用鼠标/键盘的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代表yoda。这很烦人,使您的答案难以阅读。
安东尼2010年

确实,这不是开玩笑的地方。
coderanger

2

您可以尝试使用DirectInput API,或者最近尝试使用XInput API


1
XImput是否仅适用于XBox 360控制器连接到PC?我读过DirectInput有点过时了,所以我尝试避免使用它。但是我也尝试过DirectInput并且效果很好。
豪华

XInput仅用于游戏手柄。从Windows 10开始,不推荐使用XInput,而是使用“ IGamepad”界面。最重要的是,您应在其他机制上使用RAW_INPUT,因为它们有局限性。
LaVolpe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.