VB.net中HOOK的應用(CNET中文網)

來源:互聯網
上載者:User
中文 VB.net中HOOK的應用

在vb.net版做版主有一陣子了,一直在思考怎麼能更好地協助大家理解vb.net的新概念與編程方法。小小的回貼很難說清楚很多問題,所以利用一下文檔中心這片廣闊的天地,進行更深入的探討。本人水平有限,不免錯誤與遺漏多多,請朋友們批評指正。

基本的想法是,寫一系列的文章,講解一下vb.net與vb的區別,vb.net中的新概念,vb.net控制項的使用,vb.net解決問題的新方法。如果時間允許,我希望自己能快快開展這個工作。更重要的是網友們的支援與鼓勵會是我繼續的源動力。

今天是第一次寫,不知道從哪開始,權衡再三,決定從API調用問題開始入手。Vb的愛好者經常給我寫信或發貼,詢問vb.net中API的變化,那麼今天我們就一起來探討一下。雖然微軟推出vs.net的目的是要用於跨平台,那麼Windows API是不被推薦使用的,但從此入手,能讓我們更方便地理解vb.net所做的改變,所以:

Let’s GO………………………………………………

本文不是講解Hook原理的文章,僅是討論一下在vb.net如何調用Hook功能,以及vb.net中API使用的變化問題。

由於Hook種類較多,本文中以最常用的鍵盤Hook為例進行分析。

一、先來看一下在vb中是如何?的。截的是空格鍵。表現的目標是:一個form,上面有一個textbox,不管焦點是否落在textbox裡,按空格鍵,不會在textbox中輸入一個空格,而是變成在textbox中寫入一句話:“HOOK成功!”

1.在一個Module中寫入下列代碼:

調用的API的申明:

Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long

函數說明:本函數是用於啟動Hook設定。

idHook是Hook的類型,即處理的訊息類型。

Lpfn是Hook子程(函數或過程)的地址指標。如果dwThreadId參數為0或是一個由別的進程建立的線程的標識,lpfn必須指向DLL中的Hook子程。除此以外,lpfn可以指向當前進程的一段Hook子程代碼(我們利用的就是這個性質)。

hMod是應用程式執行個體的控制代碼,標識包含lpfn所指的子程的DLL。如果dwThreadId 標識當前進程建立的一個線程,而且子程代碼位於當前進程,hMod必須為NULL。

dwThreadId是與安裝Hook子程相關聯的線程的標識符,如果為0,Hook子程與所有的線程關聯。 傳回值:函數成功則返回Hook子程的控制代碼,失敗返回NULL。

Declare Function UnHookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long

函數說明: 本函數是解除Hook之用。hHook是Hook函數的控制代碼。

Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal ncode As Long, ByVal wParam As Long, lParam As Any) As Long

函數說明:本函數的作用是將當前Hook鏈中的Hook資訊傳遞給下一個Hook。

hHook是當前Hook的控制代碼,一個應用程式接收這個控制代碼,作為先前調用SetWindowsHookEx函數的結果。

nCode指的是傳遞到當前Hook過程的Hook代碼,下一個Hook過程使用這段代碼去決定如何處理Hook資訊。

wParam指傳遞給當前Hook過程的wParam值,它的具體含義是由當前Hook鏈中的相關Hook的類型決定的。

lParam指傳遞給當前Hook過程的lParam值,它的具體含義是由當前Hook鏈中的相關Hook的類型決定的。

2. 定義的常量是:

Public hnextHookproc As Long
Public Const WH_KEYBOARD = 2 ‘這個是表明Hook的種類是鍵盤Hook
Public Const PM_KEY_SPACE = &H20 ‘空格鍵

3.程式碼片段

Public Sub UnHookKBD()  ‘解鍵盤HOOK函數
If hnextHookproc <> 0 Then
UnHookWindowsHookEx hnextHookproc
hnextHookproc = 0
End If
End Sub

Public Function EnableKBDHook() ‘設定鍵盤HOOK
If hnextHookproc <> 0 Then
Exit Function
End If
hnextHookproc = SetWindowsHookEx(WH_KEYBOARD, AddressOf _
MyKBHFunc, App.hInstance, 0)
If hnextHookproc <> 0 Then
EnableKBDHook = hnextHookproc
End If
End Function

Public Function MyKBHFunc(ByVal iCode As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
MyKBHFunc = 0
If iCode < 0 Then
MyKBHFunc = CallNextHookEx(hnextHookproc, iCode, wParam, lParam)
Exit Function
End If
If wParam = PM_KEY_SPACE Then ''偵測 有沒有按到空格鍵
MyKBHFunc = 1
‘加入自己的代碼,用於表明響應
    form1.text1.text=”Hook成功!”
End If
End Function

4.在Form中的代碼很簡單:

Private Sub Form_Load()
Call EnableKBDHook
End Sub
Private Sub Form_Unload(Cancel As Integer)
Call UnHookKBD
End Sub

打完收工!現在在form表單狀態下,敲空格鍵,就會響應你在MyKBHFunc函數中寫的代碼了。

二、那麼,在vb.net中對API的調用和Hook的實現上作了什麼樣的變化呢?

我們對應著一個問題一個問題來看:

1. 建立一個module,先寫一下API的聲明:

  Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As HookType, ByVal lpfn As HOOKPROC, ByVal hmod As Integer, ByVal dwThreadId As Integer) As Integer

Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Integer) As Integer

Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Integer, ByVal ncode As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

和上面vb6裡的聲明比一下,你發現了什嗎?是不是資料類型發生了變化?integer代替了long。這個好理解,因為vb.net中integer定義為32位(4位元組)的整數,值的範圍是-231到231(首位是符號位),這與vb6中long的定義是一致的,因此,我們必須做一下這樣的轉換。

等等,還有一個變化,就是SetWindowsHookEx的參數lpfn的類型變成了HOOKPROC,那……那是什麼意思。噢,wait,我再補一句聲明先:

Public Delegate Function HOOKPROC(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

看出來什麼了嗎?HOOKPROC其實就是一個函式宣告,但是前面有一個Delegate(委託)是什麼意思?為什麼在vb.net中不能用integer表示lpfn的類型了呢?我們回頭看一下vb6中調用API的句子:

hnextHookproc = SetWindowsHookEx(WH_KEYBOARD, AddressOf _
MyKBHFunc, App.hInstance, 0)

Lpfn在這裡被表示為AddressOf MyKBHFunc,即說明Hook發生作用時,調用的子程是MyKBHFunc。也就是說lpfn表示的是函數或過程的地址。在vb6中用long類型就可以記錄下函數或過程的地址。

在vb.net中,有了一點小變化嘍,AddressOf 運算子建立的是一個指向指定的子程的子程委託。當指定的子程是一個執行個體方法時,子程委託同時引用執行個體和方法,以便當調用該子程委託時,調用指定執行個體的指定方法。AddressOf 運算子可以用作委託建構函式的運算元,或可以用在編譯器能夠確定委託類型的上下文中。

所以,正是由於Addressof建立的不再只是簡單的函數指標了,而是子程委託!打住先,什麼是委託?(^?^)

解釋一下:事件是對象發送的訊息,以發訊號通知操作的發生。操作可能是由使用者互動(例如按一下滑鼠)引起的,也可能是由某些其他的程式邏輯觸發的。引發(觸發)事件的對象叫做事件發送方。捕獲事件並對其作出響應的對象叫做事件接收方。在事件通訊中,事件發送方類不知道哪個對象或方法將接收到(處理)它引發的事件。所需要的是在源和接收方之間存在一個媒介(或類似指標的機制)。.NET 架構定義了一個特殊的類型(Delegate),該類型提供函數指標的功能。看,這裡提到了.net架構,所以C#等vs.net中的語言都可以有這個類型嘍。

委託就是可用於調用其他對象方法的對象。與其他的類不同,委託類具有一個簽名,並且它只能對與其簽名匹配的方法進行引用。這樣,委託就等效於一個型別安全函數指標或一個回調。因為它們與其他程式設計語言中所使用的函數指標相似。但不同於函數指標,Visual Basic.NET 委託是基於 System.Delegate 類的參考型別,它可以引用共用方法—無需特定即可調用的方法—和執行個體方法。(具體內容請自已去查閱一下MSDN或等我的後續文章再說明)

回過頭來總結一下,也就是說,Addressof建立的是DelegateType(委託類型)。而不是簡單的子程指標了,所以它的標記法就不是地址類型的long了,而是與調用的子程相一致的委託類型表示形式。因此,我定義了一個與MyKBHFunc聲明同形的委託函數HOOKPROC來表示lpfn的類型。

(呼,一頭汗,還不知道說清楚了沒有。希望是說清楚了……)

繼續,我又接著聲明了一個API:

Declare Function GetCurrentThreadId Lib "kernel32" Alias "GetCurrentThreadId" () As Integer

函數說明:本函數是用於擷取當前線程一個唯一的線程標識符。傳回值:當前的線程標識符。這個有什麼用,一會再說,反正是個簡單的問題,不如賣個關子,哈哈……(不要砸我)

2. 定義的常量是:

Public hnextHookproc As Long
Public Const WH_KEYBOARD = 2 ‘這個是表明Hook的種類是鍵盤Hook
Public Const PM_KEY_SPACE = &H20 ‘空格鍵

或者,實際上也是,我在程式中對上面的第二句寫法改變了一下,也沒什麼了,就是多交待一點東西給朋友們嘛:

Public Enum HookType
  WH_KEYBOARD = 2
End Enum

定義成了一個枚舉。其實Hook的種類真的很多,比如有:WH_CALLWNDPROC、WH_CALLWNDPROCRET、WH_CBT 、WH_DEBUG、WH_GETMESSAGE等等。所以你不妨寫一個枚舉,以達到一勞永逸的目的。

3. 程式碼片段

Module Module1

Public frm1 As New Form1() ‘這個的作用,最後再說

Declare Function GetCurrentThreadId Lib "kernel32" Alias "GetCurrentThreadId" () As Integer

Declare Function SetWindowsHookEx Lib "user32" Alias _

"SetWindowsHookExA" (ByVal idHook As Integer, ByVal lpfn As HOOKPROC, _

ByVal hmod As Integer, ByVal dwThreadId As Integer) As Integer

Declare Function UnhookWindowsHookEx Lib "user32" _

(ByVal hHook As Integer) As Integer

Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Integer, _

ByVal ncode As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

Public Delegate Function HOOKPROC(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

Public hnexthookproc As Integer

Public Const PM_KEY_SPACE = &H20

Public Enum HookType

WH_KEYBOARD = 2

End Enum


Public Sub UnHook()  ‘解Hook

If hnexthookproc <> 0 Then

UnhookWindowsHookEx(hnexthookproc)

hnexthookproc = 0

End If

End Sub

Public Function SetHook() ‘設定Hook

If hnexthookproc <> 0 Then

Exit Function

End If

hnexthookproc = SetWindowsHookEx(HookType.WH_KEYBOARD, AddressOf MyKeyboardProc, 0, GetCurrentThreadId())

我把第三個參數設為0(即NULL),表示的是此Hook的代碼在此進程中。第四個參數用了一個API去取安裝Hook子程相關聯的線程的標識符。(參見前面的API聲明)

End Function


Public Function MyKeyboardProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

MyKeyboardProc = 0

If nCode < 0 Then

MyKeyboardProc = CallNextHookEx(hnexthookproc, nCode, wParam, lParam)

Exit Function

End If

If wParam = PM_KEY_SPACE Then

MyKeyboardProc = 1

‘寫入你自己的代碼

frm1.textbox1.text=”HOOK成功!”

End If

End Function

Sub main()

Application.Run(frm1)

End Sub

End Module

同時請在:

解決方案管理器-〉windowsapplication1.sln -〉右點滑鼠 -〉屬性 -〉通用屬性->常規->啟始物件 -〉改為Module1

4.在Form1中的代碼:

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Call SetHook()

End Sub

‘vb.net中沒有form_unload事件了,而是用closing

Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing

Call UnHook()

End Sub


最後簡單說明一下,為什麼我在module1裡用了Public frm1 As New Form1()這句話,及啟始物件 -〉改為Module1的作法。這是由於vb.net已經是OO的了,如果你是CSDN上vb.net版的常客,你就會很熟悉這個問題,我們已經討論過N次了。我也回過不知多少貼子來說明這一問題。由於和本文主題與篇幅所限,您要是對這個問題不明白,請先看一下:

http://www.csdn.net/expert/topic/965/965919.xml

獲得一些概念。我會在後續的文章中進行更為詳細和系統的介紹(其實它也是我最想先寫的問題之一)。


結束語:關於API的調用,本文只涉及了冰山之一角,關於具體的調用變化,我會根據vb.net版上的具體情況再寫相關的文章來說明的。

對了,補充一點,vb.net不再認any類型了,因此,在聲明時要具體聲明成你想要用的類型即可。



相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。