首页 >> 读书频道 >> 电脑 >> VB5.0与Windows API 间的呼叫技巧
 
· 如何通过VB获取网卡地址
· 限制鼠标的移动
· 图象转换下
· 文件拖动到文本框后自动打
· 图象转换上
· VB:一组应用技巧
· 编程规范四
· 编程规范三
· 在关机或Logff前信息
· 编程规范二
· 用VB快速读取TextB
· 编程规范一
 
· 电视剧《刁蛮公主》下载和
· 美容养颜手册
· 加油金顺剧情分集介绍完整
· 红楼梦:120回全本[清
· 素质教育在美国
· 全唐诗卷四十六
· 韩剧《布拉格恋人》剧情介
· 夜航船[作者:明·张岱]
· 局外人[作者:韩·可爱淘
· 现场流行病学
· 源氏物语[日本:紫式部]
· 武林寓言故事
 
· (出租)中动商场部分及写
· (出租)中动动漫基地&#
· 喜剧学院
· 《善德女王》剧情介绍
· 魔女18号 剧情
· 丑女无敌剧情介绍
· 魔女幼熙剧情介绍
· 龙游天下剧情介绍
· 震撼世界的七日剧情介绍
· 静静的白桦林剧情介绍
· 心情日记—老公今天我想对
· 旗舰剧情介绍
欢迎来到月影社区!如果您觉得这里不错,请推荐给您的朋友们。月影社区:http://wf66.com/

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


查看有无更新版本

关键字:VB 2006-9-18

 

一般会使用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

Eg.
-----------------------------------------------------------------------------
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。其实,有个更直觉的例子,那就是事件(Event);
比如说Form产生时会有一个Form_Load事件,在这个事件内我们可以写很多程式,但这些
程式不是给我们其他的Procedure呼叫的,而是Form在Load进来时,由Window去呼叫的,
这便是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

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

[ 1 ]
VB5.0与Windows API 间的呼叫技巧 num

打印本页 关闭

关于我们版权声明本站导航友情连结作品演示 TOP↑