Low level keyboard input from Windows
Какие вызовы win32 можно использовать для глобального обнаружения событий нажатия клавиш (не только для 1 окна, я хотел бы получать сообщение КАЖДЫЙ раз при нажатии клавиши) из службы Windows?
5 ответов
Вы хотите использовать Win32 Hooks. В частности, крючок для клавиатуры.
Вы можете прочитать больше об этом здесь
Тип хука, который вы хотите, - WH_KEYBOARD, и вы можете установить его через Win32 API SetWindowsHookEx.
В основном, Windows будет вызывать функцию в DLL, которую вы создаете при каждом нажатии клавиши в любой прикладной системе.
Хук вызовет вашу функцию, которая будет иметь этот интерфейс:
LRESULT CALLBACK KeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam
);
Больше информации об этом обратном звонке здесь.
С помощью хуков Windows вы можете не только отслеживать общесистемные события во всех процессах, но и фильтровать их, а также полностью их останавливать.
Проверьте Raw Input API в MSDN:
http://msdn.microsoft.com/en-us/library/ms645536(VS.85).aspx
Это позволяет вам получать ввод с клавиатуры (среди прочего), не возиться с глобальными перехватами. Глобальные хуки должны использоваться только в качестве крайней меры, поскольку они могут привести к непредвиденным последствиям.
Единственным недостатком использования необработанного ввода является то, что он может неправильно получать нажатия клавиш, генерируемые программным обеспечением.
Посмотрите на хуки, которые вы можете установить с помощью SetWindowHookEx:
http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx
Однако, если вы не используете "интерактивный" сервис, у вас не будет доступа к рабочему столу (и вам не следует запускать интерактивный сервис, потому что он не будет работать правильно в новых версиях Windows). Вам придется изучить API-интерфейсы управления сеансом и рабочим столом, чтобы получить доступ к рабочему столу / консоли.
Просто используйте встроенную функцию GetKeyState или GetAsyncKeyState или GetKeyboardState из библиотеки user32.
Вы можете прочитать о каждой функции, о которой я упоминал выше, на сайте msdn:
h ttp: //msdn.microsoft.com/en-us/library/windows/desktop/ms646301 (v = vs.85).aspx
для GetKeyState и
h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v=vs.85).aspx
для GetAsyncKeyState и
h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646299(v=vs.85).aspx
для GetKeyboardState.
Если вы программируете на C#, то:
Ваш код должен включать следующую строку из любого объявления класса, структуры или перечисления:
using System.Runtime.InteropServices;
Затем в классе, у которого есть метод, в котором вы хотите определить ключи, определите одну из трех функций выше для вашего выбора, или что работает, а что нет.
[DllImport("user32.dll")]
static uint GetKeyState(byte virtualKeyCode);
//or
[DllImport("user32.dll")]
static uint GetAsyncKeyState(byte virtualKeyCode);
//or
[DllImport("user32.dll")]
static uint GetKeyboardState(byte[] virtualKeyCodes);
//NOTE: The length of the byte array parameter must be always 256 bytes!
Если вам не нравится uint как тип возвращаемого значения, вы также можете изменить его на int.
Если нативные функции всегда возвращают 1 как true или 0 как false, тогда вы можете вместо этого изменить их тип возврата на bool, но если вы это сделаете, то я думаю, что вы должны добавить еще одну строку кода над определением нативной функции:
[MarshalAs(UnmanagedType.Bool)]
ИЛИ вы можете добавить следующую строку в середине определения нативной функции (ниже строки, где находится слово DllImport, и выше строки, где находится слово 'extern'):
[return: MarshalAs(UnmanagedType.Bool)]
Сайт Pinvoke предлагает лучшее определение для этих функций, так что вам не нужно использовать целые числа, чтобы упомянуть конкретную клавишу вашей клавиатуры, но более понятное перечисление.
Следующая ссылка ведет к определению GetKeyState в C#:
h ttp: //www.pinvoke.net/default.aspx/user32.getkeystate
Следующая ссылка ведет к определению GetAsyncKeyState в C#
h ttp: //www.pinvoke.net/default.aspx/user32.getasynckeystate
Следующая ссылка ведет к определению GetKeyboardState в C#
h ttp: //www.pinvoke.net/default.aspx/user32.getkeyboardstate
Чтобы определить, какая клавиша удерживается (в любое время и в любом месте), затем вызовите функцию GetKeyState или GetAsyncKeyState в одном параметре, укажите ключ, который вы хотите обнаружить, а затем добавьте: & 0x8000 или & 0x80 после вызова. Если упомянутый ключ удерживается нажатым, то возвращаемое значение выражения не равно нулю, но в противном случае оно равно нулю. По возвращенному целому числу выражения вы можете определить, какая клавиша удерживается или нет. Обратите внимание, что если вы поместите код внутри оператора if с этим выражением!= 0 в качестве условия ввода, которое находится внутри цикла, код в этом операторе if будет выполняться несколько раз, фактически много раз, когда вы удерживаете эту клавишу. Если вы хотите, чтобы какой-то код выполнялся один раз. Когда вы нажимаете клавишу, а затем отпускаете ее, следующий код - это трюк, который достигает этой цели:
while (boolean exression that will return false when you want to quit this loop)
{
if (boolean expression that will return true when particular key is **held down**) //This boolean expression calls either GetKeyState or GetAsyncKeyState with the & 0x8000 or & 0x80 after the call, and then != 0 to check for **held down** key
while (true) //It's purpose is to wait for the same key that was held down to be released. After code execution, it will encounter the break keyword that will finish him, even though that his enter condition is true.
if (boolean expression that will return true when the **same** particular key is **NOT** held down! (NOT held down, means that at that time, that key was released). You can copy the same condition from the first if statement, and just change **!=**, which is next to 0, to **==**, or instead add brackets to the condition '(' and ')', and in left of '(' add '!').
{
//Put here the code that you want to execute once, when paticular key was pressed and then released.
break; //the while (true), which is the only way to get out of it
}
}
Если вы реализуете это в приложении.net, я бы порекомендовал GlobalKeyboardHook, в противном случае я бы посмотрел на его исходный код и взял бы то, что вам нужно, так как он реализует функции DLLImport, упомянутые выше.