使用JNA(JAVA)的GetAsyncKeyState和VirtualKeys /特殊字符

我正在进行双向私聊,这将在全屏游戏中运行。

这需要让用户在屏幕顶部键入半透明文本框, 即使它没有焦点也是如此

使用以下代码, 我可以检测所有物理键 ,但使用虚拟键时会很困难。

检测到SHIFT

检测到2

但是, Shift + 2被检测为单独的键(即使[SHIFT+2]在键盘上给出@ )。 IE:程序输出SHIFT和2,但不输出它们产生的内容: @

问题是,我将如何根据键盘转换为角色? 例如:

  • 在英国键盘上,SHIFT + 2会给我" (引号)。
  • 在美国键盘上,SHIFT +2会给我@

如何根据键盘转换为特定字符?

这是到目前为止的代码:

 static interface User32 extends Library { public static User32 INSTANCE = (User32) Native.loadLibrary("User32", User32.class); short GetAsyncKeyState(int key); short GetKeyState(int key); IntByReference GetKeyboardLayout(int dwLayout); int MapVirtualKeyExW (int uCode, int nMapType, IntByReference dwhkl); boolean GetKeyboardState(byte[] lpKeyState); int ToUnicodeEx(int wVirtKey, int wScanCode, byte[] lpKeyState, char[] pwszBuff, int cchBuff, int wFlags, IntByReference dwhkl); } public static void main(String[] args) { long currTime = System.currentTimeMillis(); while (System.currentTimeMillis() < currTime + 20000) { for (int key = 1; key < 256; key++) { if (isKeyPressed(key)) getKeyType(key); } } } private static boolean isKeyPressed(int key) { return User32.INSTANCE.GetAsyncKeyState(key) == -32767; } private static void getKeyType(int key) { boolean isDownShift = (User32.INSTANCE.GetKeyState(VK_SHIFT) & 0x80) == 0x80; boolean isDownCapsLock = (User32.INSTANCE.GetKeyState(VK_CAPS)) != 0; byte[] keystate = new byte[256]; User32.INSTANCE.GetKeyboardState(keystate); IntByReference keyblayoutID = User32.INSTANCE.GetKeyboardLayout(0); int ScanCode = User32.INSTANCE.MapVirtualKeyExW(key, MAPVK_VK_TO_VSC, keyblayoutID); char[] buff = new char[10]; int bufflen = buff.length; int ret = User32.INSTANCE.ToUnicodeEx(key, ScanCode, keystate, buff, bufflen, 0, keyblayoutID); switch (ret) { case -1: System.out.println("Error"); break; case 0: // no translation break; default: System.out.println("output=" + String.valueOf(buff).substring(0, ret)); } } 

它工作正常并输出按下的键,但不适用于Shift +组合。 我意识到我可以做一个“Switch”并将Shift + 3更改为“£”,但这不适用于不同的键盘。

尝试使用JIntelliType库。 它比JNA使用起来更简单,它应该能够做SHIFT + (MOD_SHIFT)。 您可以遇到的唯一问题是检测3 ,但这很容易解决(例如通过KeyListener打印密钥的代码)。

GetKeyboardState有一些问题,但GetAsyncKeyState似乎工作得很好。

这里是完整的控制台应用程序工作示例,它从任何窗口读取键盘状态 在Windows 7上使用2个非en-us键盘布局进行测试。

处理所有内容=),特别是SHIFT +组合(即SHIFT + 3将转换为当前键盘布局的正确字符)

PS David,thanx到你的代码示例我最终想出了MapVirtualKeyExWToUnicodeEx函数的正确参数:)

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); } } 

我知道了。 经过多次,多个小时的搜索,我设法创建了一种方法,将组合转换为当前键盘布局上的组合。 它不处理死键(例如重音符号),但它捕获了我需要捕获的所有[SHIFT+Combinations]

要使用它,请按以下方式调用它:

 getCharacter(int vkCode, boolean shiftKeyPressed); 

所以,看这个魔术。 如果我想得到SHIFT+3将在键盘上给我的东西(£),我使用:

 getCharacter(KeyEvent.VK_3, true); 

这是代码:

 public static char getCharacter(int vkCode, boolean shiftKeyPressed) { byte[] keyStates = new byte[256]; //Create a keyboard map of 256 keys if (shiftKeyPressed) { keyStates[16]=-127; //Emulate the shift key being held down keyStates[160]=-128; //This needs to be set as well } IntByReference keyblayoutID = User32.INSTANCE.GetKeyboardLayout(0); //Load local keyboard layout int ScanCode = User32.INSTANCE.MapVirtualKeyExW(vkCode, MAPVK_VK_TO_VSC, keyblayoutID); //Get the scancode char[] buff = new char[1]; int ret = User32.INSTANCE.ToUnicodeEx(vkCode, ScanCode, keyStates, buff, 1, 0, _currentInputLocaleIdentifier); switch (ret) { case -1: //Error return (char) -1; case 0: //No Translation return (char) 0; default: //Returning key... return buff[0]; } } 

以下是声明:

 final static int MAPVK_VK_TO_VSC = 0; static IntByReference _currentInputLocaleIdentifier; static interface User32 extends Library { public static User32 INSTANCE = (User32) Native.loadLibrary("User32", User32.class); IntByReference GetKeyboardLayout(int dwLayout); int MapVirtualKeyExW (int uCode, int nMapType, IntByReference dwhkl); boolean GetKeyboardState(byte[] lpKeyState); int ToUnicodeEx(int wVirtKey, int wScanCode, byte[] lpKeyState, char[] pwszBuff, int cchBuff, int wFlags, IntByReference dwhkl); } 

非常感谢BrendanMcK,他帮助我找到了这个解决方案。