Trong môi trường Windows CE, hệ thống hỗ trợ ba loại hook chính:
-
#define WH_JOURNALRECORD 0cho phép ứng dụng theo dõi các sự kiện đầu vào. Thông thường, ứng dụng sử dụng hook này để ghi lại các sự kiện chuột, bàn phím để có thể phát lại sau này. Đây là hook toàn cục và không thể sử dụng trong một luồng cụ thể. -
#define WH_JOURNALPLAYBACK 1cho phép ứng dụng chèn tin nhắn vào hàng đợi tin nhắn của hệ thống. Hook này có thể phát lại các sự kiện bàn phím, chuột đã được ghi lại bởi hookWH_JOURNALRECORD. Khi hookWH_JOURNALPLAYBACKđược cài đặt, các sự kiện đầu vào từ chuột và bàn phím sẽ bị chặn. Hook này cũng là một hook toàn cục và không thể sử dụng trong một luồng cụ thể.
Hook WH_JOURNALPLAYBACK trả về một giá trị thời gian tạm dừng, cho hệ thống biết cần phải chờ bao nhiêu phần trăm giây trước khi xử lý tin nhắn tiếp theo. Điều này cho phép hook kiểm soát tốc độ phát lại.
#define WH_KEYBOARD_LL 20
Trong ba loại trên, hook bàn phím là loại được sử dụng phổ biến nhất.
Để làm việc với hook, bạn cần các hàm sau:
- Thiết lập hook: Sử dụng hàm
SetWindowsHookEx(). - Giải phóng hook: Sử dụng hàm
UnhookWindowsHookEx(). - Hàm xử lý hook: Hàm
HookProc. - Gọi hook tiếp theo: Sử dụng hàm
CallNextHookEx().
Ví dụ thực tế: Tạo một hook bàn phím bằng DLL
Để tạo một hook bàn phím, chúng ta thường xây dựng một Dynamic Link Library (DLL). Dưới đây là ví dụ về cách triển khai.
1. Tệp nguồn DLL (.cpp)
// BanPhimHook.cpp : Định nghĩa điểm vào cho ứng dụng DLL.
//
#include "stdafx.h"
#include "BanPhimHook.h"
#include <Pwinuser.h>
// Chỉ thị cho trình biên dịch đặt biến vào một đoạn dữ liệu chia sẻ riêng
#pragma data_seg("DuLieuChiaSe")
HINSTANCE hModuleDLL = NULL;
#pragma data_seg()
// Chỉ thị cho trình biên dịch thiết lập quyền truy cập của đoạn chia sẻ: Đọc, Ghi, Chia sẻ
#pragma comment(linker, "/SECTION:DuLieuChiaSe,RWS")
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hModuleDLL = (HINSTANCE)hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// Biến xuất khẩu mẫu
BANPHIMHOOK_API int nMaHookBanPhim = 0;
// Hàm xuất khẩu mẫu.
BANPHIMHOOK_API int fnMaHookBanPhim(void)
{
return 42;
}
// Constructor của một lớp đã được xuất khẩu.
// Xem BanPhimHook.h để biết định nghĩa lớp
CBanPhimHook::CBanPhimHook()
{
}
extern "C" BANPHIMHOOK_API void KichHoatHook(void)
{
if (hModuleDLL)
{
hHookKB = SetWindowsHookEx(WH_KEYBOARD_LL, ProcHookBanPhim, hModuleDLL, 0);
}
}
extern "C" BANPHIMHOOK_API void GiaiPhongHook(void)
{
if (hHookKB)
{
UnhookWindowsHookEx(hHookKB);
hHookKB = NULL;
}
hModuleDLL = NULL;
}
extern "C" BANPHIMHOOK_API LRESULT CALLBACK ProcHookBanPhim(int nCode, WPARAM wParam, LPARAM lParam)
{
TCHAR tenLopCuaSo[50] = { 0 };
HWND cuaSoHienTai = NULL;
HWND cuaSoNen = NULL;
BOOL laTinNhanTaskBar = FALSE;
BOOL laTinNhanOEM = FALSE;
PKBDLLHOOKSTRUCT pKetCau = (PKBDLLHOOKSTRUCT)lParam;
if (WM_KEYDOWN == wParam)
{
demNhanPhim++;
// Ghi log sự kiện nhấn phím
// RETAILMSG(1, (TEXT("WM_KEYDOWN vk %d /r/n"), pKetCau->vkCode));
// Xử lý các phím điều hướng và phím OEM
switch (pKetCau->vkCode)
{
case VK_UP:
case VK_DOWN:
case VK_LEFT:
case VK_RIGHT:
break;
case VK_OEM_SELECT:
case VK_OEM_OK:
case VK_OEM_BACK:
case VK_OEM_DIAL:
laTinNhanOEM = TRUE;
break;
case VK_NUMPAD1:
case VK_NUMPAD2:
case VK_NUMPAD3:
case VK_NUMPAD4:
case VK_NUMPAD5:
case VK_NUMPAD6:
case VK_NUMPAD7:
case VK_NUMPAD8:
case VK_NUMPAD9:
case VK_NUMPAD0:
break;
case VK_OEM_ASTERISK:
case VK_OEM_POUND:
case VK_OEM_SIDEUP:
case VK_OEM_SIDEDOWN:
case VK_OEM_CAMERA:
laTinNhanOEM = TRUE;
break;
default:
demNhanPhim = 0;
return CallNextHookEx(hHookKB, nCode, wParam, lParam); // Cho phép tin nhắn tiếp tục
}
// Nếu cần bỏ qua một tin nhắn
if (coBoQuaMotTinNhan)
{
return TRUE;
}
// Chỉ gửi các tin nhắn OEM, các tin nhắn khác sẽ được bỏ qua.
if (chiGoiTinNhanOEM)
{
if (laTinNhanOEM)
{
PostMessage(cuaSoChinh, WM_USER_KEYUP, (WPARAM)(pKetCau->vkCode), (LPARAM)demNhanPhim);
return TRUE; // Chặn tin nhắn OEM, không cho tiếp tục
}
else
{
return CallNextHookEx(hHookKB, nCode, wParam, lParam); // Cho phép các tin nhắn khác
}
}
// Chặn tin nhắn gốc và gửi tin nhắn tùy chỉnh.
if (coGiamChanTinNhan)
{
PostMessage(cuaSoChinh, WM_USER_KEYUP, (WPARAM)(pKetCau->vkCode), 0L);
return TRUE; // Chặn tin nhắn, không cho tiếp tục
}
}
else
{
demNhanPhim = 0;
}
return CallNextHookEx(hHookKB, nCode, wParam, lParam); // Cho phép tin nhắn tiếp tục
}
extern "C" BANPHIMHOOK_API void ThietLapHWNDUngDung(HWND hUngDung)
{
cuaSoUngDung = hUngDung;
}
extern "C" BANPHIMHOOK_API void ThietLapHWNDChinh(HWND hCuaSoChinh)
{
cuaSoChinh = hCuaSoChinh;
}
extern "C" BANPHIMHOOK_API void ThietLapGiamChanTinNhan(BOOL coGiamChan)
{
coGiamChanTinNhan = coGiamChan;
}
extern "C" BANPHIMHOOK_API void ThietLapChiGoiTinNhanOEM(BOOL chiGoiOEM)
{
chiGoiTinNhanOEM = chiGoiOEM;
}
extern "C" BANPHIMHOOK_API void ThietLapCoBoQuaMotTinNhan(BOOL coBoQua)
{
coBoQuaMotTinNhan = coBoQua;
}
2. Tệp header (.h)
// Khối ifdef sau là cách chuẩn để tạo macro giúp xuất khẩu từ DLL dễ dàng hơn.
// Tất cả các tệp trong DLL này được biên dịch với symbol KEYBOARDHOOK_EXPORTS được định nghĩa trên dòng lệnh.
// Symbol này không nên được định nghĩa trong bất kỳ dự án nào sử dụng DLL này.
// Cách này đảm bảo rằng các dự án khác includ tệp này sẽ thấy các hàm KEYBOARDHOOK_API như là hàm được nhập từ DLL,
// trong khi DLL này thấy các symbol được định nghĩa với macro này như là hàm được xuất khẩu.
#ifdef BANPHIMHOOK_EXPORTS
#define BANPHIMHOOK_API __declspec(dllexport)
#else
#define BANPHIMHOOK_API __declspec(dllimport)
#endif
// Lớp này được xuất khẩu từ BanPhimHook.dll
class BANPHIMHOOK_API CBanPhimHook {
public:
CBanPhimHook(void);
// TODO: Thêm các phương thức của bạn ở đây.
};
extern BANPHIMHOOK_API int nMaHookBanPhim;
BANPHIMHOOK_API int fnMaHookBanPhim(void);
extern "C" BANPHIMHOOK_API void KichHoatHook(void);
extern "C" BANPHIMHOOK_API void GiaiPhongHook(void);
extern "C" BANPHIMHOOK_API LRESULT CALLBACK ProcHookBanPhim(int nCode, WPARAM wParam, LPARAM lParam);
extern "C" BANPHIMHOOK_API void ThietLapHWNDUngDung(HWND hUngDung);
extern "C" BANPHIMHOOK_API void ThietLapHWNDChinh(HWND hCuaSoChinh);
extern "C" BANPHIMHOOK_API void ThietLapGiamChanTinNhan(BOOL coGiamChan);
extern "C" BANPHIMHOOK_API void ThietLapChiGoiTinNhanOEM(BOOL chiGoiOEM);
extern "C" BANPHIMHOOK_API void ThietLapCoBoQuaMotTinNhan(BOOL coBoQua);
3. Tệp chính của ứng dụng
// Giả sử hModule là handle của DLL đã được tải
if (hModule)
{
pKichHoatHook = (pKichHoat)GetProcAddress(hModule, L"KichHoatHook");
pGiaiPhongHook = (pGiaiPhong)GetProcAddress(hModule, L"GiaiPhongHook");
pThietLapHWNDUngDung = (pThietLapUngDung)GetProcAddress(hModule, L"ThietLapHWNDUngDung");
pThietLapHWNDChinh = (pThietLapChinh)GetProcAddress(hModule, L"ThietLapHWNDChinh");
pThietLapGiamChanTinNhan = (pThietLapGiamChan)GetProcAddress(hModule, L"ThietLapGiamChanTinNhan");
pThietLapChiGoiTinNhanOEM = (pThietLapChiGoiOEM)GetProcAddress(hModule, L"ThietLapChiGoiTinNhanOEM");
pThietLapCoBoQuaMotTinNhan = (pThietLapBoQua)GetProcAddress(hModule, L"ThietLapCoBoQuaMotTinNhan");
if (!pKichHoatHook || !pGiaiPhongHook || !pThietLapHWNDUngDung || !pThietLapHWNDChinh ||
!pThietLapGiamChanTinNhan || !pThietLapChiGoiTinNhanOEM || !pThietLapCoBoQuaMotTinNhan)
{
MessageBoxEx(hWnd, L"Không thể tải BanPhimHook.dll, chương trình sẽ bị đóng.", L"Thông báo", MB_OK);
PostQuitMessage(0);
}
}
else
{
MessageBoxEx(hWnd, L"Không thể tải BanPhimHook.dll, chương trình sẽ bị đóng.", L"Thông báo", MB_OK);
PostQuitMessage(0);
}
// Thiết lập các handle cửa sổ
pThietLapHWNDUngDung(hWnd);
pThietLapHWNDChinh(hWnd);
// Kích hoạt hook
pKichHoatHook();
Bạn có thể tham khảo thêm về cách viết DLL tại địa chỉ: http://www.bc-cn.net/Article/kfyy/cyy/jszl/200709/6328_2.html