当前位置:首页 > 投稿 > 正文内容

VB/VBA中Variant的坑,却是传参、动态调用API的利器

福瑞号2023-02-04 14:05:10投稿128
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图1

在VB中要有所为,是绕不开Variant滴

前言
在VB/VBA中Variant类型,想必各位都不陌生,因为绝大部分VB/VBA内置函数的参数和返回值,都是这货。很多讨论VB/VBA性能优化的话题,也不会忘记这货。笔者也在《Variant构造智能指针,撒欢了玩》一文中,初提Variant的妙处。要深入VB/VBA,笔者甚至在《VB变量的重构,横看成岭侧成峰》一文中,利用Variant重置VB/VBA的数据类型,是进入VB/VBA指针高阶应用的隐秘之门。
作为数据类型的容器,Variant的结构也在前述文章中给予了介绍。既然作为数据容器,最常用的场景,当然就是传递数据啦。本文,笔者就跟大家分享下Variant在数据传递中中的妙用。
一、患难之中求真情
要想体会Variant的妙,自然离不开不妙的参照物。那首先,让我们看看VB/VBA中有哪些“致命”点吧:
1、数据类型不对,编译不让通过
实话说,笔者现想,还真不好找案例,比如下面这种故意,VB/VBA都能放过,也是无语了。
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图2

看到这,就该知道,确认过眼神了

为了说明数据类型不对,对编译的影响,这里给大家介绍VB/VBA中一个神秘的菜单项。VB6位于“运行”下的“全编译执行”,而VBA位于“调试”下的“编译VBAProject”。如下图所示,说不定很多人,都还没注意过这俩兄弟哦,名字很具有误导性,尤其是VBA的。
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图3

VBA是支持编译的

但是,这哥俩却不是用来生成EXE或DLL的,却可以模拟编译过程,检查编译错误。在这里呢,笔者为了方便说明问题,就用这个功能代替编译检查。于是,又列举了下图所示的例子,然后进行编译检查。
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图4

将String传递给Long类型,会发生什么?

结果很显然,会发生类型不匹配的错误,如下图所示:
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图5

在传参时,类型匹配,是最起码的

2、传数组,动不动就红色告警,尽耽误事儿
数组在任何一种编程语言中,都占据着举足轻重的地位。尤其在业界,普遍认为VB/VBA的性能存在某种问题的情况下,数组就成为很多人提高性能的法宝。相信很多人都曾遇到过,要将数组作为参数进行传递,尽扯拐,比如下面这:
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图6

难道是Optional的锅?

好吧,那删掉吧,默认使用ByRef好了,再来...
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图7

诸位,就没遇见过?就是这么神奇!

什么叫参数必须为变体?那前面的Integer,String等,就叫变体了!什么是缺省的内部类型?数组不是数据类型,不能传递?你是不是默默地将Optional关键字取消了!尽管,有时候这个参数组,一个参数也没有,是不是也会强颜欢笑地,随便传个什么,来表示表示?
3、函数指针,动态调用
关于VB/VBA中的指针问题,前面很多文章里都提到了,读者朋友们可自行关注翻阅以往的文章、头条内容。其实在VB/VBA工程内部,AddressOf关键字就可以获取模块中的函数指针,但是意义不大。因为工程内部,直接可以Call函数名就能使用了,不必大费周章。函数指针,重要的是使用非工程内部的函数,尤其在很多讨巧领域,使用未导出函数。
在VB/VBA中可以使用Declare语句来声明要使用的函数,但是这货不仅效率低,开销大,关键是它是标准调约的呀,而且所用函数必须导出。这意味着什么?
大量优质的C库资源(C调约)没法用了吧,Sqlite数据库不能用了吧,系统很多API不能用了吧,很多讨巧的技能用不上了...少了资源不说,能用的资源又能用好了么?VB/VBA都到指针份上了,谁还会忍受Declare下的各种重复的复制粘贴?VB/VBA号称为自动化服务的,可是连小小的动态调用都搞不定,还谈什么自动化胶水呢?
或许,很多人会觉得函数指针挺简单的,GetModuleHandle+GetProcAddress不就行了。获取函数指针,是很简单。但凡事都有然而,不可能每个函数都重复配套一次吧,所以封装动态调用函数的重要性就出来了。
所以这里的动态调用,其实是指封装1个万能调用函数,对就像Invoke函数那样。相信很多朋友都曾尝试过,笔者也见过很多实现,但都很蹩脚,其中尤其是参数的处理。我们知道,Win32的API成千上万,更不要说第三方库中的API了。这么多函数,参数的个数、类型肯定是不一样的。如何在一个万能调用函数中,处理这些不同呢?
二、患难中见真情
限于篇幅和时间精力,上述列举的只是VB/VBA编程中诸多问题中的冰山一角。若是,未能深入掌握VB/VBA的语言特点,想必很容易开始嫌弃VB/VBA,从而投向其他专业工具。究竟是福是祸,这就不好说了。毕竟人生苦短,能简单何必复杂!
1、传参类型不匹配,Variant可是传参中的Any
遇到『一.1』的问题,在VB/VBA中其实很容易解决,简单地将参数类型声明为Any即可。关于Any类型,在VB/VBA的圈子里,也有很多传说。最信誓旦旦的解释,莫不过将其视为提高效率的某种神秘的危险做法。
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图8

Any,其实不是一种类型

其实,Any不算是一种数据类型,它只是告诉编译器,不检查数据类型而已。被Any修饰的参数,无论传递什么类型过去,编译时都不会报错,实际上却很可能发生内存溢出错误。所以,有人觉得它危险。再有,被Any修饰的参数,不能与ByVal关键字合作,不能传值,而只能传址,所以有人觉得它高效。
笔者在《ByVal与ByRef,究竟谁更高效?》详细地介绍了参数传递过程中,实参与形参的一些特点,未阅读过的读者,可关注阅读。因为编译器不检查数据类型,实参传递给形参时,形参因不知道要分配的内存大小,就无法分配内存。所以,只能传址进行引用,而不能传值。
Any类型,对于VB/VBA使用API而言,只要理解了上述关系,并准确使用,不仅可以提高性能,最核心的是可以极大提高API使用的灵活性。就像上图所示,不需要显式传递字符串指针给lstrlenA函数,仅需要按平常所理解的那样,将字符串变量丢给Any修饰的参数,就可以自动传递字符串指针过去。
不过,Any再好,那也只是Declare的配套,跟自己的函数沾不上边的。不过,还有Variant呀,Variant就是非Declare函数的Any。VB/VBA中的所有数据类型,都可以传递给Variant。
2、Variant传数组,Optional ByVal/ByRef,就不是个事儿
『一.2』中的问题,涉及到数组参数的传递。在VB/VBA中,传递数组变量时,只能ByRef方式,而且不能使用Optional关键字。这无疑对于重要的数组来讲,是不公平的(吐槽点)。其实改用Variant,用这个另类Any,一切就迎刃而解了。
而且,可以ByVal传值,不会影响传入的数组变量。为了形象地说明,笔者写了个Demo测试如下图所示:
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图9

看到没,ByVal传递数组哦

其中VarArrayInfo函数,提取Variant类型数组的数据地址。测试结果表明,数组变量可以ByVal方式传递参数,并不会影响传入的数组。更关键地是,数组可以作为可选参数进行传递了。
3、动态调用函数指针,Variant传参,参数类型和个数不再困扰
『一.3』中的问题,涉及到不定参数的传递问题。在VB/VBA中,可以使用ParamArray关键字修饰参数来实现这一目的。但是参数必须是Variant数组,且不能与Optional关键字组合。
或许有人说,既然Variant可以直接传递数组,还可以与Optional关键字组合,为何不直接使用Variant类型参数呢?这个,怎么说呢,只知其一不知其二啊。
ParamArray修饰的参数,本身可不传递参数啊,这不就是Optional关键字的意思了么。再者,它把数组展开,直接进行传递,多形象直观啊!尤其是,动态调用API,形式上与Declare方式也很像呀,容易理解和检查。
但是,无论是直接使用Variant传递参数数组,还是使用ParamArray关键字传递不定数参数,都必须对Variant有深入地理解。有需要进一步了解Variant机制的朋友,欢迎关注BtOfficer进行咨询哦。
三、Variant不仅是任性的小花,还是惊喜的小花
如果读者朋友们觉得,Variant传递数组,也不过如此的话。那笔者再给出一个测试Demo,看看下图的测试结果:
VB/VBA中Variant的坑,却是传参、动态调用API的利器-图10

Variant虽好,但是坑也挺多的

Variant的确可以传递数组,那当然可以像数组那样来使用数组了,比如上图中的v(0),表示数组中的第一个元素。在VB/VBA中,可以使用VarPtr函数获取非数组变量地址。a(0),不仅表示a数组中首元素地址,更代表首元素变量。所以,VarPtr(a(0))可以获取函数首元素地址。但是,传递给Variant的v(0),却不行了呢?
经验丰富的人员,一看VarPtr(v(0))的返回值,就知道这是栈地址。动态数组数据,都是分配在堆上的。这里面,是有猫腻滴。
其实,这就是Variant另一个让人惊喜的地方,它可以表现得像函数一样。v(0)中,v便是函数名,(0)便是函数列表,返回的元素值,便是函数返回值。还记得笔者以前提过,函数名就是一个临时变量么?如果读者朋友们理解这一点,就很容易理解上面的结果。
怎么样?Variant让你惊喜了吗?Variant传递数组时,可以像数组变量那样获取元素,但却不可以像数组变量那样获取元素地址。
欢迎关注BtOfficer呀(收藏、点赞、关注+转发),更多精彩仍在继续哦(专栏文章更系统,更精彩,但需要支持哦),有严肃的技术,也有轻松的唠嗑,期待你的加入!

扫描二维码推送至手机访问。

版权声明:本文由福瑞号发布,如需转载请注明出处。

本文链接:http://furui.com.cn/60023.html

“VB/VBA中Variant的坑,却是传参、动态调用API的利器” 的相关文章

海尔商标图片(【卓一慧众知识产权】海尔商标图片的演变)

海尔商标图片(【卓一慧众知识产权】海尔商标图片的演变)

海尔商标的演变是海尔从中国走向世界的见证。海尔在不断打破平衡的创新中,又要保持相对的稳定,所以,在”海尔”这两个字中都有一个笔划在整个字体中起平衡作用,”海”字中的一横,”尔”字中的一竖,”横平竖直”,使整个字体在动感中又有平衡,寓意变中有稳,企业无论如何变化是为了稳步发展。相信很多朋友都不太清楚海...

西安二本大学排名(新一线城市西安2020最好大学排名)

西安二本大学排名(新一线城市西安2020最好大学排名)

前言:在历史文章中,已经公布的新一线城市大学排名城市有:杭州,成都,天津,杭州,广州。感兴趣的同学可以关注下【麦麦谈教育】,然后在历史文章中查看最新的新一线城市大学排名。 今天是9月13日了,国内大部分省市的高考录取工作已经接近尾声,而陕西省还剩一部分高职专科录取未结束,截至明天(9月14日)下午2...

白虎穴是什么意思(37.)

白虎穴是什么意思(37.)

民间故事教人向善,学人学好,喜欢听故事的可以点赞支持一下,每天更新好故事,话不多说,我们故事接着讲。山西平遥是辽帝古都,素来民风勤俭,百姓大都住在窑洞里,豪门大户。盼遇事,召集市,曾作竹枝词,三月三天长麦苗,村庄升级至仙桃胡成中。即是关于平阳的真实记载,总兵衙门的三堂后面有五孔窑洞。在房子外面围...

新锅第一次用怎么用什么方法开锅(新锅第一次用怎么处理)

新锅第一次用怎么用什么方法开锅(新锅第一次用怎么处理)

很多人都知道新买的锅不能直接使用,新锅需要进行一些处理才能进行使用,这些都是一些生活小知识,但是对于现在很多年轻人来说却不知道,很多人搞不懂,新锅为什么不能直接使用,这个有什么讲究吗?下面我们就来一起学习一下新锅第一次用怎么处理,新锅开锅方法流程,新锅开锅后多久可以用这些关于新锅开锅的相关知识。 一...

辽宁石油化工大学就业网(辽宁石油化工大学举办2021届毕业生寒假视频空中双选会)

辽宁石油化工大学就业网(辽宁石油化工大学举办2021届毕业生寒假视频空中双选会)

在我国本土疫情呈零星散发和局部聚集性疫情交织叠加态势,防控形势严峻复杂的情况下,为了减少疫情对毕业生就业工作的影响,进一步畅通就业渠道,满足众多用人单位对2021届高校毕业生的招聘需求,助力2021届毕业生实现更加充分和更高质量就业,1月25日—26日“辽宁石油化工大学2021届毕业生寒假视频空中双...

蛇果的养殖方法和注意事项(蛇果的栽种技巧和注意事项)

蛇果的养殖方法和注意事项(蛇果的栽种技巧和注意事项)

蛇果来源于与美国,看名字,给人的第一个感觉就是长得像蛇,但是实际上它和蛇没关联,会叫蛇果也是因为英文翻译成中文就是这样的,它的外观类似于苹果,不知道的人经常把它错认为苹果。它的营养价值很高,效果很厉害,在市场上很讨人喜欢。 一、适时栽种 蛇果通常在春天三月上下种植,一般来说,种植的次年就能成果,到...