《VB6指针技术大揭秘精品资料.doc》由会员分享,可在线阅读,更多相关《VB6指针技术大揭秘精品资料.doc(72页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、目录真是想不到系列之一(VB到底为我们做了什么)(02)真是想不到系列之二(VB葵花宝典指针技术)(11)真是想不到系列之三(VB指针葵花宝典之函数指针)(22)真是想不到系列之四(VB指针葵花宝典之SafeArray)(27)真是想不到系列之五(高效字串指针类)(37)真是想不到系列之六(有用的技术和没用的指针)(41)Matthew Curland的VB函数指针调用收藏(45) 每次看大师的东西到了精彩之处,我就会拍案叫绝:哇噻,真是想不到!。在经过很多次这种感慨之后,我发现只要我们动了脑筋,我们自己也能有让别人想不到的东西。于是想到要把这些想不到的东拿出来和大家一起分享,希望抛砖引玉,能
2、引出更多让人想不到的东西。 真是想不到系列之一:VB到底为我们做了什么?关键字:VB、底层、WIN32、API、COM难度:中级要求:熟悉VB,会用VC调试器,了解WIN32 SDK、COM。 VB一直以来被认为有以下优缺点:优点是上手快、开发效率高;缺点是能力有限,运行效率低。这正是有些软件把VB做为首选语言,而有些软件肯定不会用VB做的原因。而很多VC,DELPHI的程序员都认为VB里搞开发不自由,它让我们做事变容易的同时,也让我们发挥的余地越来越小。的确,简单和功能强大这两者本身就是一对矛盾。那怕一行代码不写,仅仅起动运行一个空窗体这样简单动作,VB在底下就为我们做了大量复杂的工作(决不
3、仅仅是注册窗口类、显示窗口、起动消息循环这么简单),这些工作对程序员是透明的。我们在感谢VB开发小组对我们程序员体贴入微的同时,不禁也要责怪为什么在文档中对这些底层的动作只字未提,虽然这些动作对最终的程序也许并无影响,但我们拥有知情权,更何况这些动作有时的确会影响我们的工作(我将在本系列后面的VB多线程中谈到这种影响)。 然而,所有希望从本文得到未公开技术秘密的朋友你将会很失望,因为我能够知道的和你一样多,我们所能做的一切就是站在外面来猜VB在里面做了什么?所以我决不是要带大家一起去将VB反向工程,而是想通过猜想VB的内部工作来将一些原来比较模糊的概念搞清楚。作为一个系列的第一篇文章,它的目的
4、是为了后面的深入打下基础,所以我会在需要的时候指出我们必须掌握的知识点,如果你不清楚,请及时地学习相关书籍来补课,具体见参考书目。 最后,要声明我在本文中所做的各种实验和推断仅是我个人的观点,不能保证其正确性,并且不承担任何相关的法律责任。 好,开始吧!首先准备好我们的武器,我下面要使用的工具主要有:VB6中文企业版+SP5(废话),还有SPY+、Dependency Walk和OLE Viewer(以下简称SPY和DEPEND和OLEVIEW,SPY在VB光盘的commontoolsvb下的SPY目录中,OLEVIEW是其下OLETOOLS目录中的OLEVIEW.EXE,注意其下还有一个OL
5、E2VW32.EXE功能类似,不过本文所指的是OLEVIEW.EXE,还Denpend在其下的UnsupprtDEPEND里)。还要用用VC(上面提的工具在VC里有),因为我们还要看看VB生成的代码,搞VB高级开发的朋友一定要会用VC调试器,懂点汇编更好。当然,本文的重点不在这儿,所以没有VC也不要紧。 打开VB6新建一标准EXE工程,在工程-引用对话框里应该已有四个引用,简单点就是:1、Visual Basic For Application(VBA) 2、VB运行时对象库 3、VB对象库 4、OLE自动化。前面三个是任何VB工程都必须的,你想不要都不行,不信你试着去掉对它们的引用。那么这三
6、个核心类型库各有什么用,在最终生成的可执行程序中扮演怎样的角色,这是本文要分析的第一个问题。 1)VB、VBA、VBS的区别你搞清楚了吗? 首先VBS不应该和VB、VBA放在一起比较,它是微软按照自己定义的ActiveX Scripting规范完全从头开始写成的脚本语言,虽然它的语法结构和VB非常相似,但VBS仅仅依靠自动化对象来扩充其功能(只有后期绑定),它不能用implements来实现接口,不可能在VBS里直接使用API,没有VarPtr这样能得到指针的函数,而VBS缺少的这些功能正是VB和VBA所特有的。当然,这不是说VBS不如VB或VBA,Windows已经为VBS提供了足够强大的功
7、能,我们可以用VBS来做脚本COM组件,而且借自动化对象的能力VBS可以说能力无限,所以有病毒用VBS来写,对程序员来说VBS最重要的功能莫过于可以给自己的软件提供宏功能,就象VC中提供的VBS宏功能那样。注意,VBS是Free的,这和在Office中使用VBA来提供宏功能不同,要集成VBA需要价格不低的许可证费用,关于脚本语言可参见MSDN中Platform SDKTools and LanguagesScripting。(在本系列后面的文章脚本功能中我会实做一个用VBS来提供宏功能的小软件) 那么VB和VBA又有什么不同呢?好吧,眼见为实,开始我们的实验吧! 如果装了Office 2000
8、以上版本,那么打开OLEVIEW,点击File下的View TypeLib查看位于E:Program FilesCommon FilesMicrosoft SharedVBAVBA6下的VBE6.dll的类型库,再用同样的方法看看MSVBVM60.dll的类型库,你会发现它们的类型库基本上一模一样,除了VBE6多了一个VBEGlobal接口和实现这个接口的Global对象,这个Global对象我们也可以在VBA编程环境(比如用WORD的VB编辑器)中用对象浏览器看到。它有二个方法Load和UnLoad,还有一个UserForms属性,这是因为VBA6使用MS Form 2.0 Form设计器(
9、FM20.dll)来设计和使用UserForm窗体(而在VB6中,我们可以使用多个设计器。比如通过使用MS Form 2.0 Form设计器,我们就能在VB中使用VBA所使用的UserForm用户窗体)。和VBA的Global对象类似,在VB中也有GLobal对象,从VB的对象浏览器中可以知道它在vb6.olb这个类型库中,这个类型库就是每个工程都必须引用的VB对象库,所有的VB内置对象都在这里。而VBA的UserForm中使用的对象都在FM20.dll中。 除了上述不同外,VB和VBA还有一个最大的不同,就是VBA不能生成EXE可执行文件,但可以猜想在IDE环境中VBA和VB都要把代码编译成
10、p-code来执行,后面我将用实验来证明的确是这样,虽然在具体的实现上VB和VBA有很大的不同。 从上面的分析上可以看到VB和VBA还是有很大不同的,这种不同主要体现在编程环境和对象结构上,但在本质上它们之间却有着不可割舍的血源关系。如果刚才你仔细地观察了MSVBVM60.dll的类型库,你就会发现如下的片断: / Generated .IDL file (by the OLE/COM Object Viewer) dllname(VBA6.DLL), uuid(35BFBDA0-2BCC-1069-82D5-00DD010EDFAA), helpcontext(0x000f6ec4) mod
11、ule Strings entry(0x60000000), helpcontext(0x000f665f) short _stdcall Asc(in BSTR String); entry(0x60000001), helpcontext(0x000f6e9f) BSTR _stdcall _B_str_Chr(in long CharCode); 什么?在MSVBVM60.dll中的对象其方法却定义在VBA6.DLL中?!VB安装目录下不就有个VBA6.DLL吗?再用OLEVIEW看看它,哇噻,真是想不到它居然和MSVBVM60.DLL的一模一样。怎么回事?赶快再拿出DEPEND来看看V
12、BA6.dll、MSVBVM60.dll和VBE6.dll这三个DLL的输出函数。哈,又有新发现,我们可以发现在三个DLL的输出函数中从编号512到717绝大部分都是一模一样的一些以rtc开头的函数,比如595的rtcMsgBox(rtc是什么?应该是Run Time Component? Control? Code?有谁知道吗?),这说明三个DLL都有着相同的运行时VBA函数。 我们再用DEPEND来观察一下VB6.EXE, 我们可以发现VB6.EXE引入了VBA6.DLL中一些它特有的以Eb和Tip开头的函数,从这些函数的名称上可以发现它们的功能都是IDE相关的,比如79的EbShowCo
13、de和82的TipDeleteModule。VB6.EXE恰恰没有引入任何rtc开头的函数(注意一)。我们再来看看MSVBVM60.DLL,随便找一个用了MsgBox函数的编译后的文件,用DEPEND来观察它,就会发现它引入MSVBVM60.DLL输出的595号rtcMsgBox函数(注意二)。并且引入MSVBVM60.DLL中很多以下划线开头的函数,比如_vbaVarAbs(注意三)。其实从这个三个注意中我们已经可以进行一些猜想,无论对错,你可以先想想。 如果你没有跟着我做实验,而仅仅是看这篇文章的话,我猜想你应该有点昏了。如果你自己动手做了这些实验,现在你应该充满了疑问而急侍看到结论。所以
14、请一定要亲手试一试,学习研究问题的方法比看结论更重要。 到这里至少我们可以得出结论:VB和VBA本就是同宗的姐妹,只不过姐姐VB的功夫要比妹妹VBA历害些。不过姐姐只会单打独斗是女强人;妹妹却只会傍大款。姐姐有生育能力,是真正的女人;妹妹却不会生崽,但深谱相夫之道,一番教导指挥之下可使她老公增色不少,而VBS呢,也是大户人家的女儿,不过没有VB和VBA姐妹优秀的血统,娇小玲珑干不得粗活只能指挥些自动听话的对象来干活,她乐于助人品德好不象VBA那样只认大款,VB、VBA、vbs三个女人我都喜欢。 2)Native Code(本地代码)到底做了什么? 打起精神,我们再深入一步。用OLEVIEW得到
15、的类型库还不能正确的反映各对象方法对应的DLL中的函数入口,你应该已经发现用OLEVIEW得到的IDL文件中各个方法的entry属性值都是0x600000XX这样的假东西。要得到类型库中各方法在DLL中的真正入口,我们需要自己来写段程序。 即使在VB中我们也可以非常容易地获取类型库信息,再加上点COM初始化和调用代码,我们就能用自己的代码实现VB6才引入的CallByName函数(在本系列后面的Hack COM中我会更深入谈谈COM,作为一名VB程序员对COM的理解非常重要)。由于本文的关键不是指导如何在VB里使用类型库,所以下面提供的方法尽量从简。 新建一个标准EXE工程,添加对TypeLi
16、b Infomation的引用,在Form中放一个名为lblInfo的标签,然后添加如下代码:程序1Private 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.N
17、amedItem(Math).Members With oMemInfo .GetDllEntry sDllName, vbNullString, sOrdinal lblInfo = lblInfo & .Name _ & 定义在 & sDllName & 中, _ & 其编号为 & sOrdinal _ & vbCrLf End With NextEnd Sub 运行以后我们就可以知道MATH模块中的Abs方法定义在VBA6.DLL中,其编号为656。在DEPEND中查看VBA6.DLL中编号为656的函数,果然就是rtcAbsVar,用VBE6.DLL试试结果相同。 还记得前面的注意一吧
18、,VB6.EXE没有引入rtc开头的函数这说明在IDE环境中执行的VBA方法实际上是通过COM调用VBA对象库中的方法(跟踪p-code是噩梦,所以我无法验证它用的是什么绑定方式)。而注意二中提到的最终可执行程序中引入了rtcMsgBox,如我们所料最终的程序会直接调用它,这要比COM调用快一点,但在跟踪最终程序时,我发现rtcMsgBox内部却是经过了二万五千里长征后才会去调用MessageBoxA这个API,其间有多次对其它对象的COM调用,慢!可能是因为显示的是模态对话框,在多进程多线程环境有很多需要考虑的因素吧,如果你是疯狂在意效率的程序员,你应该试试用API来重写MsgBox,绝对快
19、不少。再来看看注意三,让我们把以下的程序编译成使用本地代码的程序2.EXE(为了后面的实验,可以在工程属性的编译选项卡中将它设成无优化和生成符号化调试信息程序2.EXE):程序2Private 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并
20、没有如我们预期的一样在引入595的rtcMsgBox的同时引入656的rtcAbsVar,相反它引入了_vbaVarAbs和_vbaI4Abs,看看函数名就知道一个针对的是Variant,一个针对的是long。这说明VB在最终生成的代码中对象Abs这样的可以进一步针对不同类型优化的VBA函数进行了相应的处理,观察一下所有以_vba开头的函数绝大部分都是那些最基本最常用的VBA函数,可以说_vba开头的VBA函数是rtc开头的VBA函数的优化版本,它们基本上是VB开发小组重新写的,绝大多数在函数内部实现自身功能,而rtc开头的函数大多数是调用COM服务对象来完成工作。从这么多_vba开头的函数上
21、可以看出VB小组在Native Code(本地代码)的优化上下了不少功夫,这决对不是吹牛。它的确高度优化了不少科学计算相关的函数,以ABS为例Native Code要比p-code快4倍以上。但是并不是所有的计算函数都经过了这样的优化,比如Rnd函数,它就没有对应的_vba开头的优化函数,而是直接对应到rtcRandomNext函数上,虽然rtcRandomNext也已经优化过,但内部依然用了COM调用,还是不如自己重写的快,我不明白为什么VB开发小组没有考虑为它写一个对应的_vbaRnd。 不要以为上面的分析没有意义,因为我们可以从现象看本质,也可以从本质来解释现象。比如我们再做一个实验,给
22、你的代码加入一个类模块,你可以试试声明一个和内部方法同名的公有的方法(这是一个很有用的技术,在本系列后面的错误处理中我们会用到这种方法),比如我们可以声明一个Public Function Rnd(x) as single,同样我们可以自己写一个同名的MsgBox。但是你试试能不能声明一个Public Function abs(x) ,这时VB肯定会弹出一个莫名其妙的编译错误提示框告诉你缺少标识符,这种错误发生在你的函数名和VB关键字冲突的时候。但是为什么同样是MATH模块中的函数,abs是关键字,rnd却不是,VB文档里是不会告诉你为什么的,但如果你认真的看了我上面的实验分析,我们就能猜想这
23、是因为VB对需要进一步优化的函数已经做了高度优化处理,VB开发小组为了保护他们的劳动成果,并显示他们对自己优化技术的自信,而禁止我们重写这些函数,同时VB开发小组也承认还有些函数有待进一步优化,所以准许我们重写之。在这里我要提出一个伟大的猜想:凡是能够被重写的函数就能够被优化,就象凡是大于2的偶数就能够被分解成两个质因数的和一样。 说到优化,还应该谈谈直接API调用和使用API类型库的差别,还必须谈谈VB所使用的后端优化器(和VC用的是一样的优化器),还想谈谈如何尽最大可能来使用vTable绑定(准备在本系列中另写一篇优化来谈这些问题)。 看了本地代码,我们再来看看p-code,要是你看了MS
24、DN中关于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这
25、个API的缘故,这个API其实就是产生一个Int 3中断,使得我们能够中断程序执行。如果你装了VC这样的支持即时调试的调试器,你可以在错误对话框中点击取消,这样可以起动调试器来调试程序。我就是这样跟踪程序运行的。如果你想看看VB生成的程序反汇编代码可以自己试试,我们可以用同样的技术在VB或VBA的IDE中来中断程序执行,比如我们完全可以在Word的VB编辑器中运行上面程序2的代码,从而中断于Word的进程中,并可观察到VBA生成的p-code代码。比如VB和VBA在IDE中生成的p-code代码就会发现它们这间有很大的不同。 所以,IDE中运行的程序和最终生成的程序是完全不同的。用SPY+看看
26、你在IDE中运行的窗体,你会发现它在VB的主线程下,也就是说在IDE中你用程序做出的窗体和VB IDE工作窗口一样属于VB IDE,你的程序在IDE中运行时申请的资源也属于VB IDE。有些程序在IDE中运行会让IDE死掉(在VB5中写纯API多线程就千万别在IDE中运行,定死无疑,相比之下VB6的IDE健壮得多)。还有些程序可能在IDE中能正常工作,但生成EXE后就工作不了。总之,在写系统程序时要考虑到这种不同可能引起的问题。 3)VB的编译技术,要我怎么夸你,又要我怎么骂你。 看了上面对Native Code的高度评价,你可能会对VB做出的东西更有信心了,腰板更直了。是的,作为VB程序员没
27、有什么需要害羞的,一个功力深厚的VB程序员理应拿比普通VC程序员更多的工资,因为他的生产力是VC程序员的好几倍,而做出的程序在质量上和VC做的相差无几。 甚至有大师开玩笑说VB的内置对象就是用VB写出的,比如我们可以自己写Form.cls、Label.ctl,呵呵,我们还真不能排除这种可能性(虽然用VB不可能直接生成vb6.olb)。如果真是这样,看来VB小组自己都对自己的编译优化技术非常有信心。 实际上我们看看VB安装目录下的C2.exe的属性,再看看VC的C2.DLL的属性,就会发现它们是同一个东西,同样Link.exe也是VC的,所以我们完全可以对VB程序的后端优化编译器以及联结放心了。
28、它们根本就是VC开发小组东西,或者VB、VC都是同一个编译器开发小组在做编译模块。总之,我们可以壮着胆说我们VB做的程序其二次优化和联结用的是和VC一样的技术,嘿嘿,你有的我也有,我有的你没有的(纯属诡辩)。 还有,没有任何编译器比VB编译器更快,因为在IDE中VB就是一种解释型语言,这才是VB开发效率高的关键,快得几乎感觉不得编译过程。其请求时编译,后台编译技术更是一只独秀,厉害啊!想想看,别的语言的程序员有多少时间花在了等待代码编译和重新联结上啊! 不要高兴得太早,因为最终的目的还是要生成可执行文件。在VB中没有分块编译和增量联结的功能,VB在生成可执行程序时总是编译所有模块并完全重新联结
29、,而在别的编译语言中我们可以仅编译最近修改过的文件(分块编译),联结时将新生成的代码附在可执行程序的后面,并将原来的代吗标记为作废(增量联结,最终的可执行程序会越来越大,但联结时间大大缩短)。做实验看看,会发现在VB中每次生成可执行文件所花时间都是相同的。我不知VB开发小组为什么不提供分块编译和增量联结的功能,可能VB开发小组认为生成可执行文件在VB中不是经常要做的工作。但是实际上这种理由是说不过去的,因为如前面所说IDE中运行程序和最终程序有很大不同,如我们要经常编译出可执行文件才能真正对它进行Profile,又如我们要调试多线程程序不能在VB IDE中做,在这些情况下每次修改后都要重新生成
30、可执行文件,我们浪费了不少时间去编译已编译过的代码,联结已联结过的程序。我猜想这是因为VB生成可执行程序时进行了全局优化,所以必须得全部重新编译联结。但提供一个新的功能让我们能够生成不进行全局优化的可以分块编译的调试版本,对Vb开发小组应该不是难事吧!(我有一个变通的解决方案,还在试验中)在来看看VB6安装目录下的VBAEXE6.lib,怎么只有1k大一点,可以猜想里面应该不会有代码,多半是些象vTable这样的函数地址跳转表,或者是些全局常量,我也不知道。但至少说明VB可以用静态联结库了,为什么不把这个功能提供给我们,让我们有更多的选择。 再做个实验看看,做一个标准EXE工程,里面只有一个标
31、准模块,模块里面只一个Sub Main,Sub Main里面什么也没有,将它生成为EXE文件。看看,嚯,有16k多。你要是有时间跟踪这个什么也不做的程序看看,就会知道它要做很多事,初始化Err和App对象,准备COM调用,准备VB、VBA对象库,甚至为使用ActiveX控制也做了准备,嘿嘿,看服务多周到。你必须得用VB对象库中的控制,不用也不行。你再多找几个EXE工程看看,有很多东西相同,都是一个模子做出的,而且你没有选择模子自由。ActiveX工程也是一样,都是Dual双接口,你做的ActiveX控制都必须要躲在一个Extender Object后面。是的,在VB里有很多东西你没有选择的自由
32、。如果需要这种自由要么不用VB,要么就得采取一些未公开的非官方的古怪的技巧(本系列文章最重要的目的之一,就是介绍这样的非官方技巧)。 这又到文章开头说的,VB让我们做事情变得容易的同时也让我们失去了不少自由。在最终代码的生成上则也采取了公式化的做法。当然,我们应该全面地来看待这个问题,如同生产线上生产的东西不一定比手工的精致,群养的家禽不如野味好吃的道理一样,如果需要精致的野味,意味着更多的劳动和更大的成本,这和VB所追求的更容易更便宜的目标是相违背的。 4)VB程序员也得有HACK精神。 本文的最后这个标题是严重离题了,但我想在此为本系列文章定下一个充满HACK精神的基调。HACK精神是什么
33、?没有准确的定义,我的理解是:HACK精神 = 总想探寻未知领域的好奇心 + 凡事总想知道为什么的研究欲 + 总想拿出自己的东西的创新精神 + 解决问题的耐心和恒心。 VB的程序员也一样需要这种精神。 最后,我们都知道VB开发小组已经达上.NET的快车飞起来了,不能不说VB6以后再没有VB的新版本了。微软已经用.NET为我们划出了新的圈子,VB.NET是这个新圈子里的新产物。在圈子里面我们能够飞得更高,但是圈子外面的天空更大,所以我依然乐意站在圈子外,虔诚地祈祷真正的VB7的诞生,阿门。 每次看大师的东西到了精彩之处,我就会拍案叫绝:哇噻,真是想不到!。在经过很多次这种感慨之后,我发现只要我们
34、动了脑筋,我们自己也能有让别人想不到的东西。于是想到要把这些想不到的东拿出来和大家一起分享,希望抛砖引玉,能引出更多让人想不到的东西。 VB真是想不到系列之二:VB葵花宝典-指针技术关键字:VB、指针、动态内存分配、效率、安全难度:中级至高级要求:熟悉VB,掌握基本的C,了解汇编,了解内存分配原理。 想当年东方不败,黑木崖密室一战,仅凭一根绣花针独战四大高手,神出鬼没,堪称天下武林第一高手。若想成为VB里的东方不败,熟习VB葵花宝典,掌握VB指针技术,乃是不二的法门。 欲练神功,引刀,其实掌握VB指针技术,并不需要那么痛苦。因为说穿了,也就那么几招,再勤加练习,终可至神出鬼没之境。废话少说,让
35、我们先从指针的定义说起。 一、指针是什么? 不需要去找什么标准的定义,它就是一个32位整数,在C语言和在VB里都可以用Long类型来表示。在32位Windows平台下它和普通的32位长整型数没有什么不同,只不过它的值是一个内存地址,正是因为这个整数象针一样指向一个内存地址,所以就有了指针的概念。 有统计表明,很大一部分程序缺陷和内存的错误访问有关。正是因为指针直接和内存打交道,所以指针一直以来被看成一个危险的东西。以至于不少语言,如著名的JAVA,都不提供对指针操作的支持,所有的内存访问方面的处理都由编译器来完成。而象C和C+,指针的使用则是基本功,指针给了程序员极大的自由去随心所欲地处理内存
36、访问,很多非常巧妙的东西都要依靠指针技术来完成。 关于一门高级的程序设计语言是不是应该取消指针操作,关于没有指针操作算不算一门语言的优点,我在这里不讨论,因为互联网上关于这方面的没有结果的讨论,已经造成了占用几个GB的资源。无论最终你是不是要下定决心修习指针技术葵花宝典,了解这门功夫总是有益处的。 注意:在VB里,官方是不鼓励使用什么指针的,本文所讲的任何东西你都别指望取得官方的技术支持,一切都要靠我们自己的努力,一切都更刺激! 让我们开始神奇的VB指针探险吧! 二、来看看指针能做什么?有什么用? 先来看两个程序,程序的功能都是交换两个字串:【程序一】:标准的做法SwapStr Sub Swa
37、pStr(sA As String, sB As String) Dim sTmp As String sTmp = sA: sA = sB: sB = sTmp End Sub【程序二】:用指针的做法SwapPtr Private Declare Sub CopyMemory Lib kernel32 Alias RtlMoveMemory _ (Destination As Any, Source As Any, ByVal Length As Long) Sub SwapPtr(sA As String, sB As String) Dim lTmp As Long CopyMemory
38、 lTmp, ByVal VarPtr(sA), 4 CopyMemory ByVal VarPtr(sA), ByVal VarPtr(sB), 4 CopyMemory ByVal VarPtr(sB), lTmp, 4 End Sub 你是不是以为第一个程序要快,因为它看着简单而且不用调用API(调用API需要额外的处理,VB文档明确指出大量调用API将降低程序性能)。但事实上,在VB集成环境中运行,程序二要比程序一快四分之一;而编译成本机代码或p-code,程序二基本上要比程序一快一倍。下面是两个函数在编译成本机代码后,运行不同次数所花时间的比较:运行100000次,SwapStr需要
39、170毫秒,SwapPtr需要90毫秒。运行200000次,SwapStr需要340毫秒,SwapPtr需要170毫秒。运行2000000次,SwapStr需要3300毫秒,SwapPtr需要1500毫秒。 的确,调用API是需要额外指令来处理,但是由于使用了指针技术,它没有进行临时字串的分配和拷贝,因此速度提高了不少。 怎么样,想不到吧!C/C+程序员那么依赖指针,无非也是因为使用指针往往能更直接的去处理问题的根源,更有驾驭一切的快感。他们不是不知道使用指针的危险,他们不是不愿意开卫星定位无级变速的汽车,只是骑摩托更有快感,而有些地方只有摩托才走得过去。 和在C里类似,在VB里我们使用指针也
40、不过三个理由: 一是效率,这是一种态度一种追求,在VB里也一样; 二是不能不用,因为操作系统是C写的,它时刻都在提醒我们它需要指针; 三是突破限制,VB想照料我们的一切,VB给了我们很强的类型检查,VB象我们老妈一样,对我们关心到有时我们会受不了,想偶尔不听妈妈的话吗?你需要指针! 但由于缺少官方的技术支持,在VB里,指针变得很神秘。因此在C里一些基本的技术,在VB里就变得比较困难。本文的目的就是要提供给大家一种简单的方法,来将C处理指针的技术拿到VB里来,并告诉你什么是可行的,什么可行但必须要小心的,什么是可能但不可行的,什么是根本就不可能的。 三、 程咬金的三板斧 是的,程序二基本上就已经
41、让我们看到VB指针技术的模样了。总结一下,在VB里用指针技术我们需要掌握三样东西:CopyMemory,VarPtr/StrPtr/ObjPtr, AdressOf. 三把斧头,程咬金的三板斧,在VB里Hack的工具。1、CopyMemory 关于CopyMemory和Bruce McKinney大师的传奇,MSDN的Knowledge Base中就有文章介绍,你可以搜索ID: Q129947的文章。正是这位大师给32位的VB带来了这个可以移动内存的API,也正是有了这个API,我们才能利用指针完成我们原来想都不敢想的一些工作,感谢Bruce McKinney为我们带来了VB的指针革命。 如C
42、opyMemory的声明,它是定义在Kernel32.dll中的RtlMoveMemory这个API,32位C函数库中的memcpy就是这个API的包装,如MSDN文档中所言,它的功能是将从Source指针所指处开始的长度为Length的内存拷贝到Destination所指的内存处。它不会管我们的程序有没有读写该内存所应有的权限,一但它想读写被系统所保护的内存时,我们就会得到著名的Access Violation Fault(内存越权访问错误),甚至会引起更著名的general protection (GP) fault(通用保护错误) 。所以,在进行本系列文章里的实验时,请注意随时保存你的程
43、序文件,在VB集成环境中将工具-选项中的环境选项卡里的启动程序时设为保存改变,并记住在立即窗口中执行危险代码之前一定要保存我们的工作成果。2、VatPtr/StrPtr/ObjPtr 它们是VB提供给我们的好宝贝,它们是VBA函数库中的隐藏函数。为什么要隐藏?因为VB开发小组,不鼓励我们用指针嘛。 实际上这三个函数在VB运行时库MSVBVM60.DLL(或MSVBVM50.DLL)中是同一个函数VarPtr(可参见我在本系列第一篇文章里介绍的方法)。其库型库定义如下: entry(VarPtr), hidden long _stdcall VarPtr(in void* Ptr); entry
44、(VarPtr), hidden long _stdcall StrPtr(in BSTR Ptr); entry(VarPtr), hidden long _stdcall ObjPtr(in IUnknown* Ptr); 即然它们是VB运行时库中的同一个函数,我们也可以在VB里用API方式重新声明这几个函数,如下:Private Declare Function ObjPtr Lib MSVBVM60 Alias VarPtr _ (var As Object) As LongPrivate Declare Function VarPtr Lib MSVBVM60 _ (var As A
45、ny) As Long(没有StrPtr,是因为VB对字符串处理方式有点不同,这方面的问题太多,在本系列中另用一篇VB字符串全攻略来详谈。 顺便提一下,听说VB.NET里没有这几个函数,但只要还能调用API,我们就可以试试上面的几个声明,这样在VB.NET里我们一样可以进行指针操作。 但是请注意,如果通过API调用来使用VarPtr,整个程序二SwapPtr将比原来使用内置VarPtr函数时慢6倍。) 如果你喜欢刨根问底,那么下面就是VarPtr函数在C和汇编语言里的样子: 在C里样子是这样的: long VarPtr(void* pv) return (long)pv; 所对就的汇编代码就两行: mov eax,dword ptr esp+4 ret 4 弹出栈里参数的值并返回。 之所以让大家了解V