• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    VB5.0與Windows API 間的呼叫技巧

    發布: 2007-7-14 20:28 | 作者: 佚名    | 來源: 網絡轉載     | 查看: 21次 | 進入軟件測試論壇討論

    領測軟件測試網
    一般會使用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,便是一例;蛟S會問,這個例子是把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的方式傳),F在又有一
    個問題,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

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備2023014753號-2
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>