HOWTO · C++

Detectar pulsación de tecla en Windows usando C++

Compara las API de Windows en C++ para comprobar el estado de las teclas y gestionar eventos de teclado de bajo nivel.

En esta página

Para detectar la entrada del teclado en una aplicación de C++ para Windows, puedes elegir entre GetKeyState al procesar mensajes, GetAsyncKeyState para consultar el estado actual de una tecla y un hook WH_KEYBOARD_LL cuando necesites eventos de teclado de bajo nivel. Cada opción sirve para una tarea distinta: el texto y los atajos suelen gestionarse mediante los mensajes de una ventana, mientras que la consulta periódica y los hooks responden a necesidades de supervisión más específicas.

Entrada de teclado en aplicaciones de Windows

La entrada de teclado puede ser texto, un atajo de la aplicación como Ctrl+O o un comando del sistema como Alt+Tab. Una API de estado de tecla informa sobre una tecla, pero no convierte la entrada del teclado en texto. Para recibir texto, deja que el procesamiento habitual de mensajes de caracteres de la ventana tenga en cuenta la distribución del teclado y las teclas modificadoras.

Códigos de tecla virtual

Un código de exploración identifica la posición física de una tecla que notifica el teclado. Windows asigna a la entrada del teclado un código de tecla virtual, que las API usan para identificar el significado de una tecla. Las letras y los dígitos usan códigos de tecla virtual con el valor de su carácter: A a Z corresponden a 0x41 a 0x5A, y 0 a 9, a 0x30 a 0x39. Las demás teclas tienen constantes con nombre en WinUser.h.

Constante de tecla virtual Tecla
VK_SPACE Barra espaciadora
VK_SHIFT Mayús
VK_ESCAPE Esc
VK_LEFT Flecha izquierda
VK_F1 F1

Consulta la referencia de códigos de tecla virtual de Microsoft para ver la lista completa.

Usa GetKeyState para consultar el estado de la cola de mensajes

GetKeyState informa del estado de la tecla asociado a la cola de mensajes del hilo que realiza la llamada. Resulta útil al procesar un mensaje, cuando el estado debe corresponder a esa secuencia de procesamiento de entrada; no ofrece una instantánea general del teclado físico en cualquier momento. El bit de orden superior indica si una tecla está presionada. Por ejemplo, en un procedimiento de ventana, comprueba if (message == WM_KEYDOWN && (GetKeyState(VK_SHIFT) & 0x8000)) { /* handle Shift+key */ } para verificar Mayús mientras se procesa un mensaje de tecla presionada. Haz esta comprobación en el controlador de mensajes en lugar de mantener un bucle activo.

Esta distinción importa para una acción con la barra espaciadora en un juego: una aplicación que ya procesa mensajes de ventana puede utilizarlos para gestionar la entrada. El diseño de entrada más adecuado para un juego depende de su sistema de entrada y de su bucle de actualización; consultar las teclas periódicamente no es siempre necesario.

Usa GetAsyncKeyState para consultar una tecla periódicamente

Usa GetAsyncKeyState cuando un bucle necesite conocer si una tecla está presionada en ese momento, sin depender del estado de la cola de mensajes del hilo que realiza la llamada. El bit de orden superior está activado mientras la tecla permanece presionada. Evita usar el bit de orden inferior, que indica si se presionó desde la llamada anterior, para detectar pulsaciones de forma fiable, ya que otra aplicación puede consumir ese estado. Limita la frecuencia de consulta y compara estados consecutivos para informar una sola vez de la transición a tecla presionada.

El ejemplo portátil de abajo ilustra la lógica de transición con una secuencia breve de valores de estado de tecla. En un bucle de consulta para Windows, obtén cada valor mediante GetAsyncKeyState(VK_SPACE) y comprueba su bit de orden superior; limita la frecuencia del bucle y usa una comprobación aparte de Escape para salir.

#include <array>
#include <iostream>

int main() {
    bool spaceWasDown = false;
    const std::array<bool, 4> spaceStates{false, true, true, false};

    for (bool spaceIsDown : spaceStates) {
        if (spaceIsDown && !spaceWasDown) {
            std::cout << "Space pressed\n";
        }
        spaceWasDown = spaceIsDown;
    }
    return 0;
}
Space pressed

Usa SetWindowsHookEx para recibir eventos de teclado de bajo nivel

Un hook WH_KEYBOARD_LL recibe eventos de teclado de bajo nivel en el escritorio donde se instala. La función de devolución de llamada se ejecuta en el contexto del proceso que lo instala, por lo que ese hilo debe mantener un bucle de mensajes. Los hooks afectan al procesamiento de entrada del sistema y conviene reservarlos para los casos que requieren supervisar eventos; mantén breve la función de devolución de llamada y pasa los eventos no gestionados al siguiente hook mediante CallNextHookEx.

En una implementación para Windows, instala la función de devolución de llamada con SetWindowsHookExW(WH_KEYBOARD_LL, ...) y comprueba que el identificador del hook devuelto no sea nulo antes de iniciar el bucle de mensajes. En la función de devolución de llamada, inspecciona KBDLLHOOKSTRUCT::vkCode para encontrar la tecla deseada cuando code >= 0 y el mensaje sea WM_KEYDOWN o WM_SYSKEYDOWN. Llama a CallNextHookEx para los eventos que la aplicación no quiera suprimir deliberadamente. Mantén el hilo que instaló el hook en un bucle GetMessageW y, cuando termine, llama a UnhookWindowsHookEx; realiza esta limpieza tanto si el bucle de mensajes termina normalmente como si GetMessageW devuelve un error. Este esquema de API de Windows es específico de la plataforma y no es un ejemplo portátil ejecutable. La función de devolución de llamada de un hook de bajo nivel debe retornar enseguida; Windows puede quitar un hook que supere el tiempo límite. Devuelve el resultado del siguiente hook, salvo que la aplicación necesite suprimir un evento de forma intencionada.

Elige el método adecuado para detectar teclas

Usa GetKeyState al procesar un mensaje de ventana e interpretar el estado asociado a su cola. Usa GetAsyncKeyState para consultar, con una frecuencia limitada, si una tecla está presionada en ese momento. Elige SetWindowsHookEx con WH_KEYBOARD_LL solo cuando necesites supervisar eventos de bajo nivel y el programa pueda mantener correctamente la función de devolución de llamada y el bucle de mensajes. Para una acción de juego asociada a la barra espaciadora, empieza por el sistema de entrada que ya use el juego y elige la API que se ajuste a su modelo de eventos o de actualización.