Иногда, во время написания программ ил игр, требуется перехватить нажатия клавиш на форме приложения. Многим сразу вспомниться событие KeyDown. Казалось бы, что сложного повесить обработчик на событие KeyDown?! Но, если добавить на форму контрол, который перехватывает фокус, то обработчик события начинает вести себя не так, как хотелось бы. Дело в том, что вызывает обработчик активного контрола, например кнопки, и не передается остальным обработчикам. Можно, конечно, всем контролам ставить один обработчик события. Но это не удобно, и могут в последствии появиться подводные камни.. В этой мы решим эту проблему.
В этой статье мы будем использовать ловушки - Hook.
Для начала объявим класс, назовем его KeyHookDecl.
И первое, что нам понадобиться, это функции WinApi - SetWindowsHookEx, CallNextHookEx и UnhookWindowsHookEx.
Объявим их и делегат HookProc, из материала msdn, в классе KeyHookDecl:
Итак, теперь читаем информацию по функции SetWindowsHookEx в msdn.
Нам понадобиться следующая информация:
Если перевести то получается:
Это как раз то, что нам нужно, и теперь в классе объявим константу WH_KEYBOARD:
Теперь напишем конструктор, объявим функцию HookProc и переменную hhook. Также нам понадобиться функция GetModuleHandle, которую мы импортируем из kernel32.dll.
идем на msdn. На интересует параметр lParam и wParam.
wParam [in]
lParam [in]
В этой статье мы будем использовать ловушки - Hook.
Для начала объявим класс, назовем его KeyHookDecl.
class KeyHookDecl
{
}
И первое, что нам понадобиться, это функции WinApi - SetWindowsHookEx, CallNextHookEx и UnhookWindowsHookEx.
Объявим их и делегат HookProc, из материала msdn, в классе KeyHookDecl:
public delegate int HookProc(int code, int wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]Только не забываем в подключить System.Runtime.InteropServices.
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern bool UnhookWindowsHookEx(int hhk);
Итак, теперь читаем информацию по функции SetWindowsHookEx в msdn.
Нам понадобиться следующая информация:
|
Installs a hook procedure that monitors keystroke messages.
|
Если перевести то получается:
|
Устанавливает процедуру, которая отслеживает нажатия клавиш.
|
Это как раз то, что нам нужно, и теперь в классе объявим константу WH_KEYBOARD:
public const int WH_KEYBOARD = 2;Итак, теперь объявляем основной класс KeyHook:
public class KeyHookТеперь первое что нам нужно объявить события KeyDown, KeyUp.
{
}
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;И не забываем про using System.Windows.Forms, так как именно там объявлены KeyEventHandler.
Теперь напишем конструктор, объявим функцию HookProc и переменную hhook. Также нам понадобиться функция GetModuleHandle, которую мы импортируем из kernel32.dll.
Все, наш перехватчик уже функционирует, осталось написать обработчик сообщений, для этого
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string lpModuleName);
int hhook;// хэндл созданой нами ловушки
int HookProc(int code, int wParam, IntPtr lParam)
{
return KeyHookDecl.CallNextHookEx(hhook, code, wParam, lParam);// Вызов следующей ловушки в очереди
}
public KeyHook()
{
// Создание ловушки
hhook = KeyHookDecl.SetWindowsHookEx(KeyHookDecl.WH_KEYBOARD, new KeyHookDecl.HookProc(HookProc), GetModuleHandle(null), AppDomain.GetCurrentThreadId());
}
идем на msdn. На интересует параметр lParam и wParam.
Type: WPARAM
The virtual-key code of the key that generated the keystroke message.
Type: LPARAM
The repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag. For more information about the lParam parameter, see Keystroke Message Flags. The following table describes the bits of this value.
Bits | Description |
---|---|
0-15 | The repeat count. The value is the number of times the keystroke is repeated as a result of the user's holding down the key. |
16-23 | The scan code. The value depends on the OEM. |
24 | Indicates whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0. |
25-28 | Reserved. |
29 | The context code. The value is 1 if the ALT key is down; otherwise, it is 0. |
30 | The previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up. |
31 | The transition state. The value is 0 if the key is being pressed and 1 if it is being released. |
int HookProc(int code, int wParam, IntPtr lParam)
{
BitArray bt = new BitArray(BitConverter.GetBytes((uint)lParam));// для доступа к битам
KeyEventArgs arg = new KeyEventArgs((Keys)wParam);
//Если клавиша нажата - вызвать KeyDown, если отпущена - вызвать KeyUp
if (bt[30]) { if (KeyUp != null) KeyUp(this, arg); } else if (KeyDown != null) KeyDown(this, arg);
KeyHookDecl.CallNextHookEx(hhook, code, wParam, lParam);// Вызов следующей ловушки в очереди
return 1;
}
Теперь напишем деструктор:
Вот и все, теперь этим можно пользоваться так:
private void Form1_Load(object sender, EventArgs e)
{
KeyHook kh = new KeyHook();
kh.KeyDown += new KeyEventHandler(kh_KeyDown);
kh.KeyUp += new KeyEventHandler(kh_KeyUp);
}
void kh_KeyUp(object sender, KeyEventArgs e)
{
textBox1.Text += "UP " + e.KeyCode.ToString() + Environment.NewLine;
}
void kh_KeyDown(object sender, KeyEventArgs e)
{
textBox1.Text += "DOWN " + e.KeyCode.ToString() + Environment.NewLine;
}
Вот готовый класс
~KeyHook()
{
KeyHookDecl.UnhookWindowsHookEx(hhook);
}
Вот и все, теперь этим можно пользоваться так:
private void Form1_Load(object sender, EventArgs e)
{
KeyHook kh = new KeyHook();
kh.KeyDown += new KeyEventHandler(kh_KeyDown);
kh.KeyUp += new KeyEventHandler(kh_KeyUp);
}
void kh_KeyUp(object sender, KeyEventArgs e)
{
textBox1.Text += "UP " + e.KeyCode.ToString() + Environment.NewLine;
}
void kh_KeyDown(object sender, KeyEventArgs e)
{
textBox1.Text += "DOWN " + e.KeyCode.ToString() + Environment.NewLine;
}
Вот готовый класс
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Reflection; using System.Collections; class KeyHookDecl { public const int WH_KEYBOARD = 2; public delegate int HookProc(int code, int wParam, IntPtr lParam);
[DllImport("user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)] public static extern int SetWindowsHookEx(int idHook,
HookProc lpfn,
IntPtr hMod,
int dwThreadId);
[DllImport("user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)] public static extern int CallNextHookEx(int idHook,
int nCode,
int wParam,
IntPtr lParam);
[DllImport("user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)] public static extern bool UnhookWindowsHookEx(int hhk); } public class KeyHook { public event KeyEventHandler KeyDown; public event KeyEventHandler KeyUp; [DllImport("kernel32.dll")] public static extern IntPtr GetModuleHandle(string lpModuleName); int hhook;// хэндл созданой нами ловушки int HookProc(int code, int wParam, IntPtr lParam) {
// для доступа к битам BitArray bt = new BitArray(BitConverter.GetBytes((uint)lParam)); KeyEventArgs arg = new KeyEventArgs((Keys)wParam); //Если клавиша нажата - вызвать KeyDown, если отпущена - вызвать KeyUp if (bt[30]) {
if (KeyUp != null) KeyUp(this, arg);
}
else
if (KeyDown != null) KeyDown(this, arg);
// Вызов следующей ловушки в очереди KeyHookDecl.CallNextHookEx(0, code, wParam, lParam); return 1; } public KeyHook() { // Создание ловушки hhook = KeyHookDecl.SetWindowsHookEx(KeyHookDecl.WH_KEYBOARD,
new KeyHookDecl.HookProc(HookProc),
GetModuleHandle(null),
AppDomain.GetCurrentThreadId()); } ~KeyHook() { KeyHookDecl.UnhookWindowsHookEx(hhook); } }
Спасибо, за статью.
ОтветитьУдалитьСамое странное, что вроде должна ловушка срабатывать всегда, а она работает только если в фокусе форма, в случае когда фокусировка переходит на другой объект, ловушка не срабатывает.
Что он сказал???
ОтветитьУдалить