Un tasto stampa genera un evento hardware che segnala un "codice di scansione" al sistema operativo Windows. Questo codice di scansione viene poi convertito in un "codice tasto virtuale", basato sul codice di scansione insieme ad altri fattori stato della tastiera (Caps Lock stato, Maiusc/Alt/Ctrl keystate, e anche un tasto qualsiasi morti in attesa ictus). Il valore VK convertito è quello che viene segnalato dall'evento KeyDown
ecc.
La conversione dal codice di scansione al codice VK dipende dalla locale di input corrente. Semplicisticamente, la locale di input definisce una mappatura tra codici di scansione e codici di chiave virtuali. Vedi the MSDN documentation per una descrizione completa dell'input da tastiera.
Invertendo questo processo di ricerca, è possibile determinare il codice di scansione che corrisponde a ciascun codice tasto virtuale (ovviamente, lo stesso codice di scansione si associerà a più codici VK a causa di shift/ctrl/alt stato ecc.). L'API Win32 fornisce la funzione MapVirtualKeyEx
per eseguire questa mappatura, utilizzando l'opzione MAPVK_VK_TO_VSC_EX
. È possibile utilizzare questo per determinare quale codice di scansione genera il particolare codice VK.
Purtroppo, questo è quanto si può andare di programmazione - non v'è alcun modo per determinare la disposizione fisica della tastiera o la posizione della chiave per ogni codice di scansione. Tuttavia, la maggior parte delle tastiere fisiche sono cablate allo stesso modo, quindi (ad esempio) il tasto in alto a sinistra avrà lo stesso codice di scansione sulla maggior parte dei progetti di tastiera fisica. È possibile utilizzare questa convenzione presunta per dedurre la posizione fisica corrispondente a un codice di scansione, a seconda del layout di base della tastiera fisica (101 tasti, 102 tasti, ecc.). Non è garantito, ma è un'ipotesi abbastanza sicura.
Il codice seguente è un estratto da una grande libreria di tastiera di gestione che ho scritto (ho l'intenzione di open-source, ma non ho avuto il tempo). Il metodo inizializza una matrice (this._virtualKeyScanCodes
) indicizzata dal codice VK per una determinata lingua di input (memorizzata in this._inputLanguage
che è dichiarata come System.Windows.Forms.InputLanguage
. È possibile utilizzare la matrice per determinare il codice di scansione che corrisponde al codice VK esaminando ad esempio this._virtualKeyScanCodes[VK_NUMPAD0]
- se il codice di scansione è zero, quel VK non è disponibile sulla tastiera nella locale di input corrente, se è diverso da zero, è il codice di scansione da cui è possibile dedurre la chiave fisica.
Sfortunatamente, le cose sono leggermente più complicate di questo quando si entra nel regno delle chiavi morte (combinazioni di tasti multipli che producono caratteri accentati, per esempio). È tutto troppo complicato per entrare in questo momento, ma Michael S. Kaplan ha scritto una serie dettagliata di blog posts se vuoi approfondire ulteriormente. In bocca al lupo!
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);
}
Edit: vedi anche this question che è simile alla tua.
Sembra un buon add-on per qualcuno da scrivere;) – Oded
È possibile ottenere immagini dei layout di tastiera internazionali semplicemente utilizzando Google Immagini. Prova googling per [layout di tastiera francese] (http://www.google.com/images?hl=it&q=french+keyboard+layout), ad esempio. – Timwi
Sì, ma ho bisogno di disegnarlo da zero con uno stile predefinito. Inoltre, se utilizzo i layout, avrei bisogno di memorizzare tutte le immagini, giusto? Devo farlo dinamicamente al runtime (quando viene avviata la finestra di dialogo Personalizza). –