Una pulsación de tecla genera un evento de hardware que informa un "código de exploración" al sistema operativo Windows. Este código de exploración se convierte entonces en base al código de escaneado junto con otros factores de estado del teclado de un "código de tecla virtual" (bloqueo de mayúsculas estado, Shift/Alt/Ctrl KeyState, y también cualquier tecla muertos en espera golpes). El valor de VK convertido es el que informa el evento KeyDown
, etc.
La conversión del código de escaneo al código VK depende de la configuración regional actual: simplísticamente, la configuración regional de entrada define una asignación entre códigos de escaneo y códigos de tecla virtual. Consulte the MSDN documentation para obtener una descripción completa de la entrada del teclado.
Al invertir este proceso de búsqueda, es posible determinar el código de escaneo que corresponde a cada código de tecla virtual (por supuesto, el mismo código de escaneo se correlacionará con múltiples códigos VK debido al estado shift/ctrl/alt, etc.). La API de Win32 proporciona la función MapVirtualKeyEx
para realizar esta asignación, mediante la opción MAPVK_VK_TO_VSC_EX
. Puede usar esto para determinar qué código de exploración genera el código VK particular.
Lamentablemente, esto es lo más lejos que puede ir programáticamente: no hay forma de determinar el diseño físico del teclado o la ubicación de la clave para cada código de exploración. Sin embargo, la mayoría de los teclados físicos están cableados de la misma manera, por lo que (por ejemplo) la tecla de la parte superior izquierda tendrá el mismo código de exploración en la mayoría de los diseños de teclado físico. Puede usar esta convención supuesta para inferir la ubicación física correspondiente a un código de escaneo, dependiendo de la disposición física básica del teclado (101 teclas, 102 teclas, etc.). No está garantizado, pero es una suposición bastante segura.
El siguiente código es un extracto de una librería de manejo de teclado más grande que escribí (he tenido la intención de abrirlo pero no he tenido el tiempo). El método inicializa una matriz (this._virtualKeyScanCodes
) indexada por código VK para una configuración regional de entrada determinada (almacenada en this._inputLanguage
que se declara como System.Windows.Forms.InputLanguage
. Puede usar la matriz para determinar el código de exploración que corresponde al código VK examinando, por ejemplo, this._virtualKeyScanCodes[VK_NUMPAD0]
- si el código de exploración es cero, entonces ese VK no está disponible en el teclado en la configuración regional de entrada actual; si no es cero, es el código de exploración desde el que puede deducir la clave física.
Desafortunadamente, las cosas son un poco más complicadas que esto cuando se entra en el reino de las teclas muertas (combinaciones de teclas múltiples que producen caracteres acentuados, por ejemplo). Eso es demasiado complicado para entrar en este momento, pero Michael S. Kaplan escribió una serie detallada de blog posts si desea explorar más a fondo. ¡Buena suerte!
private void Initialize()
{
this._virtualKeyScanCodes = new uint[MaxVirtualKeys];
// Scroll through the Scan Code (SC) values and get the Virtual Key (VK)
// values in it. Then, store the SC in each valid VK so it can act as both a
// flag that the VK is valid, and it can store the SC value.
for (uint scanCode = 0x01; scanCode <= 0xff; scanCode++)
{
uint virtualKeyCode = NativeMethods.MapVirtualKeyEx(
scanCode,
NativeMethods.MAPVK_VSC_TO_VK,
this._inputLanguage.Handle);
if (virtualKeyCode != 0)
{
this._virtualKeyScanCodes[virtualKeyCode] = scanCode;
}
}
// Add the special keys that do not get added from the code above
for (KeysEx ke = KeysEx.VK_NUMPAD0; ke <= KeysEx.VK_NUMPAD9; ke++)
{
this._virtualKeyScanCodes[(uint)ke] = NativeMethods.MapVirtualKeyEx(
(uint)ke,
NativeMethods.MAPVK_VK_TO_VSC,
this._inputLanguage.Handle);
}
this._virtualKeyScanCodes[(uint)KeysEx.VK_DECIMAL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_DECIMAL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_DIVIDE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_DIVIDE, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_CANCEL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_CANCEL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LSHIFT] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LSHIFT, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RSHIFT] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RSHIFT, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LCONTROL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LCONTROL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RCONTROL] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RCONTROL, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LMENU] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LMENU, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RMENU] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RMENU, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_LWIN] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_LWIN, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_RWIN] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_RWIN, NativeMethods.MAPVK_VK_TO_VSC, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_PAUSE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_PAUSE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_UP] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_VOLUME_UP, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_DOWN] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_VOLUME_DOWN, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_VOLUME_MUTE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_VOLUME_MUTE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_NEXT_TRACK] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_NEXT_TRACK, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_PREV_TRACK] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_PREV_TRACK, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_PLAY_PAUSE] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_PLAY_PAUSE, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._virtualKeyScanCodes[(uint)KeysEx.VK_MEDIA_STOP] =
NativeMethods.MapVirtualKeyEx(
(uint)KeysEx.VK_MEDIA_STOP, NativeMethods.MAPVK_VK_TO_VSC_EX, this._inputLanguage.Handle);
this._stateController = new KeyboardStateController();
this._baseVirtualKeyTable = new VirtualKeyTable(this);
}
Editar: véase también this question que es similar a la suya.
Parece un complemento agradable para que alguien escriba;) – Oded
Puede obtener imágenes de los diseños internacionales de teclado simplemente con Google Images. Prueba buscar en Google [diseño de teclado francés] (http://www.google.com/images?hl=es&q=french+keyboard+layout), por ejemplo. – Timwi
Sí, pero necesito dibujarlo desde cero con un estilo predefinido. Además, si uso diseños, entonces tendría que almacenar todas las imágenes, ¿verdad? Necesito hacer esto en tiempo de ejecución dinámicamente (cuando se inicia el diálogo de personalización). –