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应用程序和动态库间字符串参数的传递是一个比较复杂的过程,使用时要非常谨慎。同时应尽可能避免传递字符串数组类型的参数,因为这很容易引起下标越界、堆栈溢出等严重错误。
【编辑推荐】