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

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

返回
[TUP第30期]直击移动应用开发难点 探讨跨平台最佳解决方案 用动态语言编写程序,命名更重要 命中率80%,磁盘I/O减半,Flashcache的发展史 这次它很低调:谷歌正式发布Android4.4和Nexus 5 iPhone 5S来了,iPhone 6还会远吗?概念机来袭 [开源推荐]PhysicsJS:基于JavaScript的强大的物理引擎 望虚窥实!专访Oculus Rift创始人Palmer Luckey 利益至上,开源也不免于此 开发者必备,超实用的PHP代码片段! 福利来了!AngularJS资源大集锦 移动周报:“360儿童卫士”手环的是与非 Ion Stoica和他的数据故事(上) 开发者必备:测试网站速度的五个免费在线工具 谷歌员工曝光在谷歌工作那些最糟糕的事情 MDCC 2013全民移动 玩转智能硬件展 如何降低白噪声对网站用户体验的影响? 谁才是真正的资深开发者? 东软董事长刘积仁阐述三大战略 构建以知识资产驱动的商业模式 一切皆为 JavaScript 黑莓:弃私有化任命华人为临时CEO 卖给联想的节奏? 谁说WP仍然不行?意大利WP份额已超iOS MDCC之移动娱乐游戏峰会:百家手游齐逐金玩奖 专访Mozilla宫力:基于Web是Firefox OS最大的资本 如何设计一款Java框架? AngularJS优化最佳实践:从1200ms缩减到35ms Linus Torvalds宣布Linux kernel3.12正式发布! 硅谷的技术文化——渴求独处! MDCC为移动开发者服务:一看、一聊、一聚 做华为合伙人:华为开发者联盟沙龙·北京站纪实 Q3全球智能手机报告:Android和iOS基本不变 WP最得意 Google Apps不再支持IE9 IE9用户或将无法使用Gmail等服务 谁知道哪里可以弄到这本书"Windows Graphics Programming: Win32 GDI and DirectDraw" 程序员杂志第7期的程序擂台目前为止只有一个人做对了 分数不高,还请各位DX们指点以下:如何用DELPHI控制excel的格式(比如第一列的宽度,第五行的高度),以及如何在WORD中生成表格,其中填入数据库的数据. 各位情感高手请解答(没有谈过恋爱的免谈) 一个list控件,style设为checkbox,要怎样才能使列表中各项的默认值是选中状态,也就是说各项一开始都是打了勾的 阿虎菜问7:请问C与C++中的内封函数应该差不多吧? s_cube(竹子), s_cube(竹子)~!! 我爱你,s_cube(竹子) s_cube(竹子) ~!在哪里 简单问题(1) 如何不运行编译? 有谁知道怎么从perl里调用一个c库里的函数????????? 请问在CB中如何隐藏WINDOWS的任务栏呢? 今天不爽,下班后打星际,大家一起来。 指点!在PB中怎样用程序作成一张动态的交叉表 编辑控件怎样才能显示那些怪字符? 有没有人喜欢郑智化 大家推荐好听的几首歌吧! 请教:怎么学c++? 高手点拨! 各位网络高手.如何在程序里讲ip地址转化为域名 有什么方法,可使表单内的组件背景变成透明的而文字不透明,请看例子: 编辑控件怎样才能显示那些怪字符???? swall(壹心) 你还在深圳特区吗? 如何设置Session的密码?请高手指点迷经^^^^^ 哪位可以解释一下下面的代码? perl 的人气太差。罢了罢了。找本perl自己搞定 MsSQL7.0 当日志文件大小未限制而变得很大时,怎样清日志文件,使其减小? 请教诸位牛兄牛姐牛夫人---有难度哦(着急出活!) 基本问题:如何将一个.txt中的内容读入到内存再从内存中读出并存为另一个文件?谢了先!!!! 高手帮帮忙!!!!!!!!!!!!!! 又有台风了。。。。 在zip包中加入ASP推荐的PAD File有用吗????? PB连接SQL数数库,为什么显示的表会不全 如何把一个文件file.dat里的数据写入软盘0磁道0磁头1扇区. 我要骂人! chris2001(牛虻),不是东西 华为工程师 谁知道 vb 的序列号?????? VS.NET 有开了个新铺子。为什么我的ejb/j2ee还没有开张。气愤。 老大们:我是菜鸟,有问题要问,不过不要笑我。一定给分的 再次发问:关于在SQL SERVER中的某张表上建立触发器...... 没有有这样的病毒,或者文件 各位老大,如果有asp、sql基础学jsp和php是不是比较容易一些呀?言者有分哦! dll中怎样调用主程序中的函数? 新手请教接口聚合!!!!!!!!! 高分求教!有没有谁编写过WEB服务器程序? 微软OfficeXp女孩 请教一个报表问题? 小技巧!!! delphi中调用MessageBox出错,高手指点一下。 大家好,这是我提的第一个问题!我学DELPHI才两周!对您的指点,小弟万分感激! 请问sybase12。0中,游标能不能用动态sql申请 紧急求助:如何调用父对话框的函数 焓是什么意思? 九年级物理知识点总结 对知识点有解释的 下列说法正确的是A物体在它的运动状态发生改变时,惯性表现最明显 为什么等压下绝热反应中标准摩尔焓变等于零 九年级物理的知识点有哪些 物体的运动状态改变,的改变或?的改变或它们同时改变.要改变物体的运动状态,必须对物体 某化学反应在恒压,绝热和只做体积功的条件下,体系温度由T1升为T2,此过程的焓变为()A △H (2013•苏州一模)随着夏季的来临,小明想为家庭设计一个54L简易太阳能晒水箱.他随着夏季的来临,小明想为家庭设计一个淋浴用的能装54L的简易太阳能晒水箱,他从资料中得知,地球上与 质量500g的金属,温度从20℃上升至10℃,吸收热量3.5*10^4J,求这种金属的比热容,并判断是哪种金属. 温度会改变为什么焓变还是等于零焓变不是状态函数 只跟温度有关吗?为什么温度变了 焓变还为零 太阳能热水器哪个牌子好?谁来说下?太阳能介绍啊dhi8com谁有更好的? 甲乙两种物质,质量之比是4:1,放出热量之比是3:1,则它们的比热容之比和降低温度之比分别是下列四种情况可能满足题设的条件是:( )①1:1,3:4 ②2:1,1:3 ③1:2,3:2 ④1:3,9:4A.①② B.①③④ C.②③ D. 气体等焓节流后会温度会变化,我想知道这个现象的微观解释是什么,详细点……理解能力有限有的气体节流后会降温,而有的气体会升温这是为什么?还有咱用的冰箱也利用了节流,给具体解释 谁知道2013年的气候预测上班年是冷还是热? 甲、乙两种物质的比热容之比是2∶1,质量之比是3∶1,若它们吸收的热量相等,则甲、乙升高的温度之比为________;若它们升高相等的温度,则它们吸收的热量之比是__________________.若甲、乙两种 绝热节流前后,气体的焓不变,为什么温度还会变? 从现在到2013年的一月初三一共有多少天?看见的朋友要注明 从2008年的几月几号算起的 甲乙两物体,质量比为1:2,升高的温度比为2:1,吸收相同的热量,则甲乙的比热容之比为 什么叫节流?节流是个等焓过程,为什么? (2013连云港)27.下列关于氧气的说法中错误的是 A.氧气约占空气总体积的1/5 B.氧气可以支持燃烧,说明氧气具有可燃性 C.氧气供给呼吸,它和体内物质反应,释放能量,维持生命活动的需要 D _的某种物质 温度升高或降低_ 所吸收或放出的热量数值等于他的比热容 绝热可逆膨胀温度为什么变小 苏州地区哪里有的卖硫酸如题 谢谢了RT. 已知甲乙两物质,当他们的质量相同,甲的温度升高的比乙多,对于他们吸收热量与比热容之间的关系 请问绝热可逆过程和绝热不可逆过程的焓变有什么区别? 相同条件下,相同浓度的硫酸和盐酸相比,硫酸溶液的PH值小 这个说法对的还是错的 有甲乙2个物体,甲的比热容是乙的2倍,质量是乙的二分之五,若吸收热量相同,升高的温度比为几比几 湿空气在喷蒸汽加湿的过程中,含湿量,相对湿度,焓的变化 2013年苏州园林卡办理时间和地点 有甲、乙两个物体,质量之比为5:3,比热容之比为2:1,如果他们放出相同的热量,降低温度之比为? 为什么循环水喷淋空气处理过程是等焓加湿过程 推荐几个夏天天气不太热的城市 明年就读大学了. 想去一个天气好点儿的城市. 1 . 夏天最热月平均气温32度以下. 2 . 冬天最冷月平均气温0度以上. 最好是临海、经济发达 气体体积与压力大小的变化 为什么电热式加湿器会是等温加湿?而水洗加湿等是等焓加湿?等焓加湿说通俗点是什么意思?各位大侠们是怎么理解这个焓值的? 夏天中国哪个城市气温最低最好可以给出那些城市夏季的大约温度 压缩客气灌体积大小与它所冲装气体的压力有关吗 物体温度越高,所含热量越多. 这句话为什么是错的?这句话指的应该是同一个物体吧,也就是说100g99度的水和100g100度的水比,为什么不是100度的水所含热量多? 中国哪个城市气候最好冬天不冷夏天不热 80公斤气体压力有多大?80公斤气体压力是等于8mpa吗?这个压力有大?如果用这个压力推动50克重物能推多远? 什么叫焓差? 温度越高物体含有的热量越多吗? 下列说法正确的是()A高温物体的内能一定大B物体的内能变小,温度一定降低C内能大的物体,温度一定高D物体的内能越大,温度可能不变 要理由 小焓差是什么意思?谢谢 物体的温度越高,它所含的热量就越多.这句话是对的还是错的 人们永远无法改变天气,这种说法对吗?为什么? 焓的物理意义具体是指什么?书上写焓是与内能有关的物理量,焓的具体的物理意义是什么? 物体的温度越高 它所含热量就越多吗?理由 物体吸收了热量 温度一定会升高吗?理由 因为天气是变化无常的,所以人们永远无法改变天气,这种说法对吗?为什么? 焓差的定义是什么 物体的温度越高,它含有的热量就越多. 天气变化是大气层变化的结果,所以人们永远无法改变天气.这种说法对吗?为什么? 焓值差由什么决定? 物理温度与内能的知识点填空 下列事实中 ,属于物体运动状态改变的有:A 用力拉弹簧 ,弹簧变长B跳伞运动员在空中匀速直线下降C正在进站的火车D运动员透出的标枪在空中运动带解析 、 等焓状态什么意思? 九年级物理知识点 关于物体运动状态的改变,下列说法中正确的是 ( )A.物体运动的速率不变,其运动状态就不变B.物体运动的加速度不变,其运动状态就不变C.物体运动状态的改变包括两种情况:一是由静止到 任何一种物质都有焓吗?比如说,我用热空气干燥一种物质,热空气降温,这种物质升温,水份蒸发,利用焓来计算热量平衡,应该怎样算呢? 求初中物理内能详细知识点 关于运动和力,下面说法正确的是:A、物体运动,必须有力的作用.B、物体的运动状态要发生改变,必须有力的作用.C、没有力的作用,运动物体速度不可能快起来,但可以慢下来.D、物体受到力的
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn