Сначала вам понадобится класс для переноса вызовов ОС, необходимых для проверки раскладки клавиатуры:
public class KeyboardPointer : IDisposable
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
[DllImport("user32.dll")]
static extern bool UnloadKeyboardLayout(IntPtr hkl);
[DllImport("user32.dll")]
static extern IntPtr LoadKeyboardLayout(string pwszKLID, uint Flags);
private readonly IntPtr pointer;
public KeyboardPointer(int klid)
{
pointer = LoadKeyboardLayout(klid.ToString("X8"), 1);
}
public KeyboardPointer(CultureInfo culture)
:this(culture.KeyboardLayoutId){}
public void Dispose()
{
UnloadKeyboardLayout(pointer);
GC.SuppressFinalize(this);
}
~KeyboardPointer()
{
UnloadKeyboardLayout(pointer);
}
public bool GetKey(char character, out Keys key)
{
short keyNumber = VkKeyScanEx(character, pointer);
if(keyNumber == -1)
{
key = Keys.None;
return false;
}
key = (System.Windows.Forms.Keys)(((keyNumber & 0xFF00) << 8) | (keyNumber & 0xFF));
return true;
}
}
Тогда, если вы запустите:
Keys atKey;
bool hasAtKey;
using(var keyboard = new KeyboardPointer(CultureInfo.CurrentCulture))
hasAtKey = keyboard.GetKey('@', out atKey);
hasAtKey
будет истинным, если клавиатура имеет комбинацию клавиш для @
, в этом случае atKey
будет содержать соответствующее значение, в то время как в противном случае она будет содержать Keys.None
.
Например, когда я запускаю его, тогда atKey
содержит значение Keys.Oemtilde | Keys.Shift
, но если я изменяю код для передачи в CultureInfo.GetCultureInfo("en-US")
, оно содержит Keys.D2 | Keys.Shift
.
Как вы можете видеть, KeyboardPointer
написан так, чтобы принимать как культуру, так и числовой идентификатор, чтобы вы не ограничивались основной клавиатурой для культуры, а также другими макетами, используемыми с ней, такими как макеты Dvorak и т. Д. 1020 *
Все вышеперечисленное использует вызовы функций Windows32 в Windows, поэтому на других платформах потребуется другой подход.