分享
 
 
 

VB与Windows API 间的呼叫技巧

王朝vb·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

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

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有