Hệ điều hành Windows điều khiển các ứng dụng bằng cách gửi các Message đến các Window của các ứng dụng. Những Message này thông báo đến các Window, cho chúng biết khi nào có cú nhắp chuột…và tất cả các thông tin cần thiết khác để Window ứng xử chính xác. Theo cách này, một ứng dụng Windows tối thiểu phải chứa một hàm để xử lí các Message này (hàm WindowProc). Hàm này được khai báo với hệ thống khi Window được tạo để Windows có thể biết sẽ gửi Message đó cho ai, và không bao giờ nhầm lẫn.Ở đây chúng tôi xin lưu ý để các bạn không bị nhầm lẫn. Thông thường chúng ta quan niệm một Window là một cửa sổ chương trình. Nhưng theo cái nhìn của HĐH Windows thì một Window có thể là một Form, có thể là một TextBox và cũng có thể là một Button… Vì thế nên từ Window mà chúng tôi dùng ở đây là chỉ tất cả các Control chứ không phải là một Form như chúng ta đã thường nghĩ.Trở lại vấn đề, trong lập trình Windows, Hook(hay còn gọi là Subclass) là kĩ thuật mà trong đó chúng ta sẽ chặn những sự kiện (các message, các cú nhấn chuột, các cú gõ phím) trước khi chúng đến được ứng dụng. Hàm này có thể làm việc trên các sự kiện, hoặc trong một số trường hợp có thể thay đổi hoặc vô hiệu chúng. Hàm chuyên nhận các sự kiện gọi là filter functions (chúng tôi tạm dịch là hàm xử lý Message) và được phân loại tùy theo sự kiện mà nó nhận xử lý.Kĩ thuật Hook cung cấp một khả năng lập trình rất mạnh, vượt ra ngoài những gì mà môi trường lập trình cung cấp cho chúng ta. Sử dụng kĩ thuật Hook chúng ta có thể xử lí, thay đổi tất cả các Message cho tất cả các control và Dialog Box hoặc Menu của ứng dụng và của hệ thống. Ta cũng có thể chặn bất kì một Message nào khi hàm SendMessage được gọi. Nói chung với kỹ thuật này chúng ta có thể biết được những gì đang xảy ra và thực sự làm chủ chương trình.Cách SubClass1. Tạo một Project mới với tên mặc định là Form1.2. Thêm vào hai nút bấm (Command Button) với tên là Command1 và Command2.3. Thêm đoạn Code sau vào một Module
eclare Function CallWindowProc Lib “user32″ Alias _”CallWindowProcA” (ByVal lpPrevWndFunc As Long, _ByVal hwnd As Long, ByVal Msg As Long, _ByVal wParam As Long, ByVal lParam As Long) As LongDeclare Function SetWindowLong Lib “user32″ Alias _”SetWindowLongA” (ByVal hwnd As Long, _ByVal nIndex As Long, ByVal dwNewLong As Long) As LongPublic Const GWL_WNDPROC = -4Public Const WM_LBUTTONDOWN = &H201Public IsHooked As BooleanGlobal lpPrevWndProc As LongGlobal gHW As LongPublic Sub Hook()If IsHooked ThenMsgBox “Dung hook hai lan mà khong unhook ” & _”neu khong ban se khong the hook.”ElselpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, _AddressOf WindowProc)IsHooked = TrueEnd IfEnd SubPublic Sub Unhook()Dim temp As Longtemp = SetWindowLong(gHW, GWL_WNDPROC,lpPrevWndProc)IsHooked = FalseEnd SubFunction WindowProc(ByVal hw As Long, ByVal uMsg As _Long, ByVal wParam As Long, ByVal lParam As Long) As LongIf uMsg = WM_LBUTTONDOWN ThenMsgBox “Hook Test”ElseWindowProc = CallWindowProc(lpPrevWndProc, hw, _uMsg, wParam, lParam)End IfEnd Function3. Thêm vào Form đoạn code sau:Private Sub Form_Load()gHW = Me.hwndCommand1.Caption = “Hook”Command2.Caption = “Unhook”End SubPrivate Sub Command1_Click()HookEnd SubPrivate Sub Command2_Click()UnhookEnd SubPrivate Sub Form_QueryUnload(Cancel As Integer, UnloadMode _As Integer)If IsHooked ThenCancel = 1MsgBox “Unhook truoc khi dong, neu khong VB se Crash, va moi thu ban lam se mat.”End IfEnd Sub4. Chạy ứng dụng, nhấn vào nút Hook. Sau đó bấm chuột trái lên Form, một Message box sẽ xuất hiện. Nếu không có lỗi gì thì bạn đã thành công rồi đấy.Phân tích:Bạn đừng xem thường ví dụ vừa rồi. Nó có vẻ hơi tầm thường (xét về tác dụng) nhưng lại chứa đựng trong đó rất nhiều thứ. Sau đây tôi sẽ phân tích tỉ mỉ từng bước cho các bạn.Đầu tiên có một điều cần nhớ là phải bấm vào nút Unhook trước khi đóng ứng dụng, đây là điều quan trọng vì nếu không VB sẽ đỗ vỡ, và nếu bạn quên chưa lưu thì mọi công lao của bạn sẽ đi xuống biển. Vì thế hãy cẩn thận lưu trước khi chạy thử vì không phải lúc nào bạn cũng làm đúng đâu. Một điều hay nữa là hàm của bạn sẽ xử lí trước tất cả các sự kiện của VB. Ví dụ như nếu bây giờ bạn thêm vào sự kiện Form_MouseClick() một công việc gì đó thì nó sẽ không có tác dụng (trừ phi bạn nhấp chuột trái). Bạn có thể tự thử lấy.Để có thể Hook được một Windows bạn cần phải biết một thứ rất quan trọng đó là Handle của Window cần Hook. Đây là một số nguyên đặc trưng cho mỗi Window, bạn không thể tạo ra nó nhưng có thể truy cập nó bằng thuộc tính hWnd (Ví dụ: Form1.hWnd, Text1.hWnd…). Bây giờ chúng ta trở lại với ví dụ trên. Công việc đầu tiên của kĩ thuật Hook là phải báo với Window hàm xử lý Message của bạn nằm ở đâu trong bộ nhớ để Windows sẽ truyền tham số vào đó khi cần thiết. Chúng ta sử dụng hàm SetWindowLong để làm việc này.lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)Hàm SetWindowLong có nhiều chức năng khác, trong trường hợp này nó sẽ thay đổi hàm xử lí Message bằng hàm WindowProc do ta định nghĩa. Sau đó nó sẽ lưu lại điạc chỉ trong bộ nhớ của hàm xử lý Message cũ vào biến lpPrevWndProc chúng ta sẽ cần dùng lại nó sau này.Sau khi đã khai báo xong với Windows chúng ta hoàn có thể tiến hành kĩ thuật Hook. Bây giờ chúng ta hãy xem lại cái quan trọng nhất của chương trình – hàm xử lí Message :Function WindowProc(ByVal hw As Long, ByVal uMsg As _Long, ByVal wParam As Long, ByVal lParam As Long) As LongIf uMsg = WM_LBUTTONDOWN ThenMsgBox “Hook Test”ElseWindowProc = CallWindowProc(lpPrevWndProc, hw, _uMsg, wParam, lParam)End IfEnd FunctionKhi hàm này được chạy, nó sẽ có 4 đối số, 4 đối số này sẽ được Windows truyền cho hàm mỗi khi có một sự kiện, đây là ý nghĩa của các đối số :*hw chính là Handle của Window nhận Message (trong ví dụ này chính là Handle của Form).*uMsg là Message mà Windows gửi đến cho hàm (bạn sẽ chặn những Message này).*Còn wParam và lParam là các thông tin đi kèm theo Message, các thông tin này đôi khi cũng rất quan trọng nhưng trong trường hợp này ta chưa cần đến chúng. Chúng ta sẽ gặp lại chúng trong các ví dụ sau.Trong đó đối số quan trọng nhất là uMsg. Nó là những Message do HĐH Windows gửi đến, và chính là những gì mà chúng ta sẽ chặn lại. Trong ví dụ trên chúng ta đã chặn Message WM_LBUTTONDOWN lại để xử lí. Các Message được gửi đến bằng những con số, chúng được lưu trong các hằng, có thể tìm thấy các hằng này trong thư viện MSDN hoặc API Text-Viewer. Nếu không có Message mà ta muốn chặn thì ta phải trả quyền hoạt động lại cho hàm xử lí Message cũ, đây là điều rất quan trọng và bạn không được phép quên, nếu không chương trình của bạn sẽ chẳng đáp ứng gì đâu. Để trả lại các Message không cần thiết cho hàm xử lí cũ ta sử dụng hàm CallWindowProc, hàm này sẽ gửi trả lại những đối số do Windows gửi đến lại cho hàm xử lý Message cũ, lpPrevWndProc là địa chỉ của hàm xử lý Message cũ trong bộ nhớ mà chúng ta đã lưu lại từ trước :WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam)Bạn đã thấy vì sao chúng tai phải lưu lại địa chỉ trong bộ nhớ của hàm xử lý Message cũ rồi chứ (lpPrevWndProc). Đấy là những công việc mang tính thủ tục mà ta cần phải làm khi áp dụng kĩ thuật Subclass.Note: Trong ví dụ trên bạn phải bấm vào nút Hook để thực sự bắt đầu Hook, và nhớ Unhook trước khi kết thúc chương trình. Nếu bạn đang vò đầu bức tai không hiểu tại sao nó không chạy thì đây là lời giải đáp cho bạn.
« Cách viết Module trên Drupal GIỚI THIỆU VỀ KỸ THUẬT LẬP TRÌNH HOOK »
