Робота з клавіатурою та мишею (Win API)


11

Існує ряд способів зловити мишу або клавіатуру під Windows. Тому я спробував деякі з них, але кожен з них має свої переваги та недоліки. Я хочу запитати вас: Який метод використовувати?

Я спробував це:

  1. WM_KEYDOWN / WM_KEYUP - Основним недоліком є ​​те, що я не можу розрізнити клавіші лівої та правої руки, такі як ALT, CONTROL або SHIFT.

  2. GetKeyboardState - це вирішує проблему першого методу, але є новий. Коли я переконуюсь, що натиснута клавіша Right-ALT, я також зрозумію, що клавіша Left-Control вниз. Така поведінка відбувається лише при використанні локалізованої розкладки клавіатури (чеська - CS).

  3. WM_INPUT (Raw Input) - Цей метод також не розрізняє ліву та праву клавіші (якщо я пам'ятаю) і для руху миші іноді генерує повідомлення з нульовими значеннями дельти положення миші.

Відповіді:


10

Найкращий і найпростіший спосіб зробити це - використовувати свою першу ідею та обробляти повідомлення WM_KEYUP / WM_KEYDOWN, а також повідомлення WM_SYSKEYUP / WM_SYSKEYDOWN. Вони можуть впоратися з виявленням різниці між лівою та правою клавішами зсуву / управління / альт, просто потрібні відповідні віртуальні коди ключів . Це VK_LSHIFT / VK_RSHIFT, VK_LCONTROL / VK_RCONTROL та VK_LMENU / VK_RMENU (для клавіші ALT).

Я написав пост про те, як це робив, і я обробляв і WM_KEYUP / WM_KEYDOWN, і WM_SYSKEYUP / WM_SYSKEYDOWN в одному обробнику. (На жаль, блог більше не доступний.)

Єдине ускладнення, яке я можу бачити, - це те, що ви використовуєте клавіатуру, що не використовується США, вам потрібно буде додати трохи додаткової логіки для обробки послідовності, описаної в статті WM_SYSKEYUP про MSDN. Однак, мабуть, я б спробував зробити щось простіше, ніж мастериода.


Повідомлення WM_, безумовно, найпростіші, але "найкращі", лише якщо вам не байдуже про пропущені події. Я відмовився від цього рішення, як тільки зрозумів, що це нерозв'язна проблема; якщо ваша програма втрачає фокус під час натискання клавіші, ця клавіша буде "застрягла", поки ви знову не натиснете її в фокусі.
dash-tom-bang

1
Дійсно відсутні дані - це проблема, але найпростішим рішенням було б належним чином обробити повідомлення про фокус / активацію та обійти її. Практично те, що ви хочете зробити, це призупинити гру, коли ви втратите фокус, оскільки користувачеві може знадобитися переключитися на інший більш терміновий додаток, або вони просто випадково натискають клавішу Windows.
Daemin

3

Чи є причина, що ви не можете їх поєднати? Наприклад, використовуйте WM_KEYDOWN для виявлення натискання клавіші Ctrl / Alt / Shift, тоді в межах цього виклику використовуйте GetKeyboardState (), щоб відрізнити ліворуч від праворуч?


Так, я можу. Я, мабуть, закінчуватимуться цим рішенням (можливо, буде краще використовувати GetAsyncKeyState). Але я шукаю кращого рішення, якщо таке існує. І клавіша Right-ATL також генерує два повідомлення WM_KEYDOWN (Через розкладку клавіатури). Тож залишається лише WM_INPUT або DirectInput.
Делюкс

3

WM_INPUT приємно. Я думаю, ви можете розрізнити ліву / праву клавіші, використовуючи структуру RAWKEYBOARD . Важкою частиною може бути з'ясування способів поводження з ідентифікаторами ключів (тобто сканкодами), але я не можу сказати, оскільки я ніколи не намагався використовувати це для введення з клавіатури. WM_KEYDOWN - це так просто :)

Я використовував WM_INPUT для введення миші. Це дуже низький рівень. У ньому не застосовується прискорення, що дуже приємно (ІМО). WM_INPUT раніше був єдиним способом скористатися рухом миші з високою точністю на дюйм, але я не впевнений, чи все ще так. Дивіться цю статтю MSDN від 2006 року .

Microsoft DirectInput для миші / клавіатури явно не рекомендує Microsoft. Дивіться раніше пов'язану статтю MSDN. Якщо вам потрібен джойстик, XInput - це, мабуть, шлях.

EDIT: Моя інформація щодо цього може бути занадто датовою.


3

Власне, розкажіть L / R Ctrl / Alt, коли ви ловите WM_KEYDOWN / WM_KEYUP, ви можете. Легко, це не так, але код, який я використовую, тут ви можете мати, хм хм.

Сподіваюся, це все-таки працює, я.

// 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 для виступу, як йода. Це дратує і робить ваші відповіді важкими для читання.
Ентоні

Дійсно, це не місце для жартівливих рахунків.
кодерангер

2

Ви можете спробувати API DirectInput або нещодавно API XInput .


1
Чи не XImput тільки для контролера XBox 360 підключений до ПК? Я читав DirectInput мало застарілий, тому намагався уникати його використання. Але я теж спробував DirectInput і добре прокинувся.
Делюкс

XInput призначений виключно для ігрових панелей. Як і в Windows 10, XInput застарілий на користь інтерфейсу 'IGamepad'. Крім того, слід використовувати RAW_INPUT над іншими механізмами, оскільки в них є обмеження.
LaVolpe
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.