打开网易新闻 查看更多图片

前言

在《VB/VBA的虚拟机(二) 》末尾提到COM接口函数能有效降低使用门槛的问题,那具体是如何实现的呢?

首先,COM是面向对象的。面向对象的封装性、多态性等特性,是更接近于人的,有利于映射和处理现实世界各种复杂问题。在VB/VBA中,几乎万物皆COM。只需引用绑定,就可轻松使用其定义的常数、结构和函数。

其次,是COM的自动化能力。C库要想拿给别人使用,除了二进制外,还得提供1份头文件给别人。但COM库,你提供二进制就可以了。因为COM接口本身,就具有自动化能力,可以自我描述。这就是为什么在VB中引入COM库后,就可以使用"."进行智能提示和代码补全。其实跟C头文件具有异曲同工之妙。

最后,COM接口函数,可以进一步封装函数,以属性、方法等多种形态服务于各大应用场景。在《VB/VBA中的函数,用了那么久,其实是这样的 》中也提到了COM库函数在VB中拥有更高的效率。可以避免使用Declare语句带来的额外开销,因为使用COM接口库函数,可以在编译时进行处理,而Declare是在运行时进行处理。

好了,关于导出函数和COM接口函数,暂时就说这么多。接下来继续分析VB/VBA的虚拟机。

三、VB/VBA的虚拟机

(一)VB的虚拟机

2、VB6.EXE用到的库

(3)MSVBVM60.DLL

MSVBVM,翻译过来,难道不是微软VB虚拟机么,它和VBA6.DLL之间究竟是什么关系呢?也来看看MSVBVM60.DLL的结构吧:

熟悉的配方,熟悉的味道

一样有1个Engine段,尺寸也几十K,也像是解释器啊,疑惑ing...再来看看导出函数:

打开网易新闻 查看更多图片

还是熟悉的味道

一样有Eb系列、Tip系列、rtc系列,还有__vba系列函数。这难道不是两套虚拟机机制吗?二者之间有没有亲戚关系呢?MSVBVM60.DLL对VBA6.DLL有依赖么?是否使用了VBA6.DLL呢?我们再看下前者的导入库,只有Kernel32、User32、Advapi32、Ole32和OleAuto32,都是系统库。看不出来有什么依赖,但VB是基于COM的,所以有必要再看看其接口:

难道就是两套,实锤了?

为了更清楚地说明问题,将VB6.DLL的接口放进来对比。惊喜不,竟然一模一样。不仅绿框部分与VBA6.DLL的接口GUID相同。而且红框标识的部分,竟然就来自VBA6.DLL,也就是说MSVBVM60.DLL的确使用了VBA6.DLL的代码。

这就让人有点疑惑了,既然MSVBVM60.DLL未导入VBA6.DLL,那又是如何使用VBA6.DLL中的代码的呢?难道使用LoadLibrary函数动态载入?但是VBA6.DLL位于VB6开发工具的安装目录,未安装的计算机上并没有该动态链接库呀,动态载入就不太可能。

这说明:1、MSVBVM60.DLL的COM接口函数以非动态载入的方式使用了VBA6.DLL中的代码。2、MSVBVM60.DLL的COM接口函数的有效性,需与VBA6.DLL同时存在为前提。

回到《VB/VBA的虚拟机(一) 》的后半部分,VBA6.DLL和MSVBVM60.DLL的确都加载到了VB6.EXE的进程空间,上面的疑惑迎刃而解。但是,为何要实现两处解释引擎?在IDE中究竟用的是哪个解释引擎?MSVBVM60.DLL中“_vba”系列函数又是干什么的?

<未完待续,请关注本号,更多VB/VBA虚拟机内容持续更新中,欢迎大家留言讨论。>