1. Trang chủ
  2. » Giáo Dục - Đào Tạo

Ứng Dụng Cơ Chế Hook Để Xây Dựng Chương Trình Hỗ Trợ Gõ Tiếng Việt Trên Hệ Điều Hành Window 32 Bit

18 601 2

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 18
Dung lượng 271,61 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Đối với Windows vấn đề này được giải quyết một cách trọn vẹn : HĐH đưa ra cơ chế thông điệp message cùng với tập hợp các cấu trúc dữ liệu và các hàm API hỗ trợ ứng dụng trong việc giao

Trang 1

[Programming] Ứng dụng cơ chế Hook - Phần 1

Ứng dụng cơ chế Hook để xây dựng chương trình hỗ trợ gõ Tiếng Việt trên Hệ Điều Hành Windows 32 Bit - Phần 1

I.Sự kiện và thông điệp trên HĐH Windows

I.1.Giới thiệu

Ngày nay, Windows có lẽ không còn xa lạ với người sử dụng PC Nhắc đến

Windows, người ta thường nghĩ về nó như một Hệ Điều Hành (HĐH) dễ sử dụng, ở

đó, tương tác giữa người dùng với các ứng dụng cũng như với các thành phần tiện ích của Windows thông qua giao diện đồ họa (GUI), bằng các thao tác trên keyboard, mouse vô cùng đơn giản

Một câu hỏi được đặt ra là : “Các ứng dụng làm thế nào để phân loại, lưu giữ cũng

như đáp lại những tương tác đó cho người dùng ?” Đối với Windows vấn đề này được giải quyết một cách trọn vẹn : HĐH đưa ra cơ chế thông điệp (message) cùng

với tập hợp các cấu trúc dữ liệu và các hàm API hỗ trợ ứng dụng trong việc giao tiếp với người dùng

I.2.Hai loại hàng đợi thông điệp trên Windows

Trước hết, ta cần phải biết làm thế nào HĐH Windows lưu trữ các thông điệp Hai loại hàng đợi trong Windows phục vụ cho mục đích này :

+ Hàng đợi hệ thống (The System Queue)

+ Hàng đợi thông điệp của ứng dụng (Application Queue)

Hàng đợi hệ thống (The System Queue)

Windows có các trình điều khiển thiết bị (Drivers) chịu trách nhiệm cho các dịch vụ

ngắt từ thiết bị phần cứng mouse và keyboard.Tại mỗi thời điểm ngắt phát sinh, các

Driver này gọi một điểm vào (hàm) đặc biệt trong USER.EXE để chỉ ra rằng một sự

kiện vừa xảy ra

Tất cả các sự kiện mouse, keyboard được lưu trong một hàng đợi được gọi là hàng đợi

hệ thống (The System Queue) Đây là hàng đợi dùng chung cho toàn hệ thống , mọi tiến trình đang chạy đều chia xẻ hàng đợi này Nhiệm vụ độc nhất của nó là ghi nhận lại những sự kiện phần cứng (mouse actions, keystrokes) khi chúng xảy ra.

Trang 2

Hàng đợi thông điệp của ứng dụng (Application Queue)

Khi một tiến trình được khởi tạo, một hàng đợi đại diện cho nó được tạo ra, hàng đợi này (đôi khi được gọi là hàng đợi tác vụ) được dùng để chứa những thông điệp sẽ được gởi cho các cửa sổ của ứng dụng Những thông điệp này là những thông điệp được gởi một cách tường minh bằng một trong hai hàm sau :

+ PostMessage + PostAppMessage (Lưu ý : Hàm PostQuitMessage không gởi thông điệp vào hàng đợi ứng dụng

Ứng dụng có thể dùng hai Primitive là GetMessage và PeekMessage doWindows

cung cấp để khảo sát hàng đợi của mình Hai hàm này cho phép ứng dụng “Lấy một message ra khỏi hàng đợi” để từ đó phân loại và có những “Trả lời” thích hợp với người dùng

I.3.Bàn thêm về GetMessage và PeekMessage

Bên trong HĐH Windows, GetMessage và PeekMessage thi hành cùng mã lệnh.

Điểm khác biệt chính giữa 2 hàm là trong trường hợp không có message nào được trả

về cho ứng dụng Trong trường hợp này, GetMessage đặt ứng dụng vào trạng thái

“Sleep” trong khi PeekMessage trả về ứng dụng giá trị NULL.

Ngay trước khi GetMessage và PeekMessage trả message về cho ứng dụng, hệ thống

sẽ kiểm tra xem có sự tồn tại của hook WH_GETMESSAGE hay không Nếu có một

“Filter Function” (thực chất là 1 hàm) ứng với hook trên được cài đặt, hàm này sẽ

được gọi “Filter Function” không được gọi nếu PeekMessage không tìm thấy

message và trả về NULL

PeekMessage với tùy chọn PM_NOREMOVE

Theo mặc định, cả PeekMessage và GetMessage sẽ lấy message ra khỏi hàng đợi khi

mỗi message được trả về cho ứng dụng Tuy nhiên, có đôi lúc, ứng dụng có thể chỉ muốn vào hàng đợi để kiểm tra sự tồn tại của một messsage mà không muốn gỡ bỏ

nó Để làm được điều này, chương trình của ta sẽ gọi PeekMessage với tùy chọn

PM_NOREMOVE, lúc này PeekMessage vẫn trả về message như thường lệ nhưng

sẽ không lấy nó ra khỏi hàng đợi.(Chi tiết về GetMessage và PeekMessage có thể

tham khảo tại Article “GetMessage and PeekMessage Internals” trong thư viện

MSDN)

Trang 3

II.Sự kiện bàn phím

II.1.Mô hình chung

Mỗi phím trên bàn phím được hệ thống gắn với một giá trị duy nhất gọi là mã quét (Scan Code), đây là một giá trị phụ thuộc thiết bị, tức là giá trị này sẽ lệ thuộc vào

từng loại bàn phím cụ thể Khi người dùng gõ một phím, sẽ có 2 mã quét được sinh ra : một khi phím được nhấn và một khi phím được thả

Trình điều khiển bàn phím (Keyboard Device Driver)sẽ thông dịch mã quét và

chuyển nó thành mã phím ảo (vitual-key code), một giá trị độc lập thiết bị, được

định nghĩa bởi hệ thống Sau đó, trình điều khiển tạo một thông điệp bao gồm

scancode, virtual-key code cùng một số thông tin khác (sự lặp phím, trạng thái các phím Alt, Ctrl ) , đặt vào hàng đợi hệ thống Hệ thống lấy thông điệp này ra khỏi hàng đợi hệ thống và gởi đến hàng đợi thông điệp của ứng dụng.Cuối cùng, vòng lặp

thông điệp (Message Loop) sẽ lấy thông điệp ra khỏi hàng đợi ứng dụng và chuyển

nó đến hàm xử lý message thích hợp để xử lý Quá trình trên có thể minh hoạ qua hình vẽ sau :

Rõ ràng là nếu ta có một cách nào đó để có được message trước khi nó đến hàm xử lý thông điệp của cửa sổ, ta có thể thực hiện được một vài thao tác thú vị Và trên HĐH

Windows, điều này hoàn toàn có thể thực hiện được nhờ cơ chế hook như ta sẽ thấy ở

phần sau

II.2.Các thông điệp liên quan bàn phím

Khi ta nhấn một phím, một thông điệp WM_KEYDOWN sẽ được đặt vào hàng đợi của tiểu trình ứng với cửa sổ đang được focus Và khi ta nhả phím, hàng đợi sẽ có một thông điệp WM_KEYUP Hai thông điệp này thường xuất hiện thành cặp Tuy nhiên,

nếu ta nhấn phím và giữ phím đủ lâu làm đặc tính “Automatic Repeat” của bàn phím

hoạt động, hệ thống sẽ sinh nhiều thông điệp WM_KEYDOWN liên tiếp nhau.Và lưu ý một điều là khi chúng ta nhả phím, chỉ có một thông điệp WM_KEYUP được phát sinh mà thôi

wParam của cả 2 thông điệp trên sẽ cho ta mã phím ảo (virtual-key code) của phím

được nhấn

lParamdùng 32 bit của mình để mô tả thông tin thêm về sự kiện gõ phím đã tạo ra message

Trang 4

Thông tin này bao gồm : sự lặp phím, mã quét (scan code), trạn thái phím ALT, trạng thái phím trước, Sơ đồ của 32 bit này nhưsau :

Repeat Count : Hệ thống tăng bộ đếm này lên khi bàn phím phát sinh thông điệp

WM_KEYDOWN nhanh hơn khả năng xử lý message của ứng dụng Điều này thường xảy ra khi người dùng giữ phím đủ lâu làm cho đặc tính “Automatic Repeat” của bàn phím hoạt động Lúc này, thay vì đặt vào hàng đợi hệ thống nhiều thông điệp WM_KEYDOWN liên tiếp, hệ thống sẽ kết hợp các message này thành một message duy nhất và tăng giá trị Repeat Count Tác vụ nhả phím không làm cho đặc tính

“Automatic Repeat” được kích hoạt, do đó giá trị Repeat Count của thông điệp WM_KEYUP luôn luôn là 1

Scan Code : Giá trị phụ thuộc thiết bị được bàn phím phát sinh khi một phím bị nhấn

hay nhả Đây không phải là mã ký tự đại diện cho phím được nhấn.Ứng dụng thường không quan tâm đến giá trị này, thay vì vậy, nó sử dụng giá trị độc lập thiết bị là mã phím ảo (virtual-key code) để xử lý message

Extended-Key Flag Bit cờ hiệu cho biết một phím có phải là phím mở rộng hay

không Bit này được bật khi phím bị nhấn là phím mở rộng như : INS, DEL, HOME, END,

Context Code Bit cờ hiệu cho biết tại thời điểm thông điệp phát sinh, phím ALT có

bị nhấn hay không Bit này bật khi phím ALT bị nhấn, tắt khi phím ALT được nhả

Previous Key-State Flag Bit cờ hiệu cho biết trạng thái của phím trước khi gởi

message là “up” hay “down” Bit này là 1 nếu phím là “down”, 0 nếu phím là “up”

Transition-State Flag Bit này là 0 đối với WM_KEYDOWN, là 1 đối với

WM_KEYUP.Thông điệp WM_KEYDOWN, WM_KEYUP cung cấp cho ta khá nhiều thông tin về sự kiện gõ phím, tuy nhiên, nó không cho ta mã ký tự của phím

được nhấn Để có được thông tin này, ứng dụng phải thêm hàm TranslateMessage

vào vòng lặp thông điệp của mình.Hàm này chuyển thông điệp WM_KEYDOWN thành thông điệp WM_CHAR chứa mã ký tự ứng với phím được gõ, sau khi đã kiểm tra trạng thái của phím SHIFT và CAPS LOCK

II.3.Giả lập gõ phím

Đôi khi chúng ta muốn có một sự kiện gõ phím mà không có sự tác động về mặt vật

lý đối với bàn phím.Hàm keybd_event giúp ta có được khả năng này, chức năng của

nó là giả lập một thao tác đối với bàn phím (nhấn hay nhả) Khai báo của hàm này như sau :

Trang 5

VOID keybd_event(

BYTE bVk, // virtual-key code

BYTE bScan, // hardware scan code

DWORD dwFlags, // function options

ULONG_PTRdwExtraInfo // additional keystroke data

);

Ý nghĩa của từng tham số :

bVk : mã phím ảo của phím cần giả lập

bScan :tham số này không dùng

dwFlags : xác định loại thao tác cần giả lập, tham số này là một trong các giá

trị sau :

KEYEVENTF_EXTENDEDKEYScan Code được đi trước bởi giá trị 224

dwExtraInfo : thông tin thêm về sự kiện ta giả lập

III.Cơ chế Hook trên HĐH Windows

III.1.Giới thiệu

Trên HĐH Windows, một hook là cơ chế mà nhờ đó một hàm có thể chặn

các sự kiện (message, mouse actions, keystrokes) trước khi chúng đến

ứng dụng Hàm này có thể thực hiện một số thao tác trên sự kiện, và

trong một vài trường hợp có thể định nghĩa lại hoặc hủy bỏ sự kiện mà

nó bắt được Một đặc điểm quan trọng cần lưu ý là các hàm này được

gọi bởi bản thân HĐH Windows chứ không phải bởi ứng dụng.

Các hàm thuộc loại vừa nêu thường được gọi là Filter Function, chúng được phân

loại theo loại sự kiện mà chúng can thiệp.Ví dụ, một Filter Function hứng tất cả các

sự kiện mouse sẽ khác với một Filter Function can thiệp các sự kiện keyboard …

Một Filter Funtion muốn được Windows gọi thì trước tiên nó phải được gắn với một

Windows Hook, công việc này thường được gọi là “Cài đặt một hook” Windows

Hook thật ra là các loại hook khác nhau được HĐH định nghĩa, nó giúp người sử

Trang 6

dụng có thể cài đặt Filter Function của mình đúng với mục đích mà họ mong muốn Khi cài đặt hook, ta xác định loại hook thông qua các hằng số có dạng WH_XXX, ví

dụ WH_KEYBOARD xác định loại hook dùng để can thiệp các sự kiện thuộc về bàn phím

Một Windows Hook có thể có nhiều Filter Function gắn với nó, trong tình huống này,

Windows sẽ duy trì một chuỗi các Filter Function Hàm được cài đặt gần nhất sẽ ở

đầu chuỗi, hàm được cài đặt lâu nhất sẽ ở cuối chuỗi Lúc này, nếu có một sự kiện xảy

ra và sự kiện này tương ứng với loại hook ta đã cài đặt, Windows sẽ gọi hàm đầu tiên

trong chuỗi các Filter Function ứng với hook đó

III.2.Khả năng và ứng dụng của cơ chế Hook

Hook cung cấp các khả năng mạnh cho các ứng dụng chạy trên nền Windows, các

ứng dụng này có thể dùng hook để :

· Xử lý hoặc định nghĩa tất cả các thông điệp cho dialog box, message box, scroll bar, hoặc menu của một ứng dụng (Sử dụng hook WH_MSGFILTER)

· Xử lý hoặc định nghĩa tất cả các thông điệp cho dialog box, message box, scroll bar, hoặc menu của hệ thống (Sử dụng hook WH_SYSMSGFILTER)

· Xử lý hoặc định nghĩa tất cả các thông điệp (bất chấp là thông điệp gì) của hệ thống

mỗi khi GetMessage hoặc PeekMessage được gọi (Sử dụng hook

WH_GETMESSAGE)

· Xử lý hoặc định nghĩa tất cả các thông điệp (bất chấp là thông điệp gì) của hệ thống

mỗi khi SendMessage được gọi (Sử dụng hook WH_CALLWNDPROC).

· Thu (Record) và phát lại (Playback) các sự kiện keyboard và mouse (Sử dụng hook WH_JOURNALRECORD, WH_JOURNALPLAYBACK)

· Xử lý , định nghĩa hoặc hủy bỏ tất cả các sự kiện bàn phím.(Sử dụng hook

WH_KEYBOARD)

· Xử lý , định nghĩa hoặc hủy bỏ tất cả các sự kiện chuột (Sử dụng hook

WH_MOUSE)

Tận dụng các khả năng trên, các ứng dụng có thể sử dụng hook để :

· Cung cấp phím trợ giúp F1 hỗ trợ menu, dialog box và message box (Sử dụng hook WH_MSGFILTER)

· Cung cấp các tính năng thu và phát các sự kiện mouse và keyboard, thường được gọi

là các macro (Sử dụng hook WH_JOURNALRECORD,

WH_JOURNALPLAYBACK)

· Theo dõi các thông điệp để biết được thông điệp nào được gởi đến cửa sổ nào cũng như các hành động nào sẽ làm phát sinh thông điệp tương ứng (Sử dụng hook

WH_GETMESSAGE & WH_CALLWNDPROC) Chương trình Spy trong bộ

Trang 7

Win32™ SDK của Windows NT đã rất thành công trong việc sử dụng hook để thực hiện tác vụ này

· Giả lặp các tác vụ input của keyboard và mouse (Sử dụng hook

WH_JOURNALPLAYBACK) Chỉ có hook mới có thể cho ta một phương pháp chắc chắn và tin cậy để thực hiện điều này Nếu ta tiếp cận theo cách khác, gởi message

chẳng hạn, Windows sẽ không cập nhật trạng thái của mouse cũng như keyboard, và

điều này sẽ dẫn đến những hành động không mong muốn Còn nếu như hook được sử dụng, các sự kiện sẽ được xử lý y hệt như sự kiện vật lý

III.3.Quản lý chuỗi các Filter Function

Giao diện lập trình ứng dụng (API) của Windows cung cấp 3 hàm để thao tác với

hook :

· SetWindowsHookEx

· UnhookWindowsHookEx

· CallNextHookEx

Cài đặt một Filter Function vào chuỗi các Filter Function của một hook

Tác vụ này được thực hiện thông qua hàm SetWindowsHookEx, khai báo của hàm này như sau :

HHOOK SetWindowsHookEx(

int idHook, // hook type

HOOKPROC lpfn, // hook procedure

HINSTANCE hMod, // handle to application instance

DWORDdwThreadId // thread identifier

);

Ý nghĩa của từng tham số :

idHook : Xác định loại hook mà ta muốn cài đặt, tham số này có thể là một trong các

giá trị sau :

· WH_CALLWNDPROC

· WH_CALLWNDPROCRET

· WH_CBT

· WH_DEBUG

Trang 8

· WH_FOREGROUNDIDLE

· WH_GETMESSAGE

· WH_JOURNALPLAYBACK

· WH_JOURNALRECORD

· WH_KEYBOARD

· WH_MOUSE

· WH_MSGFILTER

· WH_SYSMSGFILTER

Mỗi giá trị trên xác định một loại hook mà ta muốn cài đặt, mỗi loại hook có một ý nghĩa và tình huống sử

dụng khác nhau, chi tiết về từng loại hook sẽ được đề cập sau

lpfn : Địa chỉ của Filter Function mà ta muốn gắn với hook, hàm này phải được

“export” bằng macro thích

hợp khi ta cài đặt

hMod : Handle của module chứa Filter Function Nếu ta cài đặt một hook cục bộ

(nghĩa là sự thực thi của

Filter Function chỉ ảnh hưởng đối với tiến trình cài đặt hook), tham số này phải là NULL Còn nếu ta muốn

có một hook với phạm vi toàn hệ thống (tức là mọi tiến trình đang hiện hữu đều chịu ảnh hưởng bởi Filter

Function của ta), tham số này sẽ là Handle của DLL chứa Filter Function

dwThreadID : Định danh của thread ứng với hook đang được cài đặt Nếu tham số

này là một số khác 0,

Filter Function được gắn với hook chỉ được gọi trong ngữ cảnh của một thread xác định Còn nếu

dwThreadID = 0, Filter Function sẽ có phạm vi toàn hệ thống, và dĩ nhiên, nó sẽ được gọi trong ngữ cảnh

của bất kỳ thread nào đang tồn tại trên HĐH Có thể sử dụng hàm

GetCurrentThreadId để lấy được

handle của thread muốn cài đặt hook

Trang 9

Một hook có thể được sử dụng ở mức hệ thống, ở mức cục bộ, hoặc ở cả hai mức vừa nêu Bảng sau mô

tả loại hook cùng tầm ảnh hưởng của nó :

Với một loại hook xác định, hook cục bộ sẽ được gọi trước, sau đó là hook toàn cục

SetWindowsHookEx trả về handle của hook được cài đặt (là 1 giá trị có kiểu

HHOOK) Giá trị này cần được

lưu lại để dùng trong hàm UnhookWindowsHookEx khi ta muốn gỡ bỏ hook Nó sẽ

trả về NULL nếu không

thể gắn Filter Function vào hook, trong trường hợp này, hãy gọi hàm GetLastError

để biết lý do cài đặt hook

thất bại, giá trị mã lỗi là một trong các giá trị sau :

ERROR_INVALID_HOOK_FILTER : hằng số xác định loại hook sai.

ERROR_INVALID_FILTER_PROC : địa chỉ Filter Function không chính

xác

ERROR_HOOK_NEEDS_HMOD : tham số hMod của hàm

SetWindowsHookEx đang là NULL trong khi ứng dụng muốncài đặt một hook toàn cục

ERROR_GLOBAL_ONLY_HOOK : Ứng dụng đang cố cài đặt một hook

cục bộ trong khi bản chất của loại hook này là hook toàn cục

ERROR_INVALID_PARAMETER : giá trị của tham số dwThreadID

không chính xác

ERROR_JOURNAL_HOOK_SET : Đã có một hook loại “Journal”

(WH_JOURNALPLAYBACK hoặc WH_JOURNALRECORD) đang hiện

hữu trong hệ thống Vì lý do an toàn, Windows chỉ cho phép ta cài đặt một và

chỉ một hook loại “Journal” tại một thời điểm

ERROR_MOD_NOT_FOUND : Ứng dụng không thể định vị handle của

DLL trong khi muốn cài đặt một hook toàn cục

Các giá trị khác : Vấn đề an toàn của HĐH không cho phép hook này được

cài đặt, hoặc hệ thống đã hết bộ nhớ

Trang 10

Gỡ bỏ một Filter Function ra khỏi chuỗi các Filter Function của một hook Windows cung cấp hàm UnhookWindowsHookEx giúp ta thực hiện việc

này.Khai báo của nó như sau :

BOOL UnhookWindowsHookEx(

HHOOK hhk // handle to hook procedure

);

Tham số duy nhất của hàm này là handle của hook cần được gỡ bỏ Giá trị này là giá

trị được trả về từ hàm SetWindowsHookEx khi ta cài đặt hook

Chi tiết về Filter Function

Đây là thời điểm thích hợp để bàn về Filter Function Nhắc lại rằng Filter Function là một hàm được gắn với loại hook mà ta muốn cài đặt Hàm này được gọi bởi HĐH

Windows chứ không được gọi bởi ứng dụng, đây cũng là lý do mà đôi khi người ta

thường gọi nó là “Callback Function” Tuy nhiên , để thống nhất về mặt thuật ngữ,

từ nay về sau ta vẫn gọi nó là Filter Function

Tất cả các Filter Function đều có dạng sau :

LRESULT CALLBACK FilterFunc(int nCode, WPARAM wParam, LPARAM lParam);

Lưu ý : “FilterFunc” chỉ là tên hàm tượng trưng, khi cài đặt, Filter Function sẽ có tên

bất kỳ theo ý của lập trình viên

· Ý nghĩa của từng tham số truyền cho hàm.

nCode : tham số này thường được gọi là “hook code”, Filter Function sử dụng giá trị

này để quyết định cách thức xử lý đối với sự kiện

(Lưu ý là điều này hoàn toàn tuỳ thuộc vào lập trình viên Có thể hình dung hook code đem lại những thông tin nào đó, và từ những thông tin này, lập trình viên sẽ quyết định xử lý sự kiện bắt được như thế nào theo hướng riêng của anh ta)

Giá trị của hook code tùy thuộc vào từng loại hook cụ thể, và mỗi loại hook sẽ có tập hợp những giá trị hook code đặc trưng của riêng mình Có một quy luật mà dường

như các Filter Function của mọi loại hook cần tuân thủ : Khi Windows truyền cho

hàm giá trị hook code âm, Filter Function không được xử lý sự kiện mà phải gọi hàm

CallNextHookEx với chính những tham số mà HĐH truyền cho nó Sau đó, nó phải

trả về giá trị được trả về bởi hàm CallNextHookEx wParam, lParam : Đây là

những thông tin cần thiết cho Filter Function trong quá trình xử lý sự kiện Các giá trị này sẽ có ý nghĩa khác nhau tuỳ thuộc vào từng loại hook Ví dụ, Filter Function gắn với hook WH_KEYBOARD sẽ nhận mã phím ảo (Virtual-Key Code) từ wParam, đồng thời có được từ lParam thông tin mô tả trạng thái của bàn phím khi sự kiện gõ phím xảy ra

Ngày đăng: 28/10/2015, 18:04

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w