VB.NET字符串?dāng)?shù)組全面分析
VB.NET有很多值得學(xué)習(xí)的地方,這里我們主要介紹VB.NET字符串?dāng)?shù)組,包括介紹將VB.NET字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組等方面。
大部分的DLL過程(包括Windows 95 API中的所有過程)使用LPSTR類型字符串,這是指向標(biāo)準(zhǔn)的以null結(jié)束的C語言字符串的指針,它也被稱為ASCIIZ字符串。LPSTR 沒有前綴。下圖顯示了一個指向ASCIIZ字符串的LPSTR。
如果DLL過程需要一個LPSTR(指向以null結(jié)束的字符串的指針)作為參數(shù),可以在 VB 中將一個字符串以傳值的方式傳遞給它。因為指向BSTR的指針實際指向以null值結(jié)束的字符串的第一個數(shù)據(jù)字節(jié),所以對于DLL過程來說,它就是一個 LPSTR。這樣傳入動態(tài)連接庫的字符串,DLL過程也可以對它進(jìn)行修改,盡管它是以傳值方式傳入的。只有當(dāng)DLL過程需要一個指向LPSTR的指針時,才以傳址的方式傳入字符串,這時DLL過程得到的是一個指向字符串指針的指針(相當(dāng)于C/C++中的char**),而不是通常所用的字符串的首地址(相當(dāng)于C/C++中的char*)。
當(dāng)需要把一個VB.NET字符串?dāng)?shù)組整個傳入動態(tài)連接庫時,情況就變得復(fù)雜多了,用傳遞簡單數(shù)據(jù)類型數(shù)組的方式來傳遞VB.NET字符串?dāng)?shù)組是行不通的。當(dāng)我們以傳值的方式將一個VB.NET字符串?dāng)?shù)組的第一個元素傳進(jìn)動態(tài)連接庫時,DLL過程得到的實際上是該元素壓入堆棧段后的地址,而不是數(shù)據(jù)段中整個數(shù)組的首地址。也就是說,這時DLL過程只能得到數(shù)組的第一個元素,而無法訪問整個數(shù)組。而以傳址方式傳入第一個元素時,DLL過程只能得到指向該元素在堆棧段中地址的指針,同樣無法訪問整個數(shù)組。這不能不說是VB的一個不足。因此,在程序設(shè)計中,如果確實需要將整個VB.NET字符串?dāng)?shù)組傳入動態(tài)庫,就必須采取其它方法。
我們知道,在VB中,有一種Byte數(shù)據(jù)類型。每個Byte型變量占一個字節(jié),不含符號位,因 此所能表示的范圍為0到255。這種數(shù)據(jù)類型是專門用于存放二進(jìn)制數(shù)據(jù)的。為了將整個VB.NET字符串?dāng)?shù)組傳進(jìn)動態(tài)庫,可以用字節(jié)數(shù)組來保存字符串。由于Byte是一種簡單數(shù)據(jù)類型,因此字節(jié)數(shù)組的傳遞是非常簡單的。首先,需要把一個字符串正確地轉(zhuǎn)變成一個字節(jié)數(shù)組。這要涉及一 些字符集的知識。Windows 95和VB使用不同的字符集,Windows 95 API使用的是ANSI或DBCS 字符集,而VB使用的則是Unicode字符集。所謂ANSI字符集,是指每個字符都用一個字節(jié)表示,因此最多只能有28=256個不同的字符,這對于英語來說已經(jīng)足夠了,但不能完全支持其它語言。DBCS字符集支持很多不同的東亞語言,如漢語、日語和朝鮮語,它使用數(shù)字0-255表示ASCII 字符,其它大于255或小于0的數(shù)字表明該字符屬于非拉丁字符集;在DBCS中,ASCII字符的長度是一個字節(jié),而漢語、日語和其它東亞字符的長度是2個字節(jié)。而Unicode字符集則完全用兩個字節(jié)表示一個字符,因此最多可以表示216=65536個不同字符。也就是說,ANSI字符集中所有的字符都只占一個字節(jié),DBCS字符集中ASCII字符占一個字節(jié),漢字占兩個字節(jié),Unicode 字符集中每個字符都占兩個字節(jié)。由于VB與WindowsAPI使用的字符集不同,因此在進(jìn)行字符串到字節(jié)數(shù)組的轉(zhuǎn)換時,當(dāng)用Asc函數(shù)取得一個字符的字節(jié)碼后,需要判斷它是否是一個ASCII 字符;如果是ASCII字符,則在轉(zhuǎn)換后的字節(jié)數(shù)組中就只占一個字節(jié),否則要占兩個字節(jié)。
下面給出了轉(zhuǎn)換函數(shù):GetChar Byte得到一個字符的高字節(jié)或低字節(jié),它的第一個參數(shù)是一個字符的ASCII碼,第二個參數(shù)是標(biāo)志取高字節(jié)還是低字節(jié);StrToByte按DBCS或ANSI格式將一個字符串轉(zhuǎn)換成一個字節(jié)數(shù)組,第一個參數(shù)是待轉(zhuǎn)換的字符串,第二個參數(shù)是轉(zhuǎn)換后的一個定長字節(jié)數(shù)組,若該數(shù)組長度不足以存放整個字符串,則截去超長的部分;ChangeStrAryToByte 利用前兩個函數(shù)將VB.NET字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組,第一個參數(shù)是定長的VB.NET字符串?dāng)?shù)組,其中每個元素都是一個字符串(各個元素包含的字符數(shù)可以不同),第二個參數(shù)是一個變長的字節(jié)數(shù)組, 保存轉(zhuǎn)換后的結(jié)果。
- Function GetCharByte(ByVal OneChar As Integer,
ByVal IsHighByte As Boolean) As Byte- ' 該函數(shù)獲得一個字符的高字節(jié)或低字節(jié)
- If IsHighByte Then
- If OneChar >= 0 Then
- GetCharByte = CByte(OneChar \ 256)
- '右移8位,得到高字節(jié)
- Else
- GetCharByte = CByte((OneChar
- And &H7FFF) \ 256) Or &H80
- End If
- Exit Function
- Else
- GetCharByte = CByte(OneChar And &HFF)
- '屏蔽掉高字節(jié),得到低字節(jié)
- Exit Function
- End If
- End Function
- Sub StrToByte(StrToChange As String, ByteArray() As Byte)
- '該函數(shù)將一個字符串轉(zhuǎn)換成字節(jié)數(shù)組
- Dim LowBound, UpBound As Integer
- Dim i, count, length As Integer
- Dim OneChar As Integer
- count = 0
- length = Len(StrToChange)
- LowBound = LBound(ByteArray)
- UpBound = UBound(ByteArray)
- For i = LowBound To UpBound
- ByteArray(i) = 0 '初始化字節(jié)數(shù)組
- Next
- For i = LowBound To UpBound
- countcount = count + 1
- If count <= length Then
- OneChar = Asc(Mid(StrToChange, count, 1))
- If (OneChar > 255) Or (OneChar < 0) Then
- '該字符是非ASCII字符
- ByteArray(i) = GetCharByte(OneChar, True) '得到高字節(jié)
- ii = i + 1
- If i <= UpBound Then ByteArray(i)
- = GetCharByte(OneChar, False)
- '得到低字節(jié)
- Else
- '該字符是ASCII字符
- ByteArray(i) = OneCha
- End If
- Else
- Exit For
- End If
- Next
- End Sub
- Sub ChangeStrAryToByte(StrAry()
- As String, ByteAry() As Byte)
- '將字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組
- Dim LowBound, UpBound As Integer
- Dim i, count, StartPos, MaxLen As Integer
- Dim TmpByte() As Byte
- LowBound = LBound(StrAry)
- UpBound = UBound(StrAry)
- count = 0
- ReDim ByteAry(0)
- For i = LowBound To UpBound
- MaxLen = LenB(StrAry(i))
- ReDim TmpByte(MaxLen + 1)
- ReDim Preserve ByteAry(count + MaxLen + 1)
- Call StrToByte(StrAry(i), TmpByte) '轉(zhuǎn)換一個字符串
- StartPos = count
- Do
- ByteAry(count) = TmpByte(count - StartPos)
- countcount = count + 1
- If ByteAry(count - 1) = 0 Then Exit Do Loop
- '將每一個字符串對應(yīng)的字節(jié)數(shù)組按順序填入結(jié)果數(shù)組中
- ReDim Preserve ByteAry(count - 1)
- Next i
- End Sub
這樣,VB.NET字符串?dāng)?shù)組就全部轉(zhuǎn)換成了字節(jié)數(shù)組,然后只要將字節(jié)數(shù)組的第一個元素以傳址的方式傳入動態(tài)連接庫,DLL過程就可以正確地訪問數(shù)組中的所有字符串了。但是,使用這種方法,當(dāng)DLL過程處理結(jié)束返回VB時,VB得到的仍然是字節(jié)數(shù)組。如果需要在VB中再次得到該字節(jié)數(shù)組表示的字符串,還要把整個字節(jié)數(shù)組重新以0為分割符分成多個子數(shù)組(每個子數(shù)組都對應(yīng)原來字符串?dāng)?shù)組中的一個元素),然后使用VB函數(shù)StrConv將每個子數(shù)組轉(zhuǎn)換成字符串(轉(zhuǎn)換時第二個參數(shù)選vbUnicode),就可以顯示或進(jìn)行其它操作了。例如,其中一個子數(shù)組的名字是SubAry,則函數(shù)StrConv(SubAry,vbUnicode)就返回了它所對應(yīng)的字符串。
總之,VB應(yīng)用程序和動態(tài)庫間字符串參數(shù)的傳遞是一個比較復(fù)雜的過程,使用時要非常謹(jǐn)慎。同時應(yīng)盡可能避免傳遞字符串?dāng)?shù)組類型的參數(shù),因為這很容易引起下標(biāo)越界、堆棧溢出等嚴(yán)重錯誤。
【編輯推薦】