说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

WIN32 API-VB资料

HTML文档下载 WORD文档下载 PDF文档下载
WIN32 API-VB资料
一、VB5.0与Windows API 间的呼叫技巧

一、VB5.0与Windows API 间的呼叫技巧

一般会使用WINDOW API的情况,实在是因为VB本身不提供某些功能,但是,程式所需又
不得不然,例如:读取Registry内的资料,VB只提供SaveSetting、Getsetting 等系列
的指令,但是它只能读取特定地区的值,要读、删、更动其他区域的值时,就无法使用
。再如:仔细看一看Combo Box的Events,其中没有MouseMove,但这是我们经常用上的
一个Event,那该如何呢?是的,那只有透过Winodow API。而VB呼叫Window API一般不
都使用API检视员,直接将相对应的API COPY到我们的程式中就好,那还用什麽技巧吗?
其实不然,因为VB资料格式的问题,又加上VB本身没有指标,在许多地方需要一些小技
巧才能解决,而且我们经常因应不同的需求,将API 检视员的宣告COPY过来後再做一些
修改,最重要的,如果有一个.DLL档,它不在API 检视员中定义,那时,就只有自己想
办法啦。
一、 整数叁数

Windows API32位元VB
Int, INT ByVal Long
UNIT, DWORD ByVal Long
BOOL ByVal Long ture时为1
WPARAM, LPARAM, LRESULT ByVal Long
Handle(如HKEY) ByVal Long
WORD, ATOM, SHORT ByVal Integer
BYTE, CHAR ByVal Byte

Windows API 宣告
SHORT GetKeyState( int nVirtKey )
对应的VB宣告
Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
-----------------------------------------------------------------------------
这个API 可用来检视某些KEY (如Insert键、Num Lock、CapsLock等)是on/off。程式如
下:这个例子应该可十分楚的看到各个整数间的宣告对应。
-----------------------------------------------------------------------------
Dim InsertMode as Integer
InsertMode = GetKeyState(vbKeyInsert) And vbShiftMask
If InsertMode = 1 then
Debug.print "表示 Insert Mode"
Else
Debug.print "表示 OverWrite Mode"
End If


-----------------------------------------------------------------------------
二、 指向整数的指标
Windows API 32位元VB
LPINT (ByRef ) Long
LPUNIT (ByRef ) Long
LPBOOL (ByRef ) Long
LPDWORD (ByRef ) Long
LPHANDLE (如:PHKEY) (ByRef ) Long
LPWORD (ByRef ) Integer
LPSHORT (ByRef ) Integer
LPBYTE (ByRef ) Byte

VB内定是使用传址呼叫,所以ByRef 可以省略,也就是说
Func(ByRef param1 as type)

Func(param1 as type)
是相同的,使用传址呼叫的方式,不外??想将叁数传给API 後将结果传回来。然而LONG
型态的传址呼叫在VB中又占了相当大的份量,因为32位元的指标都是LONG的型态,而字
串、自定型态的Structure在Windows API中是以指标来传递的,而指标的传递事实上也
是Long值的传递,只不过传过去的LONG值,於WIN API中会将之当成Address,而再配合
指标运作而得指标所指的内容,这个观念在後面会很重要。
例如: -----------------------------------------------------------------------------
LONG RegOpenKeyEx(
HKEY hKey, // handle of open key
LPCTSTR lpszSubKey, // address of name of subkey to open
DWORD dwReserved, // reserved
REGSAM samDesired, // security access mask
PHKEY phkResult // address of handle of open key
);
相对应的VB 宣告
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" _
(ByVal hKey As Long, _
ByVal lpSubKey As String, _
ByVal ulOptions As Long, _
ByVal samDesired As Long, _
phkResult As Long) As Long '//最後一个叁数是ByRef之宣告
-----------------------------------------------------------------------------
我们经常会想要用程式来读取Registry中的资料,例如:我们想得知Win95的Produ ct
ID该如何做呢?这里有几个观念要先清楚:首先:ProductId在何处呢?在
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVerson下的ProductId。
我们要取得的便是
KEY 为 HKEY_LOCAL_MACHINE
SUBKEY 为 SOFTWARE\Microsoft\Windows\CurrentVerson
ValueName 为 ProductId 的value
然而要取得ProductId的value可没那麽直接,要先取得SubKey的KeyHandle而Key Handle
的取得便是利用RegQueryKeyEx的API 。程式部份在介绍Win API字串传递时再一并介绍。
三、 字串叁数
凡是所有字串叁数指标都以 ByVal 叁数名称 As String 传。如RegOpenKeyEx()的第二叁
数ByVal lpSubKey As String,便是一例。或许会问,这个例子是把subkey值传给 Win
API所以用ByVal,没什麽大不了,其实不然,要Win API传回字串时,也一定要用ByVal
的宣告。这是VB5字串格式(BSTR)与WIN API标准字串格式(LPSTR)不同的因素。 LPSTR
字串格式是NULL Terminate的字串,若有一字串"HaHa !OK!",则格式如下:
-----------------------------------------------------------------------------
Address 0 1 2 3 4 5 6 7 8 9
-- -- -- -- -- -- -- -- -- --
内容 H a H a ! O K ! \0
而BSTR则在字串的前面还有一个LONG值存字串长度,格式如下:
Address 0.. 3 4 5 6 7 8 9 10 11 12 13
------ -- -- -- -- -- -- -- -- -- --
内容 9 H a H a ! O K ! \0
-----------------------------------------------------------------------------
所以了字串以ByVal的方式来传像不像指到BSTR中第4个位置,如此一来,不就和LP STR
可以相容了吗?我想也正因为如此以ByVal的方式来传String可以取得Win API的传回值,
(就算不是如此,至少这麽想比较记得住String要用ByVal的方式传)。现在又有一个问题,
Window95 API的字串使用的是ASCII Code但VB是用Unicode,Unicode占两个位元组,那麽
能和WinAPI的字串相?所幸我们可以先不用管它,因为vb本身做了转换,即 vb传给api时,
转了一次,传回时又转回 Unicode,所以如果我们用的是Byte Array来传字串,也可以但
是要自己去转码。。然而32位元的VB 中,字串有种格式,一个是BSTR,另一个是HLSTR
,如果我们宣告的串是非固定长度者,就会是BSTR,反之则为HLSTR。
DIM BSTR5 AS STRING 劤 BSTR
DIM HLSTR5 AS STRING(255) 劤 HLSTR
VB5中WIN32 API的呼叫请多多使用BSTR,因为使用HLSTR的结果是,VB还得做HLSTR ->
BSTR的转换来呼叫WIN API若有传回STRING而後再做BSTR->HLSTR的工作。然而使用
BSTR来工作时,若处理有传回值的STRING叁数,则还要有额外的动作:
1.先给定字串的初值,且字串的长度要够放传回值。
2.传回後,去除传回值中多馀的字元。

例如:
-----------------------------------------------------------------------------
int GetWindowText(
HWND hWnd, // handle of window or control with text
LPTSTR lpString, // address of buffer for text
int nMaxCount // maximum number of characters to copy
);
该 API 取得WINDOW Title Bar的文字,而传回值是放入lpString的character个数。
VB的宣告如下:
Decl are Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long
范例一
*****************************************************************************
Dim CharCnt As Long
Dim lpString As String
Dim tmpstr As String
Dim NullPos As Long
Form1.Caption = "这是一个test"
lpString = String(255, 0) '设定初值
CharCnt = GetWindowText(Me.hwnd, lpString, 256) 'CharCnt = 12
tmpstr = Left(lpString, CharCnt) '如此做会有一些问题
Debug.Print Len(tmpstr) '得12
Label1.Caption = Left(lpString, CharCnt)
Debug.Print Len(Label1.Caption) '得8
*****************************************************************************
以范例一的例子来看,设定lpString= String(255,0)的目的,是设定255个字元的空间
给lpString(加上最後的null一共256),CharCnt的值是12,明眼者可看到len("这是一个
test") 会是8,但CharCnt是12, 所以直接使用Left()函数来取得子字串会有问题,这
是UniCode与ANSI String间的关系,所以了,当您看到有些书的范例用这种方法取子字
串,是不太完善的,所以改用范例二的方式,比较正确。
范例二
*****************************************************************************
Form1.Caption = "这是一个test"
lpString = String(255, 0) '设定初值
CharCnt = GetWindowText(Me.hwnd, lpString, 256) 'CharCnt = 12
NullPos = InStr(1, lpString, Chr(0), vbBinaryCompare)
tmpstr = Left(lpString, NullPos - 1)
lable1.Caption = tmpstr
*****************************************************************************
四、 Null 值的传递
我们再回到求ProductId的问题,我们已知使用RegOpenKeyEx()来取得subkey的Han dle
值,紧接着便是用RegQueryValueEx()来取值。
-----------------------------------------------------------------------------
LONG RegQueryValueEx(
HKEY hKey, // handle of key to query
LPTSTR lpszValueName, // address of name of value to query
LPDWORD lpdwReserved, // reserved
LPDWORD lpdwType, // address of buffer for value type
LPBYTE lpbData, // address of data buffer
LPDWORD lpcbData // address of data buffer size
);
VB的宣告(由API检视员中Copy下来者)
Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" _
(ByVal hKey As Long, _
ByVal lpValueName As String, _
ByVal lpReserved As Long, _
lpType As Long, _
lpData As Any, _
lpcbData As Long) As Long
-----------------------------------------------------------------------------
仔细看一下第三个叁数,WIN API中是LPDWORD可是VB中麽会是用ByVal的方式传递呢?原
因在於 lpReserved一定要传Null进去,VB在呼叫时便在 这叁数的位置上填0(见范例三)。
为何传Null就得这做?我们可以这麽想,我们 在程式中下指令,告诉VB要以 ByVal 的
方式传0出去,而WIN API里,它可不管VB是ByVal或ByRef,API 认定我们传进来的就是
它需要的,所以了,第三个叁数在API中认定我们传进的是一个Address,而VB 传0进去,
那代表API若去取得它的内容,便会取得Address 0 的内容,或许Window的 Null值便是
指向Address 0呢!另一个作法比较直接,将VB宣告的第三个叁数宣告由 ByVal
lpReserved As Long改成 ByVal lpReserved as String而使用时固定传 vbNullString
进去也可以。这里在一个观念,那就是VB对Win API的宣告,纯粹是给VB 自己看的,在
API中定义了一个指标的叁数,Api检视员会将之宣告成ByRef的方式(字串除外),但我们
可随需要而更动它,一个原始应为ByRef的叁数宣告,我们可以将之改为 ByVal的方式,
只要我们能取得叁数的位址,而将这型态为Long的位址以ByVal传出去, Win API 端根
本不知道VB端是用什麽方式传,反正只要我们传了一Long值进去,Win API 就会以这个
Long值当作是Address来运作。
问题还没有解决,RegQueryValueEx()的第四个叁数lpType若为REG_SZ(= 1)那代表
lpData是Null Terminate的String,若为REG_DWORD ( = 4)那代表lpData是Long值,正
是因为没有办法事先知道lpData的真正型态,所以VB就使用 ASAny的型态,它要VB放弃
型态的检查,传什麽值进去都可以,但是在这里有一些问题,如果lpType是REG_DWORD
那麽lpData以ByRef的方式没有问题,但是如果lpType 是REG_SZ,STRING是要以ByVal
的方式来宣告,所以会有冲突,而解决的方式就是改写API检视员Copy进来的宣告。
-----------------------------------------------------------------------------
Declare Function RegQueryLong Lib "advapi32.dll" Alias "RegQueryValueExA" _
(ByVal hKey As Long, _
ByVal lpValueName As String, _
ByVal lpReserved As Long, _
lpType As Long, _
lpData As Long, _
lpcbData As Long) As Long
Declare Function RegQueryString Lib "advapi32.dll" Alias "RegQueryValueExA" _
(ByVal hKey As Long, _
ByVal lpValueName As String, _
ByVal lpReserved As Long, _
lpType As Long, _
Byval lpData As String, _
lpcbData As Long) As Long
-----------------------------------------------------------------------------
使用两个宣告来解决这个问题,依不同的lpType呼叫不同的函式,即lpType=
REG_ DWORD时,呼叫RegQueryLong, lpType = REG_SZ时则为RegQueryString这也可以让
我们了解为何VB API的宣告为什麽要有Alias的存在。
范例三
*****************************************************************************
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) _
As Long
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA"
(ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, _
ByVal samDesired As Long, phkResult As Long) As Long
Declare Function RegQueryString Lib "advapi32.dll" Alias _
"RegQueryValueExA" (ByVal hKey As Long, _
ByVal lpValueName As String, ByVal lpReserved As Long, _
lpType As Long, ByVal lpData As String, lpcbData As Long) As Long
Const REG_EXPAND_SZ = 2
Const HKEY_CLASSES_ROOT = &H80000000
Const READ_CONTROL = &H20000
Const STANDARD_RIGHTS_READ = (READ_CONTROL)
Const KEY_QUERY_VALUE = &H1
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const SYNCHRONIZE = &H100000
Const KEY_READ = ((STANDARD_RIGHTS_READ Or _
KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or _
KEY_NOTIFY) And (Not SYNCHRONIZE))
Dim key5 As String, ValueName as String, strBuff as String, ResultStr as String
Dim leng1 As Long, resul As Long, hkey As Long
Dim tp As Long, i As Long
key5 = " SOFTWARE\Microsoft\Windows\CurrentVerson "
resul = RegOpenKeyEx(HKEY_CLASSES_ROOT, key5, 0, KEY_READ, hkey)
'hkey便是subkey (key5)的KeyHandle,先取得它才能存取Subkey内的ValueName
ValueName= "ProDuctId "
tp = REG_SZ
strBuff = String(255, 0)
leng1 = Len(strBuff) + 1
resul = RegQueryString(hkey, ValueName, 0, tp, strBuff, leng1)
'注意,第三个叁数传0,leng1传回copy 到strBuff的字元个数(anci)
leng1 = InStr(1, strBuff, Chr(0), vbBinaryCompare) '重新算个数(UniCode)
ResultStr = Left(StrBuff,leng1-1) '这便是ProductId的值
*****************************************************************************
在这里有另外一件事要特别说明,范例三程式中有一行leng1=Len(strBuffer)+1,这行
可省不得,很奇怪吧,为什麽明明是一个传回值,却一定要设定给它一个strBuff 的大
小呢?这是因为许多WIN API 不会聪明到找strBuff的Null Char在哪里,所以需要程式
传进去,而後它再依这个栏位传回填入strBuff 的数目。
五、Array叁数的传递
我们知道Win API 的阵列传递是传阵列的起始位址,所以了,在VB中唯一要注意的是起
始位置的写法。以另一个取得Window目录所在路径的API为
例: -----------------------------------------------------------------------------
UINT GetWindowsDirectory(
LPTSTR lpBuffer, // address of buffer for Windows directory
UINT uSize // size of directory buffer
); // 若成功,则传回目录的字元数
VB的宣告(API检视员)
Declare Function GetWindowsDirectory Lib "kernel32" Alias _
"GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) _
As Long
我们将之更改为
Declare Function GetWindowsDirectory Lib "kernel32" Alias _
"GetWindowsDirectoryA" ( lpBuffer As Byte, ByVal nSize As Long) As Long
-----------------------------------------------------------------------------
范例四
*****************************************************************************
Dim n as Long
Dim Buff() as Byte
Dim StrA as String
Buff = space(256)
n=GetWindowsDirectory(Buff(0), 256)
Buff = Leftb(Buff, n)
StrA = StrConv(Buff, vbUniCode) 'StrA便是Windows所在目录
*****************************************************************************
在范例四中,GetWindowsDirectory()传入的第一个叁数Buff(0)便是这阵列的起始 Byte
,因VB 宣告成lpBuffer As Byte,故传过去的是ByRef Buff(0)的位址,当然了,你也
可以呼叫成n=GetWindowsDirectory(Buff(1), 256),只是传回值是填在Buff(1) to
Buff(n),而Buff(0)则仍为起始的Space Character(32),因为该API传回值是字元个数
,再加上存於Buff中的是Byte Array故,使用Leftb()去除多出的byte,再用 StrConv将
Byte Array转成Unicode的字串。比照范例二的作法,我们也可以将Byte Array 改成以
String的方式来做,二者可做一比较,谁比较好或比较顺畅,那见人见智,不过可以肯
定的是,如果传的值是Binary的值,那麽使用Byte Array来做才对,因用 String来传的
话,会经过转换成UniCode的步骤,这中间会发生什麽事,没人知道。


六、CallBack Function的作法
VB的使用者通常对於这个名词有着多多少少的疑惑,或称之为"哭爸"Function,而 VB5
使用手册使用Window Procedure来说明,除非对Window 系统有一些了解,否则可能令人
更不知所云;我使用另一个例子来说明,那便是KeyBoard Hook。什麽是KeyBoard Hook
呢,简言之便是按键盘时,便会自动执行某一段Function的功能,就好比Dos时代的拦
截中断向量一般。让我们先看一下设定Hook的宣告吧。
-----------------------------------------------------------------------------
HHOOK SetWindowsHookEx(
int idHook, // type of hook to install
HOOKPROC hkprc, // address of hook procedure
HINSTANCE hMod, // handle of application instance
DWORD dwThreadID // identity of thread to install hook for
);
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有很多种,如KeyBoard Hook, Mouse Hook, JournalRecord Hook等,所以第一个
叁数指明了要哪一种Hook,第二个叁数便是Hook Procedure所在,也就是方才所说 "自
动执行某一段Function的功能"中的那一个Function,这个Function的名称可以随意给定
,但有一定的叁数传递规则,例如:
hnexthookproc = SetWindowsHookEx(WH_KEYBOARD, _
AddressOf MyKBHFunc, App.Hinstance, 0)
如此设定则每当按任一个键时,程式自动会去执行 MyKBHFunc。这个Hook Function 是
由我们所定义,但是它是由Window自动去呼叫,而不是由我们的程式呼叫,这类的
Function就叫CallBack Function。
以上面的例子来说,这个CallBack 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
'侦测 有没有按到PrintScreen键
If wParam = vbKeySnapshot Then
MyKBHFunc = 1
Debug.Print "haha"
End If
End Function
-----------------------------------------------------------------------------
这个KeyBoard Hook Function的目的主要是想拦截有没有按到Print Screen这个键,这
个键不会在Form的KeyDown, KeyPress, KeyUp Event中作用,所以只好透过KeyBoa rd
Hook去拦截。而CallBack Function放的位置有规定,一个是要与呼叫SetWindowsHo
okEx() 的地方在同样的一个Project,另外,它只能存在於.BAS档,不能放在其他地方。
KeyBoard Hook的程式於范五。
范例五
*****************************************************************************
'以下程式於Hook.bas
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
Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long
Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, _
ByVal ncode As Long, ByVal wParam As Long, lParam As Any) As Long
Public hnexthookproc As Long
Public Const HC_ACTION = 0
Public Const WH_KEYBOARD = 2
Public Sub UnHookKBD()
If hnexthookproc <> 0 Then
UnhookWindowsHookEx hnexthookproc
hnexthookproc = 0
End If
End Sub
Public Function EnableKBDHook()
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这个名称只要和
'SetWindowsHookex()中 AddressOf後的名称一样便可,不一定叫什麽
MyKBHFunc = 0
If iCode < 0 Then
MyKBHFunc = CallNextHookEx(hnexthookproc, iCode, wParam, lParam)
Exit Function
End If
If wParam = vbKeySnapshot Then '侦测 有没有按到PrintScreen键
MyKBHFunc = 1
Debug.Print "haha"
End If
End Function
'以下程式於Form
Private Sub Form_Load()
Call EnableKBDHook
End Sub
Private Sub Form_Unload(Cancel As Integer)
Call UnHookKBD
End Sub
*****************************************************************************
七、自订型态的传递
因这只要用ByRef的方式来做就没有什麽大的问题,故不做说明。
八、综合应用
我们再以一个实例来说明Win API在VB5中呼叫的技巧。有一个函式叫CopyMemory 的宣告
如下:
-----------------------------------------------------------------------------
Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" ( _
lpvDest As Any, lpvSource As Any, ByVal cbCopy as Long)
-----------------------------------------------------------------------------
这个函式可以将 lpvDest的momory copy 到lpvSource上去,cbCopy则代表要copy 多少
个byte。有了这个函式,我们可以知道一个Double值存在Memory中的各个byte到底是多
少。
-----------------------------------------------------------------------------
Dim dbl as Double
Dim bte(0 to 7) as Byte
Dbl = 168.256
CopyMemory dbl, byt(0), 8
-----------------------------------------------------------------------------
如此检视bte阵列便可以知道这Double值的各个byte是多少。再以另一个 JournalRecord
Hook为例来说明:
范例六
*****************************************************************************
' 以下在Hook.bas
Const WM_MOUSELAST = &H209
Const WM_MOUSEFIRST = &H200
Public Const WM_KEYLAST = &H108
Public Const WM_KEYFIRST = &H100
Public Const WH_JOURNALRECORD = 0
Type EVENTMSG
message As Long
paramL As Long
paramH As Long
time As Long
hwnd As Long
End Type
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
Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long
Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, _
ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long
Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" _
(lpvDest As Any, ByVal lpvSource As Long, ByVal cbCopy As Long)
Public hNxtHook As Long ' handle of Hook Procedure
Public msg As EVENTMSG
Sub EnableHook()
hNxtHook = SetWindowsHookEx(0, AddressOf HookProc, App.hInstance, 0)
End Sub
Sub FreeHook()
Dim ret As Long
ret = UnhookWindowsHookEx(hNxtHook)
End Sub
Function HookProc(ByVal code As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
CopyMemory msg, lParam, Lenb(msg)
If (msg.message >= WM_KEYFIRST _
And msg.message <= WM_KEYLAST) Then
Debug.Print msg.message, msg.paramH
End If
HookProc = CallNextHookEx(hNxtHook, code, wParam, lParam)
End Function
'以下程式於Form1
Private Sub Form_Load()
Call EnableHook
End Sub
Private Sub Form_Unload(Cancel As Integer)
Call FreeHook
End Sub
*****************************************************************************
详细的流程不多做说明,我们只把重点放在HookProc这个Hook Procedure,如果我们查
JournalRecord Hook的Hook Procedure可得定义如下:
-----------------------------------------------------------------------------
LRESULT CALLBACK JournalRecordProc(
int code, // hook code
WPARAM wParam, // undefined
LPARAM lParam // 为一个EVENTMSG Structure的address值
);
这个JournalRecordProc 对应到我们的HookProc便是
Function HookProc(ByVal code As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
-----------------------------------------------------------------------------
有没有注意到第三个叁数它是一个 ByVal的Long,指的是存放某一个EVENTMSG的位址,
而先前我们提过,自定型态的叁数传递要使用ByRef的方式才能解决,天??!它用ByVal
的方式来做,如果是C语言,那不成问题,只要如下:
-----------------------------------------------------------------------------
EVENTMSG *p;
P = (EVENTMSG *) lParam;
-----------------------------------------------------------------------------
如此便可以用 *p->message 之方式来取得内容,但VB呢?这里便要用些小技巧了,试想
,如果我们能依lParam所指的位址,一个Byte一个Byte的Copy到一个EVENTMSG的变数上
面,不就可以了吗?所以了, CopyMomory这个函式派上用场了,但是 CopyMomory的原
始宣告如下,前面两个叁数都是ByRef的方式,但目前对我们有的是lParam的内容(假设
是lParam = 25600, Address of lParam = 100100),如果我们使用底下的宣告,而去呼

-- 宣告一 ----------------------------------------------------------------------
Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" ( _
lpvDest As Any, lpvSource As Any, ByVal cbCopy as Long)
CopyMomory msg , lParam, Lenb(msg)
-----------------------------------------------------------------------------
那麽WinAPI RtlMoveMemory会得到第二个叁数值=100100,而使指标指到100100的位址,
那麽就得不到想要的资料了 (因资料在25600的位址上)。所以我们改变原始宣告,将之
变成宣告二的样子,如此VB 第二个叁数的作法会传出25600(因为ByVal嘛)给RtlMoveMe
mory,那不就成功了吗?
----宣告二 ---------------------------------------------------------------------
Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" ( _
lpvDest As Any, ByVal lpvSource As Long, ByVal cbCopy as Long)
CopyMomory msg , lParam, Lenb(msg)
-----------------------------------------------------------------------------
或许这RtlMoveMemory您在许多地方都会用上,前两个叁数时而要ByRef, 时而需 ByVal,
那是否就要定义四个宣告来因应不同之需,其实也不用,上面的例子中,只要宣告成宣
告一的样子,但是呼叫时改成:
CopyMemory msg, ByVal lParam, Lenb(msg)
在第二个叁数前加上ByVal这样这可以了啦。
这里还有另外一个做法,那就是从Hook Procedure的宣告着手,别忘了,Hook Pro
cedure是Window所呼叫的,所以它传给我们定义的HookProc()时,第三个叁数以先前的
举例来说便是传入25600,那麽,我们将HookProc()改定义成:
-----------------------------------------------------------------------------
Function HookProc(ByVal code As Long, ByVal wParam As Long, _
lParam As Long) As Long
-----------------------------------------------------------------------------
第三个叁数变成 ByRef的方式传入,所以了,用msg = lParam来取代CopyMemory的作法,
嘛可以通啦!即如下:
-----------------------------------------------------------------------------
Function HookProc(ByVal code As Long, ByVal wParam As Long, _
lParam As Long) As Long 'lParam改成ByRef
msg = lParam
' CopyMemory msg, lParam, Lenb(msg) //这行可省啦
If (msg.message >= WM_KEYFIRST _
And msg.message <= WM_KEYLAST) Then
Debug.Print msg.message, msg.paramH
End If
HookProc = CallNextHookEx(hHook, code, wParam, lParam)
End Function

返回
极限破解:利用低温环境读取Android加密数据 五种工具保护你的在线隐私 从打压到支持 微软与GitHub的爱恨情仇 “夫妻档”初创公司百万美元打造实时Hadoop系统 编程从娃娃抓起:13岁儿童的移动游戏之梦 新浪管理层调整 许良杰任CTO兼联席总裁 Java,你还会让多少人继续“受伤”? 中国或超美国 成全球智能设备市场霸主 告别手写 API文档生成工具推荐 微软Azure超越亚马逊云计算的性能测试 苹果创新已经被三星超越 不靠广告也盈利:移动应用掘金7大案例剖析 谈Clash of Clans货币化的成功 UX设计师应当规避的七大问题 初创公司Citus Data将CitusDB扩展到Hadoop 移动医疗:巢儿孵化器为何笃信是创业金矿? 一个新的雅虎:雅虎主页改版 谷歌发布Google Glass最新视频 公开接受预定 微软Windows Blue结束半程开发 或今年8月推出 JavaFX即将开源,支持iOS与Android开发 趣味编程网Codecademy携手Twitter、Evernote、Box等推出系列API教程 开发者,别让任何人绑架你的工作节奏 REST Style来袭 LinkedIn开源Rest.li框架 [探讨] 是用jQuery框架还是自造车轮呢? 一周消息树:Java 你还会让多少人继续“受伤”? Chromebook Pixel,揭开谷歌云世界的大幕 惠普:第一财季净利12亿 同比仍在下滑 谷歌发布超高清触屏笔记本Chromebook Pixel 传谷歌正开发触屏Chrome OS笔记本 今年或上市 估值25亿 视觉社交网站Pinterest融资2亿美元 跨平台开发框架Xamarin 2.0发布 不要笑偶 转分 有会打印对象编程的吗?printer对象怎样打印横线??怎样打印带风格的报表?? 表分区的数量有上限吗 ASP 0178 (0x80070005) 各位高人,请一定帮个忙吧 !! SetCellImage 利用C# 读取系统当前进程的出现问题,帮忙!! 请教高手:怎样在一个窗口里能够输入文本文件(按回车键) 另一个窗口显示出同样的文本 高手在何方 (大力请进)sql字符串处理的问题 上哪能下到exchange 2000,下到马上给分! ado问题? 数据迁移问题??? 怎么把列名选出来? [在线求助]:有什么好的方法可以使得鼠标放上有链接的地方状态栏不显示真实地址? 高手在哪!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 各位大哥﹐何為微创软件开发管理﹖﹖﹖﹖ 3com switch 3300 xm 有网管的功能吗? 关于窗口大小的问题,快来捡分~~~ 为什么2001年前的提问的问题、得分的问题、信誉分变化纪录都不见了。 无法在人才频道上提交简历。。。 请问:wsad4支持滚轮鼠标的插件哪里有下载??? 自动锁定计算机? 请问如何终止一个正在运行的应用程序 无法在人才频道上提交简历。。。 后面数据库为ms sql server2000的时间问题? DoEvents 怎么用?(不好意思,只有这么多分了!) CSDN真让我失望,大家都喜欢回答无聊的问题(不要回复) jdbc中LIKE条件如何使用prepareStatement? 请问,如果想报考系分的话,需要先过高程吗? 请问怎么把Excel形式的表结构导入到Oracle … 请问数据窗口中如何自动记录资料修改的帐户? 我倒要看看谁有本事让javac输出这样的信息? ADSL 可以直联吗? linfeng1216 (林枫) 请来领分。 在java中的暴露事件如何处理? 求PowerDesigner电子书籍资料!急! 有关_SERVER["HTTP_CONNECTION"] 两个窗体 带有setup程序和一堆代码的电子书怎么看? 这个web控件谁能介绍的详细一点 DCOM的配置问题 三国经典故事(女性,18岁以下者勿进) TIME_WAIT问题(100分!!!) linfeng1216 (林枫) 请来领分。 求C++Test2.1的license 我在用ASP.NET编程,请问如何让Datagrid的head显示两行,并且可以合并单元格。在线等 我的程序这样写对不对? 表格宽度的问题! linfeng1216 (林枫) 请来领分。 设实数√5的整数部分为a,小数部分为b,求√5×(a+a×b)的值. 环保故事征文谢绝灌水字数500字预初水平 T细胞功能检测都用什么实验 设实数根号15整数部分是a,小数部分是b ,求(b-a)平方+(b+a)平方+12b的值 第一次世界大战前各大国的殖民地具体都是哪些国家?以及后来德国要求重新划分的殖民地有哪些?最好有地图 研究发现,有一种药物可使细胞不能合成糖蛋白上的糖侧链,那么这种药物主要作用于哪种细胞器为什么是内质网而不是核糖体? 某村将48亩的耕地该种果树(如图),如果枣树与梨树的种植面积一样大,则梨树的占地面积为( )亩桃树120° 李树90 高数级数习题,1 级数un=ln n/n^2 他是发散的还是收敛点?2 选择:设0≤un≤1/n 则下列级数一定收敛的是( )A 级数un^2 B 级数un·(-1)^n 丑的读音和意思? 方形椭圆面积计算如图.长70mm,宽55mm,4个圆角均为21.请问这样的椭圆面积怎么计算。 海外殖民地被哪几国占领 燕滨扶正胶囊药理学功能实验报告书中小鼠脾细胞数IGM-PFC的影响是什么意思? 椭圆的圆环面积计算.就椭圆的圆环.要截取12平方米.该怎么计算. 4边形有几条对角线 5边形呢 10边形呢 活火山是什么 “一战”后的战胜国怎么“调整”德国殖民地的? 正12边形取出4个顶点 互不相邻的概率是多少呢 英语翻译正在设计一个请帖,求 "让爱起航" 这四个字的英文. 已知2a-b分之a+2b=5分之9则a:b=———— 不用方程解小学五年应用题李欢同学练习跳远,前6次平均跳了3.2米,又跳了2次,前后8次平均跳了3.3米,最后两次平均跳了多少米? 寅和卯的读音 已知2a-b分之a+2b=五分之九,则b分之a= 甲/乙从AB两地相向而行,第一次相遇时,距离A点8千米,继续前进,到达目的地后返回,在A点5千米相遇,AB两地距离是多少? 子丑寅都指哪段? 高数,级数,如果后项小于前项,则一般项一定趋近于0吗? 小学4年级的应用题(不用方程式)3年前,父亲与儿子的年龄和是49岁,现在父亲的年龄是儿子的4倍.父子现在各是多少岁? 子,丑,寅,牟,辰,巳,戊,未,申,酉,戌,亥.罗马发音火影里的结印手势要用的 梨树下可以套种什么蔬菜 1.甲乙两车同时从两地相对开出,甲车每小时行50千米,乙车每小时行42千米,途中甲车因故障停了半小时,乙车开出5小时后两车在途中相遇,甲乙两地相距多少千米?2.上午9:30甲乙两车同时从A.B两 谢道韫咏絮中,“公大笑乐”的原因是什么?继! 梨树套种 我们这里是河南平原地区 100多目梨园呢 请问套种什么合适 文言文单字解释范式字巨卿,少游学于太学,与汝南张劭为友.劭字元伯.二人并告还乡里.式元伯曰:"后二年当还,将过尊亲.".(后面省略)将过尊亲: 日本的活火山有哪些 苹果地套种什么农作物 求古文单字解释《鬼怕恶人》中的,然则祸当行于后来者,中的“然则”怎么译 地球上现在有多少座活火山? 大蒜不能和哪些作物套种 文言文单字解释(急)贤于材人远矣 (找出通假字)____通____ 解释:_________ 中国有活火山吗?在哪? 写一个你自己的例子来说明通往广场的路不止一条250字 桃花源记中的一词多义和词类活用都有哪些? 使人摸不透,难以揣测的成语 写一个科学事例,证明一个名言的故事(200字) 看到此网的请快速回答 4棵梨树产梨a千克,100棵同意产量的梨树产梨()千克等腰三角形的一个底角是n°它的顶角是()° 使人摸不透,难以预测.根据意思写成语 美国为什么没有瓜分德国殖民地? 4棵梨树产梨a公斤100棵一样产量的梨树产梨多少公斤? 摄取实验需要根据细胞在每个孔内的量的多少来分析其对药物的摄取情况利用大鼠胚胎细胞做药物的摄取实验后,需要根据细胞在每个孔内的量的多少来分析其对药物的摄取情况.而蛋白量可 一战后,美国为何关注德国赔偿问题 苹果树和梨树共有100棵,苹果树比梨树多5分之一.梨树有多少棵?有浓度为8%的盐水200克,需加入多少克水,才能成为浓度为5%的盐水? 做药物的细胞毒性试验时药物与细胞悬液作用产生沉淀该怎么解决啊?实验还能进行吗? 有没有收梨树的(大约10几亩梨树)哪个场子做家具的收梨树 有知道的吗 知道的说下谢谢 Q416355023 拱顶和穹顶的优点 古文单字解释宣于夏门亭候之,之:岁满不持一砚归,岁:无丝竹之乱耳,之:渔人甚异之,之: 凡尔赛和约中美国为什么没有瓜分德国殖民地 设a是实数(4-√5)的整数部分,b是实数(4-√5)的小数部分.(1)a=?,b=?(2)求a+4/b的设a是实数(4-√5)的整数部分,b是实数(4-√5)的小数部分.(1)a=?,b=?(2)求a+4/b的值,结果可保留根式 文言文的单字解释(就两个)急!“或异二者之为”和“卿今者才略”的者分别都是什么意思?我要问的是“者”字分别解释为什么?卿不是“你”的意思吗? 桃花红村种桃树的棵树是梨树的7/10,桃树有350棵.梨树有多少棵? 已知椭圆穹顶长轴短轴和高度,求椭圆穹顶面积, 摸有什么组词 世界新闻报窃听丑闻开审 卡梅伦政府威浙江释疑吴英资产处置:困难重重 已追清华公布领军计划推荐学校 浙江三中学疑似Nexus5第一张照片流出 摄像浙江19所高中入围清华大学领军计划名移动设备进入美国儿童生活 使用人数两国务院同意杭州再启城市总体规划修编女星着透视装惊现内裤 强搂导演遭拒绝11年来最强风暴袭英国 海陆空交通大美国在欧反导基地开建 防范伊朗导弹引中央巡视组:把发现问题、形成震慑作为专家析中央巡视组组长一次一授权:改变网络红人板车哥来到余杭 行万里路为穷16岁伴娘婚礼上遭多人扒光摸遍全身(温州公交车刮擦劳斯莱斯幻影加长版轿车11年来最强风暴袭英国 海陆空交通大英国“小报女王”将接受刑事审判 令世美国在欧反导基地开建 防范伊朗导弹引安倍突访土耳其 传为劝阻土放弃购中国杰克逊私人医生刑满出狱 欲再获行医执浙江11月1日起试发布县级城市实时空转型发展必须“爬坡过坎”国防部回应美方“中国网络入侵报告”:全国慈善捐助止跌回升中央财政支持太平湖生态保护县乡建菜市场可获资金支持公安部海外“猎狐”织天网“中国梦”特种邮票在京发行我省举行全国科普日主场活动中科大两教授获“求是杰出青年学者奖”对症治顽疾 村庄变美了培育增长点 激发新动力李登辉妄称:日本应该改正宪法做个真正中国女排巴里两天三练 郎平:场地硬不珍妮毫不担心科比复出 透露禅师坚信飞长三角合力捧红“老品牌” 圈内品牌数一箱山竹“急”飞合肥 终于送到 “霉家里水质如何?打开手机看看“小英雄”到底上了几天学?乌克兰冲突双方同意从交火地区各撤退110项科普活动等您参与梦游性侵一名女子 瑞典男子被判无罪
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘