Низкоуровневый ввод с клавиатуры из Windows

Какие вызовы win32 можно использовать для глобального обнаружения событий нажатия клавиш (не только для 1 окна, я хотел бы получать сообщение КАЖДЫЙ раз при нажатии клавиши) из службы Windows?


person dicroce    schedule 21.11.2008    source источник


Ответы (5)


Вы хотите использовать хуки Win32. В частности, крючок для клавиатуры.

Подробнее об этом можно прочитать здесь

Тип хука, который вам нужен, — WH_KEYBOARD, и вы можете установить его через Win32 API SetWindowsHookEx.

В основном окна будут вызывать функцию в dll, которую вы создаете каждый раз, когда нажимается клавиша в любой системе приложений.

Хук вызовет вашу функцию, которая будет иметь этот интерфейс:

LRESULT CALLBACK KeyboardProc(      
    int code,
    WPARAM wParam,
    LPARAM lParam
);

Дополнительная информация об этом обратном вызове здесь.

С помощью перехватчиков Windows вы можете не только отслеживать общесистемные события во всех процессах, но также можете фильтровать их и полностью останавливать.

person Brian R. Bondy    schedule 22.11.2008

Взгляните на хуки, которые вы можете установить с помощью SetWindowHookEx:

http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx

Однако, если вы не используете «интерактивную» службу, у вас не будет доступа к рабочему столу (и вам не следует запускать интерактивную службу, потому что она не будет правильно работать в более новых версиях Windows). Вам нужно будет изучить API-интерфейсы управления сеансом и рабочим столом, чтобы получить доступ к рабочему столу/консоли.

person jdigital    schedule 22.11.2008

Ознакомьтесь с API необработанного ввода в MSDN:

http://msdn.microsoft.com/en-us/library/ms645536(VS.85).aspx

Это позволяет вам получать ввод с клавиатуры (среди прочего), не возясь с глобальными хуками. Глобальные хуки следует использовать только в крайнем случае, поскольку они могут привести к непредвиденным последствиям.

Единственным недостатком использования необработанного ввода является то, что он может неправильно принимать нажатия клавиш, генерируемые программным обеспечением.

person Kennet Belenky    schedule 22.11.2008

Просто используйте встроенную функцию GetKeyState, или GetAsyncKeyState, или GetKeyboardState из библиотеки user32.

Вы можете прочитать о каждой упомянутой выше функции на сайте msdn:

ч http://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx

для GetKeyState и

ч http://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v=vs.85).aspx

для GetAsyncKeyState и

ч http://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 как истину или 0 как ложь, то вместо этого вы можете изменить их тип возвращаемого значения на bool, но если вы сделаете это, то, я думаю, вам следует добавить еще одну строку кода выше собственное определение функции, а именно:

[MarshalAs(UnmanagedType.Bool)]

ИЛИ вы можете добавить следующую строку в середине определения встроенной функции (под строкой, где находится слово DllImport, и над строкой, где находится слово «extern»):

[return: MarshalAs(UnmanagedType.Bool)]

Сайт Pinvoke предлагает лучшее определение для этих функций, так что вам не нужно использовать целые числа, чтобы указать конкретную клавишу вашей клавиатуры, но более понятное перечисление.

Следующая ссылка ведет к определению GetKeyState в C#:

ч http://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
           }
}
person Community    schedule 07.08.2014

Если вы реализуете это в приложении .net, я бы рекомендовал GlobalKeyboardHook, иначе я бы посмотрел на его исходный код и взял то, что вам нужно, так как он реализует функции DLLImport, упомянутые выше.

person abc123    schedule 22.01.2013