真是想不到系列之一:VB到底为我们做了什么?[2]

导读:
  打起精神,我们再深入一步。用oleview得到的类型库还不能正确的反映各对象方法对应的dll中的函数入口,你应该已经发现用oleview得到的idl文件中各个方法的entry属性值都是0x600000xx这样的假东西。要得到类型库中各方法在dll中的真正入口,我们需要自己来写段程序。
  即使在vb中我们也可以非常容易地获取类型库信息,再加上点com初始化和调用代码,我们就能用自己的代码实现vb6才引入的callbyname函数(在本系列后面的《hack com》中我会更深入谈谈com,作为一名vb程序员对com的理解非常重要)。由于本文的关键不是指导如何在vb里使用类型库,所以下面提供的方法尽量从简。
  新建一个标准exe工程,添加对typelib infomation的引用,在form中放一个名为lblinfo的标签,然后添加如下代码:
  '程序1
  private sub form_load()
  dim otlinfo as typelibinfo
  dim omeminfo as memberinfo
  dim sdllname as string
  dim sordinal as integer
  
  set otlinfo = tli.typelibinfofromfile("msvbvm60.dll")
  
  lblinfo = "math模块包含以下方法:" &vbcrlf
  
  for each omeminfo in otlinfo.typeinfos.nameditem("math").members
  with omeminfo
  .getdllentry sdllname, vbnullstring, sordinal
  
  lblinfo = lblinfo &.name _
  &"定义在" &sdllname &"中," _
  &"其编号为" &sordinal _
  &vbcrlf
  end with
  
  next
  end sub
  运行以后我们就可以知道math模块中的abs方法定义在vba6.dll中,其编号为656。在depend中查看vba6.dll中编号为656的函数,果然就是rtcabsvar,用vbe6.dll试试结果相同。
  还记得前面的注意一吧,vb6.exe没有引入rtc开头的函数这说明在ide环境中执行的vba方法实际上是通过com调用vba对象库中的方法(跟踪p-code是噩梦,所以我无法验证它用的是什么绑定方式)。而注意二中提到的最终可执行程序中引入了rtcmsgbox,如我们所料最终的程序会直接调用它,这要比com调用快一点,但在跟踪最终程序时,我发现rtcmsgbox内部却是经过了二万五千里长征后才会去调用messageboxa这个api,其间有多次对其它对象的com调用,慢!可能是因为显示的是模态对话框,在多进程多线程环境有很多需要考虑的因素吧,如果你是疯狂在意效率的程序员,你应该试试用api来重写msgbox,绝对快不少。再来看看注意三,让我们把以下的程序编译成使用本地代码的"程序2.exe"(为了后面的实验,可以在工程属性的编译选项卡中将它设成"无优化"和"生成符号化调试信息"程序2.exe""):
  '程序2
  private declare sub debugbreak lib "kernel32" ()
  private sub main()
  dim i as long, j as long
  dim k
  i = &h1234
  debugbreak
  k = 1234
  j = abs(k)
  j = abs(i)
  msgbox "ss"
  j = varptr(i)
  end sub
  用depend观察"程序2.exe",我们可以发现"程序2.exe"并没有如我们预期的一样在引入595的rtcmsgbox的同时引入656的rtcabsvar,相反它引入了__vbavarabs和__vbai4abs,看看函数名就知道一个针对的是variant,一个针对的是long。这说明vb在最终生成的代码中对象abs这样的可以进一步针对不同类型优化的vba函数进行了相应的处理,观察一下所有以__vba开头的函数绝大部分都是那些最基本最常用的vba函数,可以说__vba开头的vba函数是rtc开头的vba函数的优化版本,它们基本上是vb开发小组重新写的,绝大多数在函数内部实现自身功能,而rtc开头的函数大多数是调用com服务对象来完成工作。从这么多__vba开头的函数上可以看出vb小组在native code(本地代码)的优化上下了不少功夫,这决对不是吹牛。它的确高度优化了不少科学计算相关的函数,以abs为例native code要比p-code快4倍以上。但是并不是所有的计算函数都经过了这样的优化,比如rnd函数,它就没有对应的__vba开头的优化函数,而是直接对应到rtcrandomnext函数上,虽然rtcrandomnext也已经优化过,但内部依然用了com调用,还是不如自己重写的快,我不明白为什么vb开发小组没有考虑为它写一个对应的__vbarnd。
  不要以为上面的分析没有意义,因为我们可以从现象看本质,也可以从本质来解释现象。比如我们再做一个实验,给你的代码加入一个类模块,你可以试试声明一个和内部方法同名的公有的方法(这是一个很有用的技术,在本系列后面的《错误处理》中我们会用到这种方法),比如我们可以声明一个public function rnd(x) as single,同样我们可以自己写一个同名的msgbox。但是你试试能不能声明一个public function abs(x) ,这时vb肯定会弹出一个莫名其妙的编译错误提示框告诉你"缺少标识符",这种错误发生在你的函数名和vb关键字冲突的时候。但是为什么同样是math模块中的函数,abs是关键字,rnd却不是,vb文档里是不会告诉你为什么的,但如果你认真的看了我上面的实验分析,我们就能猜想这是因为vb对需要进一步优化的函数已经做了高度优化处理,vb开发小组为了保护他们的劳动成果,并显示他们对自己优化技术的自信,而禁止我们重写这些函数,同时vb开发小组也承认还有些函数有待进一步优化,所以准许我们重写之。在这里我要提出一个伟大的猜想:凡是能够被重写的函数就能够被优化,就象凡是大于2的偶数就能够被分解成两个质因数的和一样。
  说到优化,还应该谈谈直接api调用和使用api类型库的差别,还必须谈谈vb所使用的后端优化器(和vc用的是一样的优化器),还想谈谈如何尽最大可能来使用vtable绑定……(准备在本系列中另写一篇《优化》来谈这些问题)。
  看了本地代码,我们再来看看p-code,要是你看了msdn中关于p-code的原理,你肯定会头大。平心而论p-code真是一个了不起的技术,代码大小平均可以缩小50%。我们把程序2编译成p-code看看,还是用depend来观察,发现它并没有引入__vba开头函数(没有使用优化的vba函数?),却引入了callengine这样的东西(肯定是为了调用p-code伪码解释引擎),而且和native code一样都引入了rtcmsgbox(编译生成的p-code在调用msgbox时应该比在ide环境中运行的p-code快)。
  如果你迫不及待地运行了程序2,你就会发现它将弹出一个应用程序错误对话框,说程序发生异常。别怕,这是因为调用了debugbreak这个api的缘故,这个api其实就是产生一个int 3中断,使得我们能够中断程序执行。如果你装了vc这样的支持即时调试的调试器,你可以在错误对话框中点击"取消",这样可以起动调试器来调试程序。我就是这样跟踪程序运行的。如果你想看看vb生成的程序反汇编代码可以自己试试,我们可以用同样的技术在vb或vba的ide中来中断程序执行,比如我们完全可以在word的vb编辑器中运行上面程序2的代码,从而中断于word的进程中,并可观察到vba生成的p-code代码。比如vb和vba在ide中生成的p-code代码就会发现它们这间有很大的不同。
  所以,ide中运行的程序和最终生成的程序是完全不同的。用spy++看看你在ide中运行的窗体,你会发现它在vb的主线程下,也就是说在ide中你用程序做出的窗体和vb ide工作窗口一样属于vb ide,你的程序在ide中运行时申请的资源也属于vb ide。有些程序在ide中运行会让ide死掉(在vb5中写纯api多线程就千万别在ide中运行,定死无疑,相比之下vb6的ide健壮得多)。还有些程序可能在ide中能正常工作,但生成exe后就工作不了。总之,在写系统程序时要考虑到这种不同可能引起的问题。
  

本文转自
http://www.cn-doc.com/_soft_visual_basic_tech_doc/2005_08_18_00/20050818001733242_2.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值