Thứ Năm, 28 tháng 6, 2012

C# – Hướng dẫn cài đặt Low-Level Keyboard Hook (P2)


Thêm các event

Sẽ tiện lợi hơn nếu bạn thêm các event cho lớp để sử dụng như một control. Net đã cung cấp sẵn delegate KeyEventHandler. Nếu bạn cần tìm hiểu về cách tạo event, có thể tham khảo hướng dẫn tại:  Tạo, sử dụng và quản lý Event trong C#.
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
Trong hook procedure, ta sẽ xử lý để kích hoạt event nếu thông điệp tương ứng với event đó xảy ra:
01private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
02{
03    if (nCode >= 0)
04    {
05        KeyboardHookStruct kbStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam,typeof(KeyboardHookStruct));
06 
07        if (wParam == (IntPtr)WM_KEYDOWN)
08        {
09            if (KeyDown != null)
10                KeyDown(nullnew KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
11        }
12        else if (wParam == (IntPtr)WM_KEYUP)
13        {
14            if (KeyUp != null)
15                KeyUp(nullnew KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
16        }
17    }
18 
19    return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
20}

Ví dụ hoàn chỉnh

Để thực hiện ví dụ này, bạn hãy tạo một dự án Windows Forms Application. Thêm lớpY2KeyboardHook sau vào dự án:
001using System;
002using System.Runtime.InteropServices;
003using System.Diagnostics;
004using System.Windows.Forms;
005using System.Reflection;
006using System.ComponentModel;
007 
008namespace HookApp
009{
010 
011    class Y2KeyboardHook
012    {
013 
014        #region Win32 API Functions and Constants
015 
016        [DllImport("user32.dll", SetLastError = true)]
017        private static extern IntPtr SetWindowsHookEx(int idHook,
018            KeyboardHookDelegate lpfn, IntPtr hMod, int dwThreadId);
019 
020        [DllImport("user32.dll", SetLastError = true)]
021        private static extern bool UnhookWindowsHookEx(IntPtr hhk);
022 
023        [DllImport("user32.dll", SetLastError = true)]
024        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
025            IntPtr wParam, IntPtr lParam);
026 
027        [DllImport("kernel32.dll")]
028        private static extern IntPtr GetModuleHandle(string lpModuleName);
029 
030        private const int WH_KEYBOARD_LL = 13;
031 
032        private const int WM_KEYDOWN = 0x0100;
033        private const int WM_KEYUP = 0x101;
034 
035        #endregion
036 
037        private KeyboardHookDelegate _hookProc;
038        private IntPtr _hookHandle = IntPtr.Zero;
039 
040        public delegate IntPtr KeyboardHookDelegate(int nCode, IntPtr wParam, IntPtr lParam);
041 
042        [StructLayout(LayoutKind.Sequential)]
043        public struct KeyboardHookStruct
044        {
045            public int VirtualKeyCode;
046            public int ScanCode;
047            public int Flags;
048            public int Time;
049            public int ExtraInfo;
050        }
051 
052        #region Keyboard Events
053 
054        public event KeyEventHandler KeyDown;
055        public event KeyEventHandler KeyUp;
056 
057        #endregion
058 
059        // destructor
060        ~Y2KeyboardHook()
061        {
062            Uninstall();
063        }
064 
065        public void Install()
066        {
067            _hookProc = KeyboardHookProc;
068            _hookHandle = SetupHook(_hookProc);
069 
070            if (_hookHandle == IntPtr.Zero)
071                throw new Win32Exception(Marshal.GetLastWin32Error());
072        }
073        private IntPtr SetupHook(KeyboardHookDelegate hookProc)
074        {
075            IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
076 
077            return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
078        }
079 
080        private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
081        {
082            if (nCode >= 0)
083            {
084                KeyboardHookStruct kbStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam,typeof(KeyboardHookStruct));
085 
086                if (wParam == (IntPtr)WM_KEYDOWN)
087                {
088                    if (KeyDown != null)
089                        KeyDown(nullnew KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
090                }
091                else if (wParam == (IntPtr)WM_KEYUP)
092                {
093                    if (KeyUp != null)
094                        KeyUp(nullnew KeyEventArgs((Keys)kbStruct.VirtualKeyCode));
095                }
096            }
097 
098            return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
099        }
100 
101        public void Uninstall()
102        {
103            UnhookWindowsHookEx(_hookHandle);
104        }
105 
106    }
107}
Trong tập tin Form1.cs, bạn hãy sửa lại như sau:
01using System;
02using System.Windows.Forms;
03using HookApp;
04using System.Drawing;
05 
06namespace WindowsFormApplication1
07{
08    public partial class Form1 : Form
09    {
10        Y2KeyboardHook _keyboardHook;
11 
12        public Form1()
13        {
14            InitializeComponent();
15 
16            this.TopMost = true;
17 
18            ListBox listBox1 = new ListBox();
19            listBox1.Location = new Point(10, 10);
20            listBox1.Size = new Size(200, 200);
21 
22            this.Controls.Add(listBox1);
23 
24            _keyboardHook = new Y2KeyboardHook();
25            _keyboardHook.Install();
26 
27            _keyboardHook.KeyDown += (sender, e) =>
28                {
29                    listBox1.Items.Add("KeyDown: " + e.KeyCode);
30 
31                    listBox1.SelectedIndex = listBox1.Items.Count - 1;
32                };
33 
34            _keyboardHook.KeyUp += (sender, e) =>
35                {
36                    listBox1.Items.Add("KeyUp: " + e.KeyCode);
37 
38                    listBox1.SelectedIndex = listBox1.Items.Count - 1;
39                };
40        }
41 
42    }
43}
Đoạn mã mới thêm vào Form1 sẽ thêm một ListBox vào Form, đồng thời tạo ra một đối tượng Y2KeyboardHook và xử lý hai event KeyDown và KeyDown bằng lambda expression. Bạn có thể chuyển sang ứng dụng khác và nhấn phím bất kì, các sự kiện tương ứng sẽ được thêm vào ListBox.

Kết luận

Bạn có thể thấy rằng việc sử dụng hook trong C# rất dễ dàng, tuy nhiên để cải tiến chương trình hook bạn cần phải kiến thức đầy đủ về các hàm Win32 API cần thiết. Dựa vào ví dụ này, bạn có thể viết được một chương trình hook mouse đơn giản. Và hơn nữa là một chương trình có mức tổng quát cao cho phép cài đặt nhiều loại hook khác nhau.

Không có nhận xét nào: