VBA数据类型String
前面说到的指针Pointer,其实主要是说了取地址函数。得到了变量的地址,只能赋值给Long类型,并没有指针的作用,无法根据这个记录了变量地址的东西来操作变量。
但能得到变量地址,就可以去查看变量的内存布局,从而深入了解类型。
VBA里数值类型Integer、Long那些,内存布局比较简单,无非是1个、2个、4个连续的字节。
而String类型是有点不一样的,有了指针Pointer讲到的取指针函数,我们就可以对String深入了解一下。
深入了解数据类型有什么用?
比如你想用C语言写dll给VBA调用,C语言可是没有String类型的,C的函数该如何去用?这就涉及到了数据类型的内存结构,深入了解才能够使用好。
微软官方文档data-type-summary
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字节是如何分配的?通过网上的一些资料和自己猜测:
- 变量本身占用4个字节,用VarPtr可获取地址p
- VarPtr那个地址p保存的值,指向了字符的地址,p-4地址处保存的是长度信息,4个字节
- 另外2个是p-6处的00 88还是字符结尾的00 00?(看BSTR的介绍应该是结尾的00 00,可是p-6的处的00 88是做什么的?有什么用?待研究……)
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的编码,要不然就可能出现一些乱码了。
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代码
__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调用
Public Declare Function RetStrPtr Lib "cdlltest" Alias "_RetStrPtr@0" () As Long
Sub TestCRet()
Dim hdll As Long
hdll = LoadLibrary(ThisWorkbook.Path & "cdllcdlltest.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,结果一样。
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直接崩溃。
这个到底是什么原因?待继续研究……
- 使用JDBC向Kudu表插入中文字符-双引号的秘密
- Yarn的JobHistory目录权限问题导致MapReduce作业异常
- 输入一个数字,然后计算出从1到输入数字的和,要求,如果输入的数字小于1,则重新输入,直到输入正确的数字为止
- Linux基础(day76)
- zabbix设置QQ邮箱告警
- 关于JSON CSRF的一些思考
- linux学习第七十篇:expect脚本同步文件,expect脚本指定host和要同步的文件,构建文件分发系统,批量远程执行命令
- linux学习第六十九篇:分发系统介绍,expect脚本远程登录,expect脚本远程执行命令,expect脚本传递参数
- linux学习第六十八篇:告警系统邮件引擎,运行告警系统
- linux学习第六十七篇:告警系统主脚本,告警系统配置文件,告警系统监控项目
- linux学习第六十六篇:shell中的函数,shell中的数组,告警系统需求分析
- linux学习第六十五篇:for循环,while循环, break跳出循环,continue结束本次循环
- linux学习第六十四篇:Shell脚本中的逻辑判断,文件目录属性判断, if特殊用法,case判断
- linux学习第六十三篇:Shell脚本介绍,Shell脚本结构和执行,date命令用法,Shell脚本中的变量
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- leetcode - 分割数组的最大值
- leetcode - 除数博弈
- leetcode - 最小路径和
- Javascript中的数据类型
- JAVA 应用日志最佳实践
- 用pageadmin cms系统进行网站制作的经验总结
- 14个Linux系统安全小妙招,总有一招用的上
- CVE-2020-14645:Weblogic远程代码执行复现
- Centos7 安装Mysql-寻找共性-万变不离其宗
- MySQL 入门、安装和客户端管理工具
- MySQL 基本使用(上):DDL 和 DML 语句
- MySQL 基本使用(下):DCL 语句和聚合函数
- 玩转 PhpStorm 系列(二):导航篇
- 带你写出符合Promise/A+规范Promise的源码
- 「R」R Docker 教程