前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VBA数据类型String

VBA数据类型String

作者头像
xyj
发布2020-07-28 10:04:12
1.4K0
发布2020-07-28 10:04:12
举报
文章被收录于专栏:VBA 学习

前面说到的指针Pointer,其实主要是说了取地址函数。得到了变量的地址,只能赋值给Long类型,并没有指针的作用,无法根据这个记录了变量地址的东西来操作变量。

但能得到变量地址,就可以去查看变量的内存布局,从而深入了解类型。

VBA里数值类型Integer、Long那些,内存布局比较简单,无非是1个、2个、4个连续的字节。

而String类型是有点不一样的,有了指针Pointer讲到的取指针函数,我们就可以对String深入了解一下。

深入了解数据类型有什么用?

比如你想用C语言写dll给VBA调用,C语言可是没有String类型的,C的函数该如何去用?这就涉及到了数据类型的内存结构,深入了解才能够使用好。

微软官方文档data-type-summary

代码语言:javascript
复制
String (variable-length)  10 bytes + string length  0 to approximately 2 billion
String (fixed-length)  Length of string  1 to approximately 65,400

1、String变长内存结构

10字节是如何分配的?通过网上的一些资料和自己猜测:

  1. 变量本身占用4个字节,用VarPtr可获取地址p
  2. VarPtr那个地址p保存的值,指向了字符的地址,p-4地址处保存的是长度信息,4个字节
  3. 另外2个是p-6处的00 88还是字符结尾的00 00?(看BSTR的介绍应该是结尾的00 00,可是p-6的处的00 88是做什么的?有什么用?待研究……)
代码语言:javascript
复制
Sub TestString()
    Dim str As String
    
    str = "a"
    
    '10 官方定义str长度
    '4  变量占用
    '2  字符长度
    '2  字符后面00 00
    Dim b(10 - 4 + 2 + 2 - 1) As Byte
    CopyMemory VarPtr(b(0)), StrPtr(str) - 6, 10
    printf "b = % x", b
End Sub

输出:
b = 00 88 02 00 00 00 61 00 00 00

我们能看出,其实从StrPtr开始的位置,字符就是UTF-16的byte,所以VBA里面,byte数组和String的转换很简单,直接赋值就可以。

随便定义1个byte数组,是可以赋值给string的,但是要注意如果需要输出,要保证byte数组符合UTF-16的编码,要不然就可能出现一些乱码了。

代码语言:javascript
复制
Sub TestByte2Str()
    Dim b(11) As Byte
    Dim str As String
    
    b(0) = 65
    
    b(4) = 23
    
    b(10) = 67
    
    str = b
    
    Debug.Print str
End Sub
输出:
A   C

2、String定长

这个涉及到了字符串缓冲区,没有弄明白,不过用的也少!

3、与其他语言的dll交互

经常接触到的是windows的API调用,在VBA里先声明1个String,并且赋值一个足够的长度,调用之后再根据返回长度来取出需要的字符串。

这个我理解其实就是让API函数往StrPtr那个地址里写入Byte数组。

4、是否可以构建1个String变长的内存区域

即然知道了VBA String类型的内存结构,我就想如果自己用C语言做一个dll,函数传出StrPtr需要的那个地址,赋值给1个str的VarPtr那个地址,是不是程序退出的时候VBA的垃圾回收能释放那个内存?这样就不需要先去初始一个足够的长度的String了。

C代码

代码语言:javascript
复制
__declspec(dllexport) char* __stdcall RetStrPtr()
{
  
  char* ch = (char*)malloc(10);

  ch[0] = 0x00;
  ch[1] = 0x88;
  ch[2] = 0x02;
  ch[3] = 0x00;
  ch[4] = 0x00;
  ch[5] = 0x00;
  ch[6] = 0x61;
  ch[7] = 0x00;
  ch[8] = 0x00;
  ch[9] = 0x00;
  
  return ch;
}

编译:
cl -c 1.c 1.def
link -DLL -out:cdlltest.dll 1.obj

VBA调用

代码语言:javascript
复制
Public Declare Function RetStrPtr Lib "cdlltest" Alias "_RetStrPtr@0" () As Long

Sub TestCRet()
    Dim hdll As Long
    
    hdll = LoadLibrary(ThisWorkbook.Path & "\cdll\cdlltest.dll")
    printf "hdll = 0x%x", hdll
    
    Dim str As String
    Dim lStrPtr As Long
    lStrPtr = RetStrPtr() + 6
    CopyMemory VarPtr(str), VarPtr(lStrPtr), 4
    printf "str = %s", str
    
    Stop
    FreeLibrary hdll
End Sub

输出:
hdll = 0x69b50000
str = a

程序能成功输出str = a,但是执行End Sub后,Excel直接崩溃了。难道是程序结束后,VBA的垃圾回收机制回收这块内存的时候出了问题?

于是尝试在VBA内部用byte数组构建后赋值到VarPtr,结果一样。

代码语言:javascript
复制
Sub TestString()
    Dim str As String
    Dim b(9) As Byte
    
    b(1) = &H88
    b(2) = &H2
    b(6) = &H61
    
    Dim lStrPtr As Long
    lStrPtr = VarPtr(b(0)) + 6
    
    Printf "强制赋值VarPtr前,StrPtr(str) = 0x%x", StrPtr(str)
    '+6 StrPtr指向的字符开始的位置,不包含前面00 88和长度信息4个
    CopyMemory VarPtr(str), VarPtr(lStrPtr), 4
    Printf "强制赋值VarPtr后,StrPtr(str) = 0x%x", StrPtr(str)
    
    Printf "str = %s", str
    
    Stop
End Sub

输出:
强制赋值VarPtr前,StrPtr(str) = 0x0
强制赋值VarPtr后,StrPtr(str) = 0x1a207876
str = a

Stop之前都正常,但是执行End Sub后,Excel直接崩溃。

这个到底是什么原因?待继续研究……

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 VBA 学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、String变长内存结构
    • 我们能看出,其实从StrPtr开始的位置,字符就是UTF-16的byte,所以VBA里面,byte数组和String的转换很简单,直接赋值就可以。
      • 随便定义1个byte数组,是可以赋值给string的,但是要注意如果需要输出,要保证byte数组符合UTF-16的编码,要不然就可能出现一些乱码了。
        • 2、String定长
          • 4、是否可以构建1个String变长的内存区域
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档