GetKeyboardState
имеет некоторые проблемы, но GetAsyncKeyState
, кажется, работает просто отлично.
Вот полный рабочий пример консольного приложения, которое считывает состояние клавиатуры из любого окна.Протестировано с двумя раскладками клавиатуры, которые не используются в Windows 7.
Обрабатывает все =) и, в частности, комбинации SHIFT + (т. Е. SHIFT + 3 будет преобразовано в правильный символ для текущей раскладки клавиатуры)
PS Дэвид, спасибо твоему примеру кода. Я наконец-то определил правильные параметры для функций MapVirtualKeyExW
и ToUnicodeEx
:)
PPS Код написан на C #, но, думаю, его легко перенести наJava (поскольку, когда я читал ваш код, я по ошибке предположил, что это C #, и только намного позже заметил «JAVA» в заголовке вопроса)
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace KeyboardInputTest
{
class Program
{
static void Main(string[] args)
{
new KeyboardTestClass().RunTest();
}
}
public class KeyboardTestClass
{
public void RunTest()
{
while (true)
{
string keyString = string.Empty;
if (ReadKeyboardInput(ref keyString) && keyString.Length > 0)
{
Console.WriteLine(string.Format("Pressed: {0}", keyString));
}
Thread.Sleep(10);
}
}
public bool ReadKeyboardInput(ref string res)
{
var hwnd = WinAPI.GetForegroundWindow();
var pid = WinAPI.GetWindowThreadProcessId(hwnd, IntPtr.Zero);
var keyboardLayoutHandle = WinAPI.GetKeyboardLayout(pid);
foreach (var key in (Keys[])Enum.GetValues(typeof(Keys)))
{
if (Keyboard.GetAsyncKeyState(key) == -32767)
{
switch (key)
{
// handle exceptional cases
case Keys.Enter:
case Keys.LineFeed:
res = string.Empty;
return false;
}
res = ConvertVirtualKeyToUnicode(key, keyboardLayoutHandle, Keyboard.ShiftKey);
return true;
}
}
return false;
}
public string ConvertVirtualKeyToUnicode(Keys key, IntPtr keyboardLayoutHandle, bool shiftPressed)
{
var scanCodeEx = Keyboard.MapVirtualKeyExW(key, VirtualKeyMapType.ToVScanCodeEx, keyboardLayoutHandle);
if (scanCodeEx > 0)
{
byte[] lpKeyState = new byte[256];
if (shiftPressed)
{
lpKeyState[(int)Keys.ShiftKey] = 0x80;
lpKeyState[(int)Keys.LShiftKey] = 0x80;
}
var sb = new StringBuilder(5);
var rc = Keyboard.ToUnicodeEx(key, scanCodeEx, lpKeyState, sb, sb.Capacity, 0, keyboardLayoutHandle);
if (rc > 0)
{
return sb.ToString();
}
else
{
// It's a dead key; let's flush out whats stored in the keyboard state.
rc = Keyboard.ToUnicodeEx(key, scanCodeEx, lpKeyState, sb, sb.Capacity, 0, keyboardLayoutHandle);
return string.Empty;
}
}
return string.Empty;
}
}
// Win API Imports:
public enum VirtualKeyMapType : int
{
ToChar = 2,
ToVScanCode = 0,
ToVScanCodeEx = 4
}
public static class Keyboard
{
public static bool ShiftKey
{
get
{
return Convert.ToBoolean((int)GetAsyncKeyState(Keys.ShiftKey) & 32768);
}
}
[DllImport("User32.dll")]
public static extern short GetAsyncKeyState(Keys vKey);
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "MapVirtualKeyExW", ExactSpelling = true)]
public static extern uint MapVirtualKeyExW(Keys uCode, VirtualKeyMapType uMapType, IntPtr dwKeyboardLayoutHandle);
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern int ToUnicodeEx(Keys wVirtKey, uint wScanCode, byte[] lpKeyState, StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwKeyboardLayoutHandle);
}
public class WinAPI
{
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32")]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, IntPtr lpdwProcessId);
[DllImport("user32")]
public static extern IntPtr GetKeyboardLayout(int dwLayout);
}
}