VB.net中HOOK的應用(二)
2024-07-10 13:00:48
供稿:網友
國內最大的酷站演示中心!
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類型了,因此,在聲明時要具體聲明成你想要用的類型即可。
by henry
2002.9.21(中秋夜)
如果你看過此文,調試程序出了問題,想要獲得vb.net hook的源碼,可以和我聯系(但實際上,我已經把代碼全部寫在上面了)
e-mail: [email protected]
qq: 18349592
----
聲明:本文版權與解釋權歸韓睿所有,如需轉載,請保留完整的內容及此聲明。