AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > VB编程

VB与Windows API 间的呼叫技巧

51自学网 http://www.wanshiok.com

三、 字串参数

    凡是所有字串参数指标都以 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个位置,如此一来,不就和LPSTR 可以相容了吗?我想也正因为如此以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的Handle值,紧接著便是用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来运作。

 

 
 

上一篇:ODBC API 的VB Sample  下一篇:API实现完美的图片出现效果