1、前篇给大家伙探讨了64位Office的VBA编译DLL的问题,从中我们至少可以引出以下几个有用的信息:
A、64位可以编译32位(前篇已介绍),难道32位就不能编译64位吗?这不仅是VBA的问题,更是32与64之间鸡和蛋的问题。否则,64位的东西从何而来?64位若是需要64位的编译器,那么64位编译器又是从何而来?
B、大家或许应该知道,2001年Intel推出的Itanium(IA-64 )就是一个纯种的64位处理器,基于这货的一切都是全新构造的,系统和编译器自然也是。但是它与X86指令完全不同,自然无法兼容X86的32位应用,所以也没能在大众市场取得成功,到现在几近消失。
2003年AMD推出兼容X86的AMD64,迅速获得的市场的拥抱。后来Intel基于相互授权,将AMD64抄了遍,才得以保住颜面。所以通常所说64位PC,可以简单地理解为X86-64。
这就意味着,系统、编译器、应用等各层面上,无需重大修改就可以轻松升级。
C、熟悉Win上PE结构的朋友,就不难得出32与64之间的兼容性。简单地说,把EXE/DLL等文件视为代码存储的外壳容器,那么这个容器的结构是兼容32和64的。要知道,这套结构是在DOS时代成型的。表面上看,是微软工程师的天才设计,但实际上是对X86吃得很透而已。所以笔者总说,微软的兼容只不过是捡了X86的便宜,其他使用X86或类似架构的系统,都能营造出良好的兼容性。
事实上,也有很多极客,直接使用指令来手动生成各种高效的EXE/DLL,不仅可以兼容32/64,还可以避免编译链接器的某些缺陷。各种高级开发工具也都有现成的选项,以满足32/64位程序的编译。
D、如果再深入一点,也很容易发现一些蛛丝马迹。比如线程的TEB,其中就有一个叫WOW32Reserved的成员,有没有眼前一亮呢?对滴,是当年用来解决16/32位兼容的(叫WOW32),现在沿袭下来用于解决32/64兼容的(叫WOW64)。
其实套路,早在VB4.0时代就准备好了。所以,VBA编译64位DLL,不是什么有无的问题。
2、虽然VBA已完美融入64位,但其实大家更想要能在64位Office上抖机灵的VB6。有人说,为何要死抱着VB6不放呢?其实并非抱残守缺,专业人士完全可以借助这些研究项目,了解更深层面的知识。因为其他工具,都给你封好了,绝大部分使用者,也不见得就很了解背后的机制。
当然,对于广大业余爱好者而言,要是能有现成货,无需继续加码学习,便能让旧知识持续有效,简直不要太好!毕竟,吃饭领域的活计够多了,到了一定年龄,上有老下有小,哪有那么多时间精力来学习专业编程。拿来能用,帮助更好恰饭,才是王道!
更何况,不用死抱,VB6所代表的32位,兼容到64位的机制,就摆着的呢!抱与不抱,都在那里,哪为何不多看一眼呐?反正都那么熟了!
3、在64位平台上,从来都不应该在专业上有32/64的说法。虽然VB6编译的叫32位PE,但是他们在64位机器上,都是实打实64位的东西。更确切地说,是同一进程兼具32位和64位特征,最终都是64位的。之所以在64平台上流行32和64的说法,更多地是出于简单粗暴的商业布道。
所以,能编译32位DLL的64位OfficeVBA,没啥大改动,就直接与64位一起耍了。但是,大家见过64位VBA能使用32位API么?微软官方也只是提示到,32位DLL不能加载到64位进程,当然32位进程也不能加载64位DLL。于是大家遵循便是了,但更深层次的原因呢?
偷懒,没错就是偷懒。但是,这个懒是很巧妙的。简单地说,就是旧程序使用旧代码,新程序使用新代码,二者开发时互不干扰。分一下,方便代码资源的挂载。但是,挂载机制,是老早以前(vb4.0那个时代)就定好了,这就是进程那一系列机制。所以,Win与Linux相比,Win因为兼容机制的存在,实际上是不鼓励使用开销巨大的多进程来解决类似于多线程任务的,而是单进程多线程的方式。知道为何大家要骂VB/VBA多线程了吧?
4、尽管X86硬件为兼容铺了路,但WOW64才是VB6看得见的救命稻草
我们知道DLL,叫动态链接库,最主要的目的,就是动态地将代码嵌入到进程地址空间,以便使用,其本身并没有执行代码的能力。使用Dll本身也是很简单滴,使用LoadLibrary函数载入就完事了,至于代码是什么,有没有什么问题,这不是它要管的,这也是DLL得以存世的根本理由。
如果在64位进程里载入32位DLL代码,或者在32位进程里载入64位DLL代码,若不统一标准,那麻烦就来了。最简单的就是对齐问题,这个也是VB/VBA里的老大难问题。32位的VB/VBA里,很多东西,你比如句柄、指针等等,都可以不分你我地用Long类型来表示。因为都是4字节对齐的,现在突然改成8字节对齐了,VB/VBA里以前很多扔给Long的东西,就要分家显示各自本来面目了,VB/VBA自然就难以适应了。注入没法搞了,消息也通不了...怎么看,64位里好像都没VB6啥事了!
试想,若在LoadLibrary函数载入DLL时,处理这些代码,载入1个等半天,那还叫动态库吗?所以,挂载不可能在DLL加载机制里实现,只能在EXE加载机制上想办法。事实上,EXE加载时,系统会准备很多配套的东西到进程地址空间,这为32和64的兼容提供了绝佳的机会。所以,进程开销尽管很大,但人们却愿意容忍(用户可能愿意为程序启动等待数秒,甚至数十秒,但是很少能够容忍鼠标有半秒的卡顿)。
老早前就说过,WOW64环境,它虽然也被官方称之为仿真环境,但是和ARM版Win10模拟X86的仿真器相比,则完全是两回事。WOW64是32到64之间的兼容层,是64位系统级的。可以简单地将其理解为1个64位系统钩子,64位系统加载标记为32位PE的过程中,会强行加入该兼容层。
WOW64兼容层提供了32位代码所需用户态资源,所以32位进程的用户态代码,可以足不出户,也能该干嘛干嘛,就像在32位平台上一样。WOW64兼容层还提供了32位用户到64位用户,64位用户到内核层面的通道,完成最终硬件的调用。Windows大部分核心功能的实现,其实是在内核层。平常所用Win32API,只不过是映射到用户层的接口而已(当然也有部分是完全在用户态实现),其中Ntdll.dll就是核心跳板。
WOW64兼容层,就嵌在32位Ntdll.dll的后面,负责32位代码穿透到内核前,将数据类型等统一到64位标准(其实很简单,就是扩容),然后转发给64位的Ntdll.dll进入内核。而WOW64兼容层的那几个库,是名符其实的64位DLL,说好的不能加载呢?
事实上,Ntdll.dll作为进入内核的最后一个跳板,通常在用户态就是终极。所以,通过三链查看32位进程加载的模块里,是看不到WOW64兼容层的。所以很多人将WOW64兼容层视为32位进程的内核层,就是这么来的。其实人家也是64位用户层的实现,而真正的内核只有64位的。哪些动不动就说微软屎山的,真应该进去看看,本着虚心的态度学习学习,对自己也是有益无害的。
VB6/VBA代表的32位进程,在64位平台上究竟有什么缺陷,会丢失性能吗?要说没有影响,肯定太绝对。就性能而言,32位进程使用真实的硬件和相同的内核代码,性能即便差也差不到哪里去。多了兼容层,肯定比原生32位平台的有所降低。但WOW64兼容层同时也利用了64位平台上新增的寄存器,所以微软说WOW64对32位进程的性能几乎没有影响,诚不欺也。
实际上,64位上32位进程地址空间,就是32和64位DLL的共生之地,VB6/VBA的确也可以继续抖机灵。