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