使用go语言制作dll封装Sprintf函数给VBA使用
时间:2022-07-22
本文章向大家介绍使用go语言制作dll封装Sprintf函数给VBA使用,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
VBA的调试输出只有简单的Debug.Print,在接触过C语言之后,很喜欢printf那种形式的打印输出,无奈一直觉得C语言太难了,没能深入去学习。
后来接触了go语言后,觉得这种语言学起来还算简单,也很像C语言。所以在学习一段时间后,就想着能不能用go语言封装dll给VBA使用,前面讲到的那些关于指针、数据类型的东西,主要就是为了做dll:
- 指针Pointer
- Object对象的函数参数传递
- 数据类型String
- 数据类型Array
- 数据类型Variant
在了解了VBA的数据类型后,我们才能把VBA的数据传递到dll中,并正确的解析数据,这里简单介绍一下封装go语言的Sprintf函数。
效果:
1、函数声明
func Sprintf(format string, a ...interface{}) string
这个是go语言的Sprintf函数,可以按c语言的Sprintf函数来理解,第1个参数好处理,关键是后面的可变参数,因为它是允许任意类型和任意数量的:
- 任意类型,VBA里正好有Variant类型
- 任意数量,VBA也正好有ParamArray
所以,VBA里的函数声明为:
Public Sub Printf(format As Variant, ParamArray args() As Variant)
End Sub
format是string,为了方便统一处理,都按Variant类型来传递。
封装Sprintf的目的就是为了能得到一个格式化的字符串,VBA的String和go语言里的string是不一样的,所以dll传出来的string需要转换,API声明:
Public Declare Function gosprintf Lib "godllForVBA32.dll" (ByVal pFormat As Long, ByVal pVBAVariant As Long, ByVal nCount As Long) As MyString
Type MyString
pUCS2 As Long
Len As Long
End Type
为了方便处理,我是在go语言里把String转换为了VBA里的编码,这样在VBA里不需要再次转码了。
2、go实现:
go语言里的函数:
func Sprintf(pformat, pParamArray, nCount int32) (ptr unsafe.Pointer, lenth int)
参数pformat, pParamArray接收VBA传入的指针,nCount直接接收数值,函数返回VBA的String及长度。
实现过程:
- 根据传递进来的VBA的Variant指针,结合数据类型Variant讲到的情况进行分别解析为go语言的数据类型
- 然后调用go语言的Sprintf函数获取需要的String
- 结合cgo,把String传递出去(go是一个有垃圾回收的语言,所以go的对象指针包括String先用C的malloc申请内存ptr,再memcpy过去,让函数返回ptr,最后free释放内存)
- 编写C语言的函数,必须用__stdcall修饰,因为VBA调用API的参数传递方式是__stdcall
- 最后go编译器结合gcc编译器编译dll
3、编译
- 编译.a文件
go.exe build -v -x -buildmode=c-archive -o cgo.a
生成.a和.h 2个文件
- 编写.c文件,因为VBA调用API的参数传递方式是__stdcall
struct Sprintf_return __stdcall gosprintf(GoInt p0, GoInt p1, GoInt p2) {
return Sprintf(p0, p1, p2);
}
- 编写.def文件,目的是让dll导出函数不要带@符号:
EXPORTS
gosprintf
cfree
- 最后编译dll
gcc.exe cstdcall.c cgo.def cgo.a -shared -lwinmm -lWs2_32 -o go.dll -Wl,--enable-stdcall-fixup,--out-implib,go.lib
go语言的具体实现因为和VBA代码相关性不大,就不展开讲。
- PyTorch还是TensorFlow?这有一份新手指南
- Leetcode 300. Longest Increasing Subsequence
- Leetcode 299. Bulls and Cows
- Leetcode 297. Serialize and Deserialize Binary Tree
- Leetcode 295. Find Median from Data Stream
- 投入大见效慢,还要做AI?
- Leetcode 292. Nim Game
- Leetcode 290. Word Pattern
- 【深度学习】使用tensorflow实现VGG19网络
- Leetcode 289. Game of Life
- Leetcode 287. Find the Duplicate Number
- Leetcode 284. Peeking Iterator
- Leetcode 283. Move Zeroes
- Leetcode 282. Expression Add Operators
- 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 数组属性和方法
- Git 常用命令
- Nodejs 一些细节 (持续更新)
- Jenkins 凭据使用
- React源码解读【一】API复习与基础
- choco 安装 和 mkcert 本地https
- js 函数柯里化(Currying)
- GPS数据Python解析及地图可视化
- 文稿:Ant Design从无到有,带你体悟大厂前端开发范式
- 在React中实现和Vue一样舒适的keep-alive
- uniapp获取接口数据,渲染在picker选择器里面
- 我们是这样一步一步实现分布式锁的
- 缓存并发神技,如何通过双 key 来解决缓存并发问题?
- LRU缓存淘汰算法实现方案,这次没人再说你不会开发
- JVM技术总结之三——类加载机制
- 为什么你每次被问到HashMap底层原理都一知半解,搞定它