VB与Windows API 间的呼叫技巧.docx
- 文档编号:18535502
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:32
- 大小:34.54KB
VB与Windows API 间的呼叫技巧.docx
《VB与Windows API 间的呼叫技巧.docx》由会员分享,可在线阅读,更多相关《VB与Windows API 间的呼叫技巧.docx(32页珍藏版)》请在冰点文库上搜索。
VB与WindowsAPI间的呼叫技巧
∙VB與WindowsAPI間的呼叫技巧
窗体顶端
一般會使用WINDOWAPI的情況,實在是因為VB本身不提供某些功能,但是,程式所需又不得不然,例如:
讀取Registry內的資料,VB只提供SaveSetting、Getsetting等系列的指令,但是它只能讀取特定地區的值,要讀、刪、更動其他區域的值時,就無法仔細看一看ComboBox的Events,其中沒有MouseMove,但這是我們經常用上的一個Event,那該如何呢?
是的,那只有透過WinodowAPI。
而VB呼叫WindowAPI一般不都使用API檢視員,直接將相對應的APICOPY到我們的程式中就好,那還用什麼技巧嗎?
其實不然,因為VB資料格式的問題,又加上VB本身沒有指標,在許多地方需要一些小技巧才能解決,而且我們經常因應不同的需求,將API檢視員的宣告COPY過來後再做一些修改,最重要的,如果有一個.DLL檔,它不在API檢視員中定義,那時,就只有自己想辦法啦。
一、整數參數
Windows
Int,INT
UNIT,DWORD
BOOL
WPARAM,LPARAM,LRESULT
Handle(如HKEY)
WORD,ATOM,SHORT
BYTE,CHAR
API32位元VB
ByValLong
ByValLong
ByValLongture時為1
ByValLong
ByValLong
ByValLong
ByValInteger
ByValByte
Eg.
-----------------------------------------------------------------------------
WindowsAPI宣告
SHORTGetKeyState(intnVirtKey)
對應的VB宣告
DeclareFunctionGetKeyStateLib"user32"(ByValnVirtKeyAsLong)AsInteger
--------------------------------------------------------------------------------------------------------------
這個API可用來檢視某些KEY(如Insert鍵、NumLock、CapsLock等)是on/off。
程式如下:
這個例子應該可十分楚的看到各個整數間的宣告對應。
----------------------------------------------------------------------------------------------------------------
DimInsertModeasInteger
InsertMode=GetKeyState(vbKeyInsert)AndvbShiftMask
IfInsertMode=1then
Debug.print"表示InsertMode"
Else
Debug.print"表示OverWriteMode"
EndIf
二、指向整數的指標
WindowsAPI
LPINT
LPUNIT
LPBOOL
LPDWORD
LPHANDLE(如:
PHKEY)
LPWORD
LPSHORT
LPBYTE
32位元VB
(ByRef)Long
(ByRef)Long
(ByRef)Long
(ByRef)Long
(ByRef)Long
(ByRef)Integer
(ByRef)Integer
(ByRef)Byte
VB內定是使用傳址呼叫,所以ByRef可以省略,也就是說
Func(ByRefparam1astype)
與
Func(param1astype)
是相同的,使用傳址呼叫的方式,不外乎想將參數傳給API後將結果傳回來。
然而LONG型態的傳址呼叫在VB中又佔了相當大的份量,因為32位元的指標都是LONG的型態,而字串、自定型態的Structure在WindowsAPI中是以指標來傳遞的,而指標的傳遞事實上也是Long值的傳遞,只不過傳過去的LONG值,於WINAPI中會將之當成Address,而再配合指標運作而得指標所指的內容,這個觀念在後面會很重要。
例如:
-----------------------------------------------------------------------------
LONGRegOpenKeyEx(
HKEYhKey,//handleofopenkey
LPCTSTRlpszSubKey,//addressofnameofsubkeytoopen
DWORDdwReserved,//reserved
REGSAMsamDesired,//securityaccessmask
PHKEYphkResult//addressofhandleofopenkey
);
相對應的VB宣告
DeclareFunctionRegOpenKeyExLib"advapi32.dll"Alias"RegOpenKeyExA"_
(ByValhKeyAsLong,_
ByVallpSubKeyAsString,_
ByValulOptionsAsLong,_
ByValsamDesiredAsLong,_
phkResultAsLong)AsLong'//最後一個參數是ByRef之宣告
-----------------------------------------------------------------------------
我們經常會想要用程式來讀取Registry中的資料,例如:
我們想得知Win95的ProductID該如何做呢?
這裡有幾個觀念要先清楚:
首先:
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而KeyHandle的取得便是利用RegQueryKeyEx的API。
程式部份在介紹WinAPI字串傳遞時再一併介紹。
三、字串參數
凡是所有字串參數指標都以ByVal參數名稱AsString傳。
如RegOpenKeyEx()的第二參數ByVallpSubKeyAsString,便是一例。
或許會問,這個例子是把subkey值傳給WinAPI所以用ByVal,沒什麼大不了,其實不然,要WinAPI傳回字串時,也一定要用ByVal的宣告。
這是VB5字串格式(BSTR)與WINAPI標準字串格式(LPSTR)不同的因素。
LPSTR字串格式是NULLTerminate的字串,若有一字串"HaHa!
OK!
",則格式如下:
-----------------------------------------------------------------------------
Address0123456789
--------------------
內容HaHa!
OK!
\0
而BSTR則在字串的前面還有一個LONG值存字串長度,格式如下:
Address0..345678910111213
--------------------------
內容9HaHa!
OK!
\0
----------------------------------------------------------------------------------------------
所以了字串以ByVal的方式來傳像不像指到BSTR中第4個位置,如此一來,不就和LPSTR可以相容了嗎?
我想也正因為如此以ByVal的方式來傳String可以取得WinAPI的傳回值,(就算不是如此,至少這麼想比較記得住String要用ByVal的方式傳)。
現在又有一個問題,Window95API的字串使用的是ASCIICode但VB是用Unicode,Unicode佔兩個位元組,那麼能和WinAPI的字串相?
所幸我們可以先不用管它,因為vb本身做了轉換,即vb傳給api時,轉了一次,傳回時又轉回Unicode,所以如果我們用的是ByteArray來傳字串,也可以但是要自己去轉碼。
。
然而32位元的VB中,字串有種格式,一個是BSTR,另一個是HLSTR,如果我們宣告的串是非固定長度者,就會是BSTR,反之則為HLSTR。
DIMBSTR5ASSTRING?
BR>DIMHLSTR5ASSTRING(255)?
BR>
VB5中WIN32API的呼叫請多多使用BSTR,因為使用HLSTR的結果是,VB還得做HLSTR->BSTR的轉換來呼叫WINAPI若有傳回STRING而後再做BSTR->HLSTR的工作。
然而使用BSTR來工作時,若處理有傳回值的STRING參數,則還要有額外的動作:
1.先給定字串的初值,且字串的長度要夠放傳回值。
2.傳回後,去除傳回值中多餘的字元。
或
例如:
-----------------------------------------------------------------------------
intGetWindowText(
HWNDhWnd,//handleofwindoworcontrolwithtext
LPTSTRlpString,//addressofbufferfortext
intnMaxCount//maximumnumberofcharacterstocopy
);
該API取得WINDOWTitleBar的文字,而傳回值是放入lpString的character個數。
VB的宣告如下:
DeclareFunctionGetWindowTextLib"user32"Alias"GetWindowTextA"_
(ByValhwndAsLong,_
ByVallpStringAsString,_
ByValcchAsLong)AsLong
範例一
*****************************************************************************
DimCharCntAsLong
DimlpStringAsString
DimtmpstrAsString
DimNullPosAsLong
Form1.Caption="這是一個test"
lpString=String(255,0)'設定初值
CharCnt=GetWindowText(Me.hwnd,lpString,256)'CharCnt=12
tmpstr=Left(lpString,CharCnt) '如此做會有一些問題
Debug.PrintLen(tmpstr)'得12
Label1.Caption=Left(lpString,CharCnt)
Debug.PrintLen(Label1.Caption)'得8
*****************************************************************************
以範例一的例子來看,設定lpString=String(255,0)的目的,是設定255個字元的空間給lpString(加上最後的null一共256),CharCnt的值是12,明眼者可看到len("這是一個test")會是8,但CharCnt是12,所以直接使用Left()函數來取得子字串會有問題,這是UniCode與ANSIString間的關係,所以了,當您看到有些書的範例用這種方法取子字串,是不太完善的,所以改用範例二的方式,比較正確。
範例二
*****************************************************************************
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的Handle值,緊接著便是用RegQueryValueEx()來取值。
-----------------------------------------------------------------------------
LONGRegQueryValueEx(
HKEYhKey,//handleofkeytoquery
LPTSTRlpszValueName,//addressofnameofvaluetoquery
LPDWORDlpdwReserved,//reserved
LPDWORDlpdwType,//addressofbufferforvaluetype
LPBYTElpbData,//addressofdatabuffer
LPDWORDlpcbData//addressofdatabuffersize
);
VB的宣告(由API檢視員中Copy下來者)
DeclareFunctionRegQueryValueExLib"advapi32.dll"Alias"RegQueryValueExA"_
(ByValhKeyAsLong,_
ByVallpValueNameAsString,_
ByVallpReservedAsLong,_
lpTypeAsLong,_
lpDataAsAny,_
lpcbDataAsLong)AsLong
-----------------------------------------------------------------------------
仔細看一下第三個參數,WINAPI中是LPDWORD可是VB中麼會是用ByVal的方式傳遞呢?
原因在於lpReserved一定要傳Null進去,VB在呼叫時便在這參數的位置上填0(見範例三)。
為何傳Null就得這做?
我們可以這麼想,我們在程式中下指令,告訴VB要以ByVal的方式傳0出去,而WINAPI裡,它可不管VB是ByVal或ByRef,API認定我們傳進來的就是它需要的,所以了,第三個參數在API中認定我們傳進的是一個Address,而VB傳0進去,那代表API若去取得它的內容,便會取得Address0的內容,或許Window的Null值便是指向Address0呢!
另一個作法比較直接,將VB宣告的第三個參數宣告由ByVallpReservedAsLong改成ByVallpReservedasString而使用時固定傳vbNullString進去也可以。
這裡在一個觀念,那就是VB對WinAPI的宣告,純粹是給VB自己看的,在API中定義了一個指標的參數,Api檢視員會將之宣告成ByRef的方式(字串除外),但我們可隨需要而更動它,一個原始應為ByRef的參數宣告,我們可以將之改為ByVal的方式,只要我們能取得參數的位址,而將這型態為Long的位址以ByVal傳出去,WinAPI端根本不知道VB端是用什麼方式傳,反正只要我們傳了一Long值進去,WinAPI就會以這個Long值當作是Address來運作。
問題還沒有解決,RegQueryValueEx()的第四個參數lpType若為REG_SZ(=1)那代表lpData是NullTerminate的String,若為REG_DWORD(=4)那代表lpData是Long值,正是因為沒有辦法事先知道lpData的真正型態,所以VB就使用ASAny的型態,它要VB放棄型態的檢查,傳什麼值進去都可以,但是在這裡有一些問題,如果lpType是REG_DWORD那麼lpData以ByRef的方式沒有問題,但是如果lpType是REG_SZ,STRING是要以ByVal的方式來宣告,所以會有衝突,而解決的方式就是改寫API檢視員Copy進來的宣告。
-----------------------------------------------------------------------------
DeclareFunctionRegQueryLongLib"advapi32.dll"Alias"RegQueryValueExA"_
(ByValhKeyAsLong,_
ByVallpValueNameAsString,_
ByVallpReservedAsLong,_
lpTypeAsLong,_
lpDataAsLong,_
lpcbDataAsLong)AsLong
DeclareFunctionRegQueryStringLib"advapi32.dll"Alias"RegQueryValueExA"_
(ByValhKeyAsLong,_
ByVallpValueNameAsString,_
ByVallpReservedAsLong,_
lpTypeAsLong,_
ByvallpDataAsString,_
lpcbDataAsLong)AsLong
-----------------------------------------------------------------------------
使用兩個宣告來解決這個問題,依不同的lpType呼叫不同的函式,即lpType=REG_DWORD時,呼叫RegQueryLong,lpType=REG_SZ時則為RegQueryString這也可以讓我了解為何VBAPI的宣告為什麼要有Alias的存在。
範例三
*****************************************************************************
DeclareFunctionRegCloseKeyLib"advapi32.dll"(ByValhKeyAsLong)_
AsLong
DeclareFunctionRegOpenKeyExLib"advapi32.dll"Alias"RegOpenKeyExA"
(ByValhKeyAsLong,ByVallpSubKeyAsString,ByValulOptionsAsLong,_
ByValsamDesiredAsLong,phkResultAsLong)AsLong
DeclareFunctionRegQueryStringLib"advapi32.dll"Alias_
"RegQueryValueExA"(ByValhKeyAsLong,_
ByVallpValueNameAsString,ByVallpReservedAsLong,_
lpTypeAsLong,ByVallpDataAsString,lpcbDataAsLong)AsLong
ConstREG_EXPAND_SZ=2
ConstHKEY_CLASSES_ROOT=&H80000000
ConstREAD_CONTROL=&H20000
ConstSTANDARD_RIGHTS_READ=(READ_CONTROL)
ConstKEY_QUERY_VALUE=&H1
ConstKEY_ENUMERATE_SUB_KEYS=&H8
ConstKEY_NOTIFY=&H10
ConstSYNCHRONIZE=&H100000
ConstKEY_READ=((STANDARD_RIGHTS_READOr_
KEY_QUERY_VALUEOrKEY_ENUMERATE_SUB_KEYSOr_
KEY_NOTIFY)And(NotSYNCHRONIZE))
Dimkey5AsString,ValueNameasString,strBuffasString,ResultStrasString
Dimleng1AsLong,resulAsLong,hkeyAsLong
DimtpAsLong,iAsLong
key5="SOFTWARE\Microsoft\Windows\CurrentVerson"
resul
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VB与Windows API 间的呼叫技巧 VB Windows 呼叫 技巧