Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

376 rader
18 KiB

  1. // This code is distributed under MIT license.
  2. // Copyright (c) 2015 George Mamaladze
  3. // See license.txt or https://mit-license.org/
  4. using System;
  5. using System.Runtime.InteropServices;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using Gma.System.MouseKeyHook.Implementation;
  9. namespace Gma.System.MouseKeyHook.WinApi
  10. {
  11. internal static class KeyboardNativeMethods
  12. {
  13. //values from Winuser.h in Microsoft SDK.
  14. public const byte VK_SHIFT = 0x10;
  15. public const byte VK_CAPITAL = 0x14;
  16. public const byte VK_NUMLOCK = 0x90;
  17. public const byte VK_LSHIFT = 0xA0;
  18. public const byte VK_RSHIFT = 0xA1;
  19. public const byte VK_LCONTROL = 0xA2;
  20. public const byte VK_RCONTROL = 0xA3;
  21. public const byte VK_LMENU = 0xA4;
  22. public const byte VK_RMENU = 0xA5;
  23. public const byte VK_LWIN = 0x5B;
  24. public const byte VK_RWIN = 0x5C;
  25. public const byte VK_SCROLL = 0x91;
  26. public const byte VK_INSERT = 0x2D;
  27. //may be possible to use these aggregates instead of L and R separately (untested)
  28. public const byte VK_CONTROL = 0x11;
  29. public const byte VK_MENU = 0x12;
  30. public const byte VK_PACKET = 0xE7;
  31. //Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods
  32. private static int lastVirtualKeyCode;
  33. private static int lastScanCode;
  34. private static byte[] lastKeyState = new byte[255];
  35. private static bool lastIsDead;
  36. /// <summary>
  37. /// Translates a virtual key to its character equivalent using the current keyboard layout without knowing the
  38. /// scancode in advance.
  39. /// </summary>
  40. /// <param name="virtualKeyCode"></param>
  41. /// <param name="fuState"></param>
  42. /// <param name="chars"></param>
  43. /// <returns></returns>
  44. internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int fuState, out char[] chars)
  45. {
  46. var dwhkl = GetActiveKeyboard();
  47. var scanCode = MapVirtualKeyEx(virtualKeyCode, (int) MapType.MAPVK_VK_TO_VSC, dwhkl);
  48. TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars);
  49. }
  50. /// <summary>
  51. /// Translates a virtual key to its character equivalent using the current keyboard layout
  52. /// </summary>
  53. /// <param name="virtualKeyCode"></param>
  54. /// <param name="scanCode"></param>
  55. /// <param name="fuState"></param>
  56. /// <param name="chars"></param>
  57. /// <returns></returns>
  58. internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState,
  59. out char[] chars)
  60. {
  61. var dwhkl = GetActiveKeyboard(); //get the active keyboard layout
  62. TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars);
  63. }
  64. /// <summary>
  65. /// Translates a virtual key to its character equivalent using a specified keyboard layout
  66. /// </summary>
  67. /// <param name="virtualKeyCode"></param>
  68. /// <param name="scanCode"></param>
  69. /// <param name="fuState"></param>
  70. /// <param name="dwhkl"></param>
  71. /// <param name="chars"></param>
  72. /// <returns></returns>
  73. internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState, IntPtr dwhkl,
  74. out char[] chars)
  75. {
  76. var pwszBuff = new StringBuilder(64);
  77. var keyboardState = KeyboardState.GetCurrent();
  78. var currentKeyboardState = keyboardState.GetNativeState();
  79. var isDead = false;
  80. if (keyboardState.IsDown(Keys.ShiftKey))
  81. currentKeyboardState[(byte) Keys.ShiftKey] = 0x80;
  82. if (keyboardState.IsToggled(Keys.CapsLock))
  83. currentKeyboardState[(byte) Keys.CapsLock] = 0x01;
  84. var relevantChars = ToUnicodeEx(virtualKeyCode, scanCode, currentKeyboardState, pwszBuff, pwszBuff.Capacity,
  85. fuState, dwhkl);
  86. switch (relevantChars)
  87. {
  88. case -1:
  89. isDead = true;
  90. ClearKeyboardBuffer(virtualKeyCode, scanCode, dwhkl);
  91. chars = null;
  92. break;
  93. case 0:
  94. chars = null;
  95. break;
  96. case 1:
  97. if (pwszBuff.Length > 0) chars = new[] {pwszBuff[0]};
  98. else chars = null;
  99. break;
  100. // Two or more (only two of them is relevant)
  101. default:
  102. if (pwszBuff.Length > 1) chars = new[] {pwszBuff[0], pwszBuff[1]};
  103. else chars = new[] {pwszBuff[0]};
  104. break;
  105. }
  106. if (lastVirtualKeyCode != 0 && lastIsDead)
  107. {
  108. if (chars != null)
  109. {
  110. var sbTemp = new StringBuilder(5);
  111. ToUnicodeEx(lastVirtualKeyCode, lastScanCode, lastKeyState, sbTemp, sbTemp.Capacity, 0, dwhkl);
  112. lastIsDead = false;
  113. lastVirtualKeyCode = 0;
  114. }
  115. return;
  116. }
  117. lastScanCode = scanCode;
  118. lastVirtualKeyCode = virtualKeyCode;
  119. lastIsDead = isDead;
  120. lastKeyState = (byte[]) currentKeyboardState.Clone();
  121. }
  122. private static void ClearKeyboardBuffer(int vk, int sc, IntPtr hkl)
  123. {
  124. var sb = new StringBuilder(10);
  125. int rc;
  126. do
  127. {
  128. var lpKeyStateNull = new byte[255];
  129. rc = ToUnicodeEx(vk, sc, lpKeyStateNull, sb, sb.Capacity, 0, hkl);
  130. } while (rc < 0);
  131. }
  132. /// <summary>
  133. /// Gets the input locale identifier for the active application's thread. Using this combined with the ToUnicodeEx and
  134. /// MapVirtualKeyEx enables Windows to properly translate keys based on the keyboard layout designated for the
  135. /// application.
  136. /// </summary>
  137. /// <returns>HKL</returns>
  138. private static IntPtr GetActiveKeyboard()
  139. {
  140. var hActiveWnd = ThreadNativeMethods.GetForegroundWindow(); //handle to focused window
  141. int dwProcessId;
  142. var hCurrentWnd = ThreadNativeMethods.GetWindowThreadProcessId(hActiveWnd, out dwProcessId);
  143. //thread of focused window
  144. return GetKeyboardLayout(hCurrentWnd); //get the layout identifier for the thread whose window is focused
  145. }
  146. /// <summary>
  147. /// The ToAscii function translates the specified virtual-key code and keyboard
  148. /// state to the corresponding character or characters. The function translates the code
  149. /// using the input language and physical keyboard layout identified by the keyboard layout handle.
  150. /// </summary>
  151. /// <param name="uVirtKey">
  152. /// [in] Specifies the virtual-key code to be translated.
  153. /// </param>
  154. /// <param name="uScanCode">
  155. /// [in] Specifies the hardware scan code of the key to be translated.
  156. /// The high-order bit of this value is set if the key is up (not pressed).
  157. /// </param>
  158. /// <param name="lpbKeyState">
  159. /// [in] Pointer to a 256-byte array that contains the current keyboard state.
  160. /// Each element (byte) in the array contains the state of one key.
  161. /// If the high-order bit of a byte is set, the key is down (pressed).
  162. /// The low bit, if set, indicates that the key is toggled on. In this function,
  163. /// only the toggle bit of the CAPS LOCK key is relevant. The toggle state
  164. /// of the NUM LOCK and SCROLL LOCK keys is ignored.
  165. /// </param>
  166. /// <param name="lpwTransKey">
  167. /// [out] Pointer to the buffer that receives the translated character or characters.
  168. /// </param>
  169. /// <param name="fuState">
  170. /// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
  171. /// </param>
  172. /// <returns>
  173. /// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values.
  174. /// Value Meaning
  175. /// 0 The specified virtual key has no translation for the current state of the keyboard.
  176. /// 1 One character was copied to the buffer.
  177. /// 2 Two characters were copied to the buffer. This usually happens when a dead-key character
  178. /// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified
  179. /// virtual key to form a single character.
  180. /// </returns>
  181. /// <remarks>
  182. /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
  183. /// </remarks>
  184. [Obsolete("Use ToUnicodeEx instead")]
  185. [DllImport("user32.dll")]
  186. public static extern int ToAscii(
  187. int uVirtKey,
  188. int uScanCode,
  189. byte[] lpbKeyState,
  190. byte[] lpwTransKey,
  191. int fuState);
  192. /// <summary>
  193. /// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters.
  194. /// </summary>
  195. /// <param name="wVirtKey">[in] The virtual-key code to be translated.</param>
  196. /// <param name="wScanCode">
  197. /// [in] The hardware scan code of the key to be translated. The high-order bit of this value is
  198. /// set if the key is up.
  199. /// </param>
  200. /// <param name="lpKeyState">
  201. /// [in, optional] A pointer to a 256-byte array that contains the current keyboard state. Each
  202. /// element (byte) in the array contains the state of one key. If the high-order bit of a byte is set, the key is down.
  203. /// </param>
  204. /// <param name="pwszBuff">
  205. /// [out] The buffer that receives the translated Unicode character or characters. However, this
  206. /// buffer may be returned without being null-terminated even though the variable name suggests that it is
  207. /// null-terminated.
  208. /// </param>
  209. /// <param name="cchBuff">[in] The size, in characters, of the buffer pointed to by the pwszBuff parameter.</param>
  210. /// <param name="wFlags">
  211. /// [in] The behavior of the function. If bit 0 is set, a menu is active. Bits 1 through 31 are
  212. /// reserved.
  213. /// </param>
  214. /// <param name="dwhkl">The input locale identifier used to translate the specified code.</param>
  215. /// <returns>
  216. /// -1 &lt;= return &lt;= n
  217. /// <list type="bullet">
  218. /// <item>
  219. /// -1 = The specified virtual key is a dead-key character (accent or diacritic). This value is returned
  220. /// regardless of the keyboard layout, even if several characters have been typed and are stored in the
  221. /// keyboard state. If possible, even with Unicode keyboard layouts, the function has written a spacing version
  222. /// of the dead-key character to the buffer specified by pwszBuff. For example, the function writes the
  223. /// character SPACING ACUTE (0x00B4), rather than the character NON_SPACING ACUTE (0x0301).
  224. /// </item>
  225. /// <item>
  226. /// 0 = The specified virtual key has no translation for the current state of the keyboard. Nothing was
  227. /// written to the buffer specified by pwszBuff.
  228. /// </item>
  229. /// <item> 1 = One character was written to the buffer specified by pwszBuff.</item>
  230. /// <item>
  231. /// n = Two or more characters were written to the buffer specified by pwszBuff. The most common cause
  232. /// for this is that a dead-key character (accent or diacritic) stored in the keyboard layout could not be
  233. /// combined with the specified virtual key to form a single character. However, the buffer may contain more
  234. /// characters than the return value specifies. When this happens, any extra characters are invalid and should
  235. /// be ignored.
  236. /// </item>
  237. /// </list>
  238. /// </returns>
  239. [DllImport("user32.dll")]
  240. public static extern int ToUnicodeEx(int wVirtKey,
  241. int wScanCode,
  242. byte[] lpKeyState,
  243. [Out] [MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder pwszBuff,
  244. int cchBuff,
  245. int wFlags,
  246. IntPtr dwhkl);
  247. /// <summary>
  248. /// The GetKeyboardState function copies the status of the 256 virtual keys to the
  249. /// specified buffer.
  250. /// </summary>
  251. /// <param name="pbKeyState">
  252. /// [in] Pointer to a 256-byte array that contains keyboard key states.
  253. /// </param>
  254. /// <returns>
  255. /// If the function succeeds, the return value is nonzero.
  256. /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
  257. /// </returns>
  258. /// <remarks>
  259. /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
  260. /// </remarks>
  261. [DllImport("user32.dll")]
  262. public static extern int GetKeyboardState(byte[] pbKeyState);
  263. /// <summary>
  264. /// The GetKeyState function retrieves the status of the specified virtual key. The status specifies whether the key is
  265. /// up, down, or toggled
  266. /// (on, off—alternating each time the key is pressed).
  267. /// </summary>
  268. /// <param name="vKey">
  269. /// [in] Specifies a virtual key. If the desired virtual key is a letter or digit (A through Z, a through z, or 0
  270. /// through 9), nVirtKey must be set to the ASCII value of that character. For other keys, it must be a virtual-key
  271. /// code.
  272. /// </param>
  273. /// <returns>
  274. /// The return value specifies the status of the specified virtual key, as follows:
  275. /// If the high-order bit is 1, the key is down; otherwise, it is up.
  276. /// If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The
  277. /// key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be
  278. /// on when the key is toggled, and off when the key is untoggled.
  279. /// </returns>
  280. /// <remarks>http://msdn.microsoft.com/en-us/library/ms646301.aspx</remarks>
  281. [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  282. public static extern short GetKeyState(int vKey);
  283. /// <summary>
  284. /// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a
  285. /// virtual-key code.
  286. /// </summary>
  287. /// <param name="uCode">
  288. /// [in] The virtual key code or scan code for a key. How this value is interpreted depends on the
  289. /// value of the uMapType parameter.
  290. /// </param>
  291. /// <param name="uMapType">
  292. /// [in] The translation to be performed. The value of this parameter depends on the value of the
  293. /// uCode parameter.
  294. /// </param>
  295. /// <param name="dwhkl">[in] The input locale identifier used to translate the specified code.</param>
  296. /// <returns></returns>
  297. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  298. internal static extern int MapVirtualKeyEx(int uCode, int uMapType, IntPtr dwhkl);
  299. /// <summary>
  300. /// Retrieves the active input locale identifier (formerly called the keyboard layout) for the specified thread.
  301. /// If the idThread parameter is zero, the input locale identifier for the active thread is returned.
  302. /// </summary>
  303. /// <param name="dwLayout">[in] The identifier of the thread to query, or 0 for the current thread. </param>
  304. /// <returns>
  305. /// The return value is the input locale identifier for the thread. The low word contains a Language Identifier for the
  306. /// input
  307. /// language and the high word contains a device handle to the physical layout of the keyboard.
  308. /// </returns>
  309. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  310. internal static extern IntPtr GetKeyboardLayout(int dwLayout);
  311. /// <summary>
  312. /// MapVirtualKeys uMapType
  313. /// </summary>
  314. internal enum MapType
  315. {
  316. /// <summary>
  317. /// uCode is a virtual-key code and is translated into an unshifted character value in the low-order word of the return
  318. /// value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation,
  319. /// the function returns 0.
  320. /// </summary>
  321. MAPVK_VK_TO_VSC,
  322. /// <summary>
  323. /// uCode is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not
  324. /// distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the
  325. /// function returns 0.
  326. /// </summary>
  327. MAPVK_VSC_TO_VK,
  328. /// <summary>
  329. /// uCode is a scan code and is translated into a virtual-key code that does not distinguish between left- and
  330. /// right-hand keys. If there is no translation, the function returns 0.
  331. /// </summary>
  332. MAPVK_VK_TO_CHAR,
  333. /// <summary>
  334. /// uCode is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand
  335. /// keys. If there is no translation, the function returns 0.
  336. /// </summary>
  337. MAPVK_VSC_TO_VK_EX
  338. }
  339. }
  340. }