Hook là một cơ chế mà một ứng dụng có thể chặn các sự kiện, như các thông điệp, thao tác chuột, bàn phím. Hàm dùng để chặn một loại sự kiện riêng biệt được gọi là hook procedure/ Hook function. Mỗi khi nhận được sự kiện, hook procedure có thể thay đổi và thậm chí hủy bỏ các sự kiện đó.
Cơ chế của Hook
Như đã nói ở trên, có nhiều loại hook (như chuột, bàn phím) và hệ điều hành luôn duy trì một danh sách các hook procedure cho mỗi loại đó. Mỗi danh sách các Hook procedure này được gọi là hook chain. Bản chất của hook chain là một dãy các con trỏ hàm trỏ đến các Hook procedure.
Khi hệ thống thực một sự kiện nào đó, nó sẽ tìm kiếm trong hook chain tương ứng với sự kiện đó. Nếu một hook procedure phù hợp được tìm thấy, hệ thống sẽ thực hiện nó và chỉ lấy lại quyền điều khiển sau khi hook chain kết thúc. Vì thế khi một hook procedure thực hiện xong, nó phải thực hiện việc chuyển quyền điều khiển cho hook procedure kế tiếp trong hook chain.
Tuy nhiên cơ chế này còn tùy thuộc vào loại hook. Như một số loại hook chỉ có thể theo dõi các thông điệp, vì vậy cho dù hook procedure có chuyển quyền điều khiển cho hook procedure kế tiếp hay không, hệ thống vẫn sẽ tự động làm việc này.
Một điểm cần lưu ý là hook sẽ làm chậm hệ thống, vì thế bạn chỉ nên cài đặt hook khi cần thiết và loại bỏ nó khi đã hoàn tất công việc.
Các loại Hook (Hook Type)
Có nhiều loại hook được phân biệt dựa vào các sự kiện, thông điệp mà Hook procedure can thiệp vào. Danh sách dưới đây liệt kê các loại hook kèm với link dẫn đến tham khảo từ MSDN:
- WH_CALLWNDPROC and WH_CALLWNDPROCRET
- WH_CBT
- WH_DEBUG
- WH_FOREGROUNDIDLE
- WH_GETMESSAGE
- WH_JOURNALPLAYBACK
- WH_JOURNALRECORD
- WH_KEYBOARD_LL
- WH_KEYBOARD
- WH_MOUSE_LL
- WH_MOUSE
- WH_MSGFILTER and WH_SYSMSGFILTER
- WH_SHELL
Ứng dụng của Hook
Hook được ứng dụng khá nhiều trong lập trình và là kĩ thuật không thể thiếu nếu như bạn muốn thực hiện một số ứng dụng điển hình sau:
- Tạo các chương trình record và play back macro.
- Tạo các ứng dụng computer-based training (CBT)
- Bắt và giả lập các thông điệp bàn phím, chuột
- Cung cấp chức năng Help (F1) cho ứng dụng
- Xác định trạng thái rỗi của ứng dụng để thực hiện công việc nào đó.
- Tạo các chức năng debug.
Phạm vi của Hook
Dựa theo lọai hook bạn có thể cài đặt hook procedure có phạm vi cục bộ (local hook/thread hook) hoặc toàn cục (global hook). Tất cả các loại hook đểu có thể được cài đặt để trở thành global hook, một số cho phép chọn lựa phạm vi hook dựa vào các tham số trong hàm cài đặt (SetWindowsHookEx). Ta sẽ xem chi tiết hơn trong phần kế tiếp. Hiện tại bạn cần phân biệt đặc điểm của hai loại hook này (theo phạm vi ảnh hưởng):
- Local hook (thread hook): chỉ có ảnh hưởng trong phạm vi một thread.
- Global hook: có ảnh hưởng trong toàn hệ thống. Trường hợp này, hook procedure phải được chứa trong một thư viện DLL.
Hook Procedure
Hook procedure là một loại callback function (hàm hồi quy). Hệ thống sẽ gọi các hàm này khi các sự kiện, thông điệp tương ứng với loại hook. Mỗi loại hook có một hook procedure khác nhau nhưng đều có cùng tham số như cú pháp bên dưới. Với mỗi hook procedure khác nhau thì việc xét các giá trị tham số cũng khác nhau.
Danh sách hook procedure tương ứng với từng loại hook được liệt kê trong phần giới thiệu về hàm SetWindowsHookEx.
Cú pháp chung:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
Tham số:
- nCode: giá trị giúp xác định cách xử lý thông điệp. Nếu nCode nhỏ hơn 0, hook procedure sẽ gọi CallNextHookEx. Ngược lại sẽ thực hiện các công việc xử lý trước khi gọi CallNextHookEx
- wParam và lParam: tùy theo loại hook sẽ có ý nghĩa khác nhau. Ví dụ nếu bạn hook bàn phím thì wParam sẽ có giá trị của phím được nhấn, lParam sẽ chứa các thông tin về số lần nhấn, các phím tắt, trạng thái phím,…
Các hàm Win32 API cần thiết
Để sử dụng hook, ta cần sử dụng ba hàm API của Windows là:
- SetWindowsHookEx: cài đặt một hook procedure vào một hook chain.
- CallNextHookEx: chuyển quyền điều khiển cùng các thông tin hook cho hook procedure kế tiếp trong hook chain. Bạn có thể không sử dụng hàm này tuy nhiên chỉ khi nào bạn muốn chặn các hook procedure còn lại trong hook chain.
- UnhookWindowsHookEx: gỡ hook procedure ra khỏi hook chain được cài đặt bởi SetWindowsHookEx.
Phần cú pháp khai báo có một số từ khóa phía trước phần biến mà bạn cần chú ý:
__in: input parameter
__out output parameter
__in_opt: optional input parameter
SetWindowsHookEx
Cú pháp:
HHOOK WINAPI SetWindowsHookEx(__in int idHook,__in HOOKPROC lpfn,__in HINSTANCE hMod,__in DWORD dwThreadId);
Giá trị trả về:
Handle của hook nếu thành công, ngược lại trả về NULL. Giá trị này cần thiết để sử dụng hàm UnhookWindowsHookEx. Trong trường hợp này bạn có thể dùng hàm GetLastErrorđể lấy thông tin lỗi. Để xem thông tin lỗi, bạn có thể truy cập vào trang sau: List of System Error Codes
Tham số:
- idHook: loại hook, bao gồm các giá trị sau (đã được liệt kê trong phần Hook Type):
Name | Value | Scopre | Hook procedure |
WH_CALLWNDPROC | 4 | Thread or global | CallWndProc |
WH_CALLWNDPROCRET | 12 | Thread or global | CallWndRetProc |
WH_CBT | 5 | Thread or global | CBTProc |
WH_DEBUG | 9 | Thread or global | DebugProc |
WH_FOREGROUNDIDLE | 11 | Thread or global | ForegroundIdleProc |
WH_GETMESSAGE | 3 | Thread or global | GetMsgProc |
WH_JOURNALPLAYBACK | 1 | Global only | JournalPlaybackProc |
WH_JOURNALRECORD | 0 | Global only | JournalRecordProc |
WH_KEYBOARD | 2 | Thread or global | KeyboardProc |
WH_KEYBOARD_LL | 13 | Global only | LowLevelKeyboardProc |
WH_MOUSE | 7 | Thread or global | MouseProc |
WH_MOUSE_LL | 14 | Global only | LowLevelMouseProc |
WH_MSGFILTER | -1 | Thread or global | MessageProc |
WH_SHELL | 10 | Thread or global | ShellProc |
WH_SYSMSGFILTER | 6 | Global only | SysMsgProc |
- lpfn: một con trỏ đến hook procedure. Nếu là global hook thì tham số này phải trỏ đến một hook procedure trong một DLL. Ngược lại thì có thể trỏ đến một đoạn mã đóng vai trò hook procedure trong process hiện tại.
- hMod: handle của DLL chứa hook procedure. Trong trường hợp local hook thì tham số này được đặt là NULL.
- dwThreadId: định danh của thread mà hook procedure sẽ gắn vào. Nếu giá trị này là 0 thì mọi thread sẽ bị ảnh hưởng (global), ngược lại thì chỉ có thread được xác định là bị ảnh hưởng (local).
CallNextHookEx
Cú pháp:
LRESULT WINAPI CallNextHookEx(
__in_opt HHOOK hhk,
__in int nCode,
__in WPARAM wParam,
__in LPARAM lParam
);
Giá trị trả về:
Tham số:
- hhk: handle của hook lấy được từ SetWindowsHookEx, tuy nhiên tham số này thực sự không cần thiết.
- nCode: tham số này được gọi tên là hook code. Giá trị của nó sẽ quyết định hook procedure có thực hiện các công việc xử lý không. Nếu giá trị này là âm, bạn cần gọi hàm CallNextHookEx để chuyển tới hook procedure kế tiếp và bỏ qua bước xử lý (xem phần hook procedure). Giá trị này có được do hệ thống quyết định và bạn không nên thay đối giá trị.
- wParam / lParam: các tham số lưu trữ thông tin cần thiết cho hook procedure kế tiếp.
UnhookWindowsEx
Cú pháp:
Cú pháp:
BOOL WINAPI UnhookWindowsHookEx(
__in HHOOK hhk
);
Giá trị trả về: Một số khác 0 nếu thành công, ngược lại là 0.
Tham số:
- hhk: handle của hook sẽ được loại ra khỏi hook chain, lấy từ SetWindowsHookEx.
Kết luận
Như vậy bạn đã hiểu các khái niệm cơ bản về hook cũng như ứng dụng của nó. Trong bài viết tới tôi sẽ hướng dẫn một vài ví dụ đơn giản để áp dụng hook trong C#. Để biết thêm chi tiết và xem ví dụ bạn có thể tham khảo các link sau:
- How to set a Windows hook in Visual C# .NET (http://support.microsoft.com/kb/318804)
Không có nhận xét nào:
Đăng nhận xét