Каков самый быстрый способ определить нажатие клавиши и удерживание клавиши в Win32?

Каков самый быстрый способ определить нажатие клавиши, а также как определить, удерживается ли ключ? Похоже, что обмен сообщениями окон медленный. Просьба привести пример, как это сделать, и почему это быстрее, чем альтернатива.

Чтобы быть ясным, это для цикла реального времени (симуляция), поэтому я ищу самый быстрый способ определить, был ли нажат ключ, а также проверить, не удерживается ли он.

Ответ 1

GetAsyncKeyState() - это то, что вы ищете. Он считывает физическое состояние клавиатуры, независимо от состояния очереди ввода. Если высокий бит установлен, тогда во время разговора ключ не работал.

// Fetch tab key state.
SHORT tabKeyState = GetAsyncKeyState( VK_TAB );

// Test high bit - if set, key was down when GetAsyncKeyState was called.
if( ( 1 << 15 ) & tabKeyState )
{
    // TAB key down... 
}

Кроме того, для записи Windows не является операционной системой реального времени. Если ваше приложение требует точности в реальном времени, вы можете выбрать другую платформу.

Ответ 2

Если вы просто хотите опросить состояние клавиатуры, чтобы узнать, какие клавиши вверх/вниз, а также состояние shift/alt/ctrl, просто вызовите GetKeyboardState (ссылка MSDN).

Когда я работал в игровой студии, именно так мы получили состояние клавиатуры для каждого кадра. Должно быть применимо к вашему симуляционному коду.

Ответ 3

Учитывая, что все взаимодействия между окнами связаны с обменом Windows-сообщениями (событиями с клавиатурой, событиями мыши, почти всеми событиями, которые вы можете себе представить), не существует способа более низкого уровня доступа к событиям клавиатуры (если вы не пишете собственный драйвер клавиатуры), Я знаю.

DirectX по-прежнему использует обмен сообщениями клавиатуры Windows, чтобы предоставить программистам DirectX доступ к событиям клавиатуры.

обновленный

Моя заметка о DirectX не должна была его использовать, но когда Microsoft захотела создать интерфейс для программистов для использования в играх реального времени, они все же написали DirectX поверх очереди сообщений Windows.

Я бы предложил посмотреть, как написать программу, которая может читать непосредственно из очереди сообщений. Я считаю, что есть хороший пример Code Project Windows Message Handling - часть 1.

Ваши два варианта - либо читать из очереди сообщений (буферизованные), либо читать непосредственно из состояния клавиатуры (как указано в Bukes), что означает, что ваш собственный цикл может технически пропустить клавиатурное событие по ряду причин.

Ответ 4

TL; DR: вы можете использовать GetAsyncKeyState для проверки того, находится ли ключ в настоящий момент, но для лучшего реагирования приложений на нажатия клавиш и выпусков вы хотите использовать код конвейера Win32 в нижней части моего сообщения.

GetAsyncKeyState отлично подходит для определения того, находится ли ключ в настоящее время, но с точки зрения определения того, был ли нажат или выпущен ключ, и сколько раз это было сделано, GetAsyncKeyState пропускает нажатия клавиш в приложении с интенсивным использованием процессора даже после сохранения предыдущего ключа государство.

Это то, что я пробовал:

static const unsigned int NumberOfKeys = 256U;

bool previousKeyboardState[NumberOfKeys];

//Get the current state of each key as the application starts to ensure that keys held down beforehand are not processed as pressed keys.
for (unsigned int keyNum = 0U; keyNum < NumberOfKeys; ++keyNum)
{
    previousKeyboardState[keyNum] = isKeyDown(keyNum);
}

//Works fine.
bool isKeyDown(int key)
{
    return (GetAsyncKeyState(key) & (1 << 16));
}

//Misses key presses when application is bogged down.
bool isKeyFirstPressed(int key)
{
    bool previousState = previousKeyboardState[key];

    previousKeyboardState[key] = isKeyDown(key);

    return (previousKeyboardState[key] && !previousState);
}

//Misses key releases when application is bogged down.
bool isKeyFirstReleased(int key)
{
    bool previousState = previousKeyboardState[key];

    previousKeyboardState[key] = isKeyDown(key);

    return (!previousKeyboardState[key] && previousState);
}


//Example usage:

if (isKeyDown(VK_W))
{
    //W key.
}

if (isKeyFirstReleased(VK_SNAPSHOT))
{
    //Print screen.
}

GetKeyboardState тоже не подходит, так как он не отслеживает количество нажатий клавиш или выпусков. Как сказал в своем ответе Эрик Филипс, это небуферизованные решения, которые не подходят, если вы, например, написали игру. Вам придется обрабатывать все нажатия клавиш быстрее, чем они получены.

Теперь мой код выше работает прилично и может быть подходящим для многих людей, но я предпочитаю не пропустить ни одного нажатия клавиши. Я ненавижу использование приложений, которые не реагируют. Я считаю, что лучшим решением для приложений Win32 является захват сообщений WM_KEYDOWN и WM_KEYUP в процессе и их обработка. Хорошо, что WM_KEYDOWN также предоставляет счетчик автоматического повтора, который может быть полезен для приложений, поддерживающих ввод текста (например, чат, IDE и т.д.). Это также добавляет небольшое усложнение, о котором говорится в документации WM_KEYDOWN:

Из-за функции автоповтора может быть отправлено более одного сообщения WM_KEYDOWN до отправки сообщения WM_KEYUP. Предыдущее состояние ключа (бит 30) может использоваться для определения того, указывает ли сообщение WM_KEYDOWN первый переход вниз или повторный переход вниз.

Есть также клавиатурные крючки Windows, которые вы могли бы изучить, но их сложнее использовать. Тем не менее, они хороши для получения глобальных нажатий клавиш.