VB的使用

1、今天讲解VB的使用,明天讲解VC与VB的相互调用:c++

 1.指针是什么? 
   不须要去找什么标准的定义,它就是一个32位整数,在C语言和在VB里均可以用Long类型来表示。在32位Windows平台下它和普通的32位长整型数没有什么不一样,只不过它程序员

的值是一个内存地址,正是由于这个整数象针同样指向一个内存地址,因此就有了指针的概念。 编程

    有统计代表,很大一部分程序缺陷和内存的错误访问有关。正是由于指针直接和内存打交道,因此指针一直以来被当作一个危险的东西。以致于很多语言,如著名的JAVA,小程序

都不提供对指针操做的支持,全部的内存访问方面的处理都由编译器来完成。而像C和C++,指针的使用则是基本功,指针给了程序员极大的自由去为所欲为地处理内存访问,数组

不少很是巧妙的东西都要依靠指针技术来完成。 安全

 注意:在VB里,官方是不鼓励使用什么指针的,本文所讲的任何东西你都别期望取得官方的技术支持,一切都要靠咱们本身的努力,一切都更刺激! 网络

 2.来看看指针能作什么?有什么用? 数据结构

先来看两个程序,程序的功能都是交换两个字串: 函数

【程序一】:注释:标准的作法SwapStr 工具

Sub SwapStr(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 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须要170毫秒,SwapPtr须要90毫秒。 
运行200000次,SwapStr须要340毫秒,SwapPtr须要170毫秒。 
运行2000000次,SwapStr须要3300毫秒,SwapPtr须要1500毫秒。 
   的确,调用API是须要额外指令来处理,可是因为使用了指针技术,它没有进行临时字串的分配和拷贝,所以速度提升了很多。 
  怎么样,想不到吧!C/C++程序员那么依赖指针,无非也是由于使用指针每每能更直接的去处理问题的根源,更有驾驭一切的快感。他们不是不知道使用指针的危险,他们不是不肯意开卫星定位无级变速的汽车,只是骑摩托更有快感,而有些地方只有摩托才走得过去。 
    和在C里相似,在VB里咱们使用指针也不过三个理由: 
    一是效率,这是一种态度一种追求,在VB里也同样; 
    二是不能不用,由于操做系统是C写的,它时刻都在提醒咱们它须要指针; 
    三是突破限制,VB想照料咱们的一切,VB给了咱们很强的类型检查,VB像咱们老妈同样,对咱们关心到有时咱们会受不了,想偶尔不听妈妈的话吗?你须要指针! 
    但因为缺乏官方的技术支持,在VB里,指针变得很神秘。所以在C里一些基本的技术,在VB里就变得比较困难。本文的目的就是要提供给你们一种简单的方法,来将C处理指针的技术拿到VB里来,并告诉你什么是可行的,什么可行但必需要当心的,什么是可能但不可行的,什么是根本就不可能的。     
3. 程咬金的三板斧    
    是的,程序二基本上就已经让咱们看到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的指针革命。  
    如CopyMemory的声明,它是定义在Kernel32.dll中的RtlMoveMemory这个API,32位C函数库中的memcpy就是这个API的包装,如MSDN文档中所言,它的功能是将从Source指针所指处开始的长度为Length的内存拷贝到Destination所指的内存处。它不会管咱们的程序有没有读写该内存所应有的权限,一但它想读写被系统所保护的内存时, 咱们就会获得著名的Access Violation Fault(内存越权访问错误),甚至会引发更著名的general protection (GP) fault(通用保护错误) 。因此,在进行本系列文章里的实验时,请注意随时保存你的程序文件,在VB集成环境中将"工具"->"选项"中的"环境"选项卡里的"启动程序时"设为"保存改变",并记住在"当即"窗口中执行危险代码以前必定要保存咱们的工做成果。 
2)、VatPtr/StrPtr/ObjPtr 
    它们是VB提供给咱们的好宝贝,它们是VBA函数库中的隐藏函数。为何要隐藏?由于VB开发小组,不鼓励咱们用指针嘛。 
    实际上这三个函数在VB运行时库MSVBVM60.DLL(或MSVBVM50.DLL)中是同一个函数VarPtr(可参见我在本系列第一篇文章里介绍的方法)。 
其库型库定义以下: 
        [entry("VarPtr"), hidden] 
        long _stdcall VarPtr([in] void* Ptr); 
        [entry("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 Long 
Private Declare Function VarPtr Lib "MSVBVM60"  _ 
  (var As Any) 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; 
    } 
    所对就的汇编代码就两行: 
    moveax,dword ptr [esp+4] 
    ret             注释:弹出栈里参数的值并返回。 
   
之因此让你们了解VarPtr的具体实现,是想告诉你们它的开销并不大,由于它们不过两条指令,即便加上参数赋值、压栈和调用指令,整个获取指针的过程也就六条指令。固然,一样的功能在C语言里,因为语言的直接支持,仅须要一条指令便可。但在VB里,它已经算是最快的函数了,因此咱们彻底不用担忧使用VarPtr会让咱们失去效率!速度是使用指针技术的根本要求。 
    一句话,VarPtr返回的是变量所在处的内存地址,也能够说返回了指向变量内存位置的指针,它是咱们在VB里处理指针最重要的武器之一。 
3)、ByVal和ByRef 
    ByVal传递的参数值,而ByRef传递的参数的地址。在这里,咱们不用去区别传指针/传地址/传引用的不一样,在VB里,它们根本就是一个东西的三种不一样说法,即便VB的文档里也有地方在混用这些术语(但在C++里的确要区分指针和引用) 
    初次接触上面的程序二SwapPtr的朋友,必定要搞清在里面的CopyMemory调用中,在什么地方要加ByVal,什么地方不加(不加ByVal就是使用VB缺省的ByRef) 
    准确的理解传值和传地址(指针)的区别,是在VB里正确使用指针的基础。 
    如今一个最简单的实验来看这个问题,以下面的程序三: 
【程序三】:注释:体会ByVal和ByRef 
    Sub TestCopyMemory() 
        Dim k As Long 
        k = 5 
Note:   CopyMemory ByVal VarPtr(k), 40000, 4 
        Debug.Print k 
    End Sub 
上面标号Note处的语句的目的,是将k赋值为40000,等同于语句k=40000,你能够在"当即"窗口试验一下,会发现k的值的确成了40000。 
    实际上上面这个语句,翻译成白话,就是从保存常数40000的临时变量处拷贝4个字节到变量k所在的内存中。 
    如今咱们来改变一个Note处的语句,若改为下面的语句: 
Note2:   CopyMemory ByVal VarPtr(k), ByVal 40000, 4 
   
这句话的意思就成了,从地址40000拷贝4个字节到变量k所在的内存中。因为地址40000所在的内存咱们无权访问,操做系统会给咱们一个Access Violation内存越权访问错误,告诉咱们"试图读取位置0x00009c40处内存时出错,该内存不能为注释:Read注释:"。 
    咱们再改为以下的语句看看。 
Note3:   CopyMemory VarPtr(k), 40000, 4 
    这句话的意思就成了,从保存常数40000的临时变量处拷贝4个字节到到保存变量k所在内存地址值的临时变量处。这不会出出内存越权访问错误,但k的值并无变。 
    咱们能够把程序改改以更清楚的休现这种区别,以下面的程序四: 
【程序四】:注释:看看咱们的东西被拷贝到哪儿去了  
    Sub TestCopyMemory() 
        Dim i As Long, k As Long 
        k = 5 
        i = VarPtr(k) 
NOTE4:  CopyMemory i, 40000, 4 
        Debug.Print k 
        Debug.Print i 
        i = VarPtr(k) 
NOTE5:  CopyMemory ByVal i, 40000, 4 
        Debug.Print k 
    End Sub 
程序输出: 

40000 
40000 
    因为NOTE4处使用缺省的ByVal,传递的是i的地址(也就是指向i的指针),因此常量40000拷贝到了变量i里,所以i的值成了40000,而k的值却没有变化。可是,在NOTE4前有:i=VarPtr(k),本意是要把i自己作为一个指针来使用。这时,咱们必须如NOTE5那样用ByVal来传递指针i,因为i是指向变量k的指针,因此最后常量40000被拷贝了变量k里。 
    但愿你已经理解了这种区别,在后面问题的讨论中,我还会再谈到它。  
4)、AddressOf 
    它用来获得一个指向VB函数入口地址的指针,不过这个指针只能传递给API使用,以使得API能回调VB函数。 
    本文不许备详细讨论函数指针,关于它的使用请参考VB文档。 
5)、拿来主义。 
    实际上,有了CopyMemory,VarPtr,AddressOf这三把斧头,咱们已经能够将C里基本的指针操做拿过来了。 
    以下面的C程序包括了大部分基本的指针指针操做: 
    struct POINT{ 
        int x; int y; 
    }; 
    int Compare(void* elem1, void* elem2){} 
    void PtrDemo(){ 
    //指针声明: 
        char c = 注释:X注释:;        //声明一个char型变量 
        char* pc; long* pl;  //声明普通指针 
        POINT* pPt;          //声明结构指针 
        void* pv;            //声明无类型指针 
        int (*pfnCastToInt)(void *, void*);//声明函数指针:  
    //指针赋值: 
       pc = &c;              //将变量c的地址值赋给指针pc 
       pfnCompare = Compare; //函数指针赋值。 
    //指针取值: 
       c = *pc;              //将指针pc所指处的内存值赋给变量c 
    //用指针赋值: 
       *pc = 注释:Y注释:             //将注释:Y注释:赋给指针pc所指内存变量里。 
    //指针移动: 
       pc++; pl--;  
    } 
这些对指针操做在VB里都有等同的东西, 
    前面讨论ByVal和ByRef时曾说过传指针和传地址是一回事,实际上当咱们在VB里用缺省的ByRef声明函数参数时,咱们已经就声明了指针。 
    如一个C声明的函数:long Func(char* pc) 
    其对应的VB声明是:Function Func(pc As Byte) As Long 
    这时参数pc使用缺省的ByRef传地址方式来传递,这和C里用指针来传递参数是同样。 
    那么怎么才能象C里那样明确地声明一个指针呢? 
    很简单,如前所说,用一个32位长整数来表达指针就行。在VB里就是用Long型来明确地声明指针,咱们不用区分是普通指针、无类型指针仍是函数指针,统统均可用Long来声明。而给一个指针赋值,就是赋给它用VarPar获得的另外一个变量的地址。具体见程序五。 
【程序五】:同C同样,各类指针。 
    Type POINT 
        X As Integer 
        Y As Integer 
    End Type 
    Public Function Compare(elem1 As Long, elem2 As Long) As Long 
    注释: 
    End Function 
    Function FnPtrToLong(ByVal lngFnPtr As Long) As Long 
        FnPtrToLong = lngFnPtr 
  End Function 

    Sub PtrDemo() 
       Dim l As Long, c As Byte, ca() As Byte, Pt As POINT 
       Dim pl As Long, pc As Long, pv As Long, pPt As Long, pfnCompare As Long 
       c = AscB("X") 
       pl = VarPtr(l)     注释:对应C里的long、int型指针 
       pc = VarPtr(c)     注释:对应char、short型指针 
       pPt = VarPtr(Pt)   注释:结构指针 
       pv = VarPtr(ca(0)) 注释:字节数组指针,可对应任何类型,也就是void* 
       pfnCompare = FnPtrToLong(AddressOf Compare) 注释:函数指针 
       CopyMemory c, ByVal pc, LenB(c)   注释:用指针取值 
       CopyMemory ByVal pc, AscB("Y"), LenB(c) 注释:用指针赋值 
       pc = pc + LenB(c) : pl = pl - LenB(l)   注释:指针移动 
    End Sub 
   咱们看到,因为VB不直接支持指针操做,在VB里用指针取值和用指针赋值都必须用CopyMemory这个API,而调用API的代价是比较高的,这就决定了咱们在VB里使用指针不能象在C里那样自由和频繁,咱们必需要考虑指针操做的代价,在后面的"指针应用"咱们会再变谈这个问题。 
    程序五中关于函数指针的问题请参考VB文档,无类型指针void*会在下面"关于Any的问题"里说。 
    程序五基本上已经包括了咱们能在VB里进行的全部指针操做,仅此而已。 
   
下面有一个小测试题,若是如今你就弄懂了上面程咬金的三板斧,你就应该能作得出来。 
    上面提到过,VB.NET中没有VarPtr,咱们能够用声明API的方式来引入MSVBVM60.DLL中的VarPtr。如今的问题若是不用VB的运行时DLL文件,你能不能本身实现一个ObjPtr。答案在下一节后给出。 
4.指针使用中应注意的问题 
    1)、关于ANY的问题 
    若是以一个老师的身份来讲话,我会说:最好永远也不要用Any!是的,我没说错,是永远!因此我没有把它放在程咬金的三板斧里。固然,这个问题和是否是应该使用指针这个问题同样会引起一场没有结果的讨论,我告诉你的只是一个观点,由于有时咱们会为了效率上的一点点提升或想偷一点点懒而去用Any,但这样作须要要承担风险。 
    Any不是一个真正的类型,它只是告诉VB编译器放弃对参数类型的检查,这样,理论上,咱们能够将任何类型传递给API。 
    Any在什么地方用呢?让咱们来看看,在VB文档里的是怎么说的,如今就请打开MSDN(Visual Studio 6自带的版本),翻到"Visual Basic文档"->"使用Visual Basic"->"部件工具指南"->"访问DLL和Windows API"部分,再看看"将 C 语言声明转换为 Visual Basic 声明"这一节。文档里告诉咱们,只有C的声明为LPVOID和NULL时,咱们才用Any。实际上若是你愿意承担风险,全部的类型你均可以用Any。固然,也能够如我所说,永远不要用Any。     
    为何要这样?那为何VB官方还要提供Any?是信个人,仍是信VB官方的?有什么道理不用Any? 
    如前面所说,VB官方不鼓励咱们使用指针。由于VB所标榜的优势之一,就是没有危险的指针操做,因此的内存访问都是受VB运行时库控制的。在这一点上,JAVA语言也有着一样的标榜。可是,同JAVA同样,VB要避免使用指针而获得更高的安全性,就必需要克服没有指针而带来的问题。VB已经尽最大的努力来使咱们远离指针的同时拥有强类型检查带来的安全性。可是操做系统是C写的,里面处处都须要指针,有些指针是没有类型的,就是C程序员常说的可怕的void*无类型指针。它没有类型,所以它能够表示全部类型。如CopyMemory所对应的是C语言的memcpy,它的声明以下: 
   
void *memcpy( void *dest, const void *src, size_t count ); 
    因memcpy前两个参数用的是void*,所以任何类型的参数均可以传递给他。 
    一个用C的程序员,应该知道在C函数库里这样的void*并很多见,也应该知道它有多危险。不管传递什么类型的变量指针给上面memcpy的void*,C编译器都不会报错或给任何警告。 
    在VB里大多数时候,咱们使用Any就是为了使用void*,和在C里同样,VB也不对Any进行类型检查,咱们也能够传递任何类型给Any,VB编译器也都不会报错或给任何警告。 
    但程序运行时会不会出错,就要看使用它时是否是当心了。正由于在C里不少错误是和void*相关的,因此,C++鼓励咱们使用satic_cast<void*>来明确指出这种不安全的类型的转换,已利于发现错误。 
    说了这么多C/C++,其实我是想告诉全部VB的程序员,在使用Any时,咱们必须和C/C++程序员使用void*同样要高度当心。 
    VB里没有satic_cast这种东西,但咱们能够在传递指针时明确的使用long类型,而且用VarPtr来取得参数的指针,这样至少已经明确地指出咱们在使用危险的指针。如程序二通过这样的处理就成了下面的程序: 
【程序五】:注释:使用更安全的CopyMemory,明确的使用指针! 
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long) 

    Sub SwapStrPtr2(sA As String, sB As String) 
        Dim lTmp As Long 
        Dim pTmp As Long, psA As Long, psB As Long 
        pTmp = VarPtr(lTmp): psA = VarPtr(sA): psB = VarPtr(sB) 
        CopyMemory pTmp, psA, 4 
        CopyMemory psA, psB, 4 
        CopyMemory psB, pTmp, 4 
    End Sub 
    注意,上面CopyMemory的声明,用的是ByVal和long,要求传递的是32位的地址值,当咱们将一个别的类型传递给这个API时,编译器会报错,好比如今咱们用下面的语句: 
【程序六】:注释:有点象【程序四】,但将常量40000换成了值为1的变量. 
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long,  Length As Long) 
    Sub TestCopyMemory() 
        Dim i As Long,k As Long, z As Interger 
        k = 5 : z = 1 
        i = VarPtr(k) 
        注释:下面的语句会引发类型不符的编译错误,这是好事! 
        注释:CopyMemory i, z, 4 
        注释:应该用下面的 
        CopyMemory i, ByVal VarPtr(z), 2 
        Debug.Print k 
    End Sub 
    编译会出错!是好事!这总比运行时不知道错在哪儿好! 
    象程序四那样使用Any类型来声明CopyMemory的参数,VB虽然不会报错,但运行时结果倒是错的。不信,你试试将程序四中的40000改成1,结果i的值不是咱们想要的1,而是327681。为何在程序四中,常量为1时结果会出错,而常量为40000时结果就不错? 
    缘由是VB对函数参数中的常量按Variant的方式处理。是1时,因为1小于Integer型的最大值32767,VB会生成一个存储值1的Integer型的临时变量,也就是说,当咱们想将1用CopyMemroy拷贝到Long型的变量i时,这个常量1是其实是Integer型临时变量!VB里Integer类型只有两个字节,而咱们实际上拷贝了四个字节。知道有多危险了吧!没有出内存保护错误那只是咱们的幸运! 
    若是必定要解释一下为何i最后变成了327681,这是由于咱们将k的低16位的值5也拷贝到了i值的高16位中去了,所以有5*65536+1=327681。详谈这个问题涉及到VB局部变量声明顺序,CopyMemory参数的压栈顺序,long型的低位在前高位在后等问题。若是你对这些问题感兴趣,能够用本系列第一篇文章所提供的方法(DebugBreak这个API和VC调试器)来跟踪一下,能够加深你对VB内部处理方式的认识,因为这和本文讨论的问题无关,因此就不详谈了。到这里,你们应该明白,程序三和程序四实际上有错误!!!我在上面用常量40000而不用1,不是为了在文章中凑字数,而是由于40000这个常量大于32767,会被VB解释成咱们须要的Long型的临时变量,只有这样程序三和程序四才能正常工做。对不起,我这样有意的隐藏错误只是想加深你对Any危害的认识。 
    总之,咱们要认识到,编译时就找到错误是很是重要的,由于你立刻就知道错误的所在。因此咱们应该象程序五和程序六那样明确地用long型的ByVal的指针,而不要用Any的ByRef的指针。 
    但用Any已经如此的流行,以致不少大师们也用它。它惟一的魅力就是不象用Long型指针那样,须要咱们本身调用VarPtr来获得指针,全部处理指针的工做由VB编译器来完成。因此在参数的处理上,只用一条汇编指令:push ,而用VarPtr时,因为须要函数调用,所以要多用五条汇编指令。五条多余的汇编指令有时的确能咱们冒着风险去用Any。 
    VB开发小组提供Any,就是想用ByRef xxx As Any来表达void* xxx。咱们也彻底可使用VarPtr和Long型的指针来处理。我想,VB开发小组也曾犹豫过是公布VarPtr,仍是提供Any,最后他们决定仍是提供Any,而继续隐瞒VarPtr。的确,这是个两难的决定。可是通过我上面的分析,咱们应该知道,这个决定并不符合VB所追求的"更安全"的初衷。由于它可能会隐藏类型不符的错误,调试和找到这种运行时才产生的错误将花贵更多的时间和精力。 
    因此我有了"最好永远不要用Any"这个"惊人"的结论。 
    不用Any的另外一个好处是,简化了咱们将C声明的API转换成VB声明的方式,如今它变成了一句话:除了VB内置的能够进行类型检查的类型外,因此其它的类型咱们都应该声明成Long型。 
   
2)、关于NULL的容易混淆的问题 
    有不少文章讲过,必定要记在内心: 
    VbNullChar 至关于C里的注释:\0注释:,在用字节数组构造C字串时经常使用它来作最后1个元素。 
    vbNullString 这才是真正的NULL,就是0,在VB6中直接用0也能够。 
    只有上面的两个是API调用中会用的。还有Empty、Null是Variant,而Nothing只和类对象有关,通常API调用中都不会用到它们。   
    另:本文第三节曾提出一个小测验题,作出来了吗?如今公布正确答案: 
   【测验题答案】 
    Function ObjPtr(obj as Object) as long 
Dim lpObj As Long 
        CopyMemory lpObj, Obj, 4 
        ObjectPtr = lpObj 
    End Function     
5.VB指针应用 
    如前面所说VB里使用指针不象C里那样灵活,用指针处理数据时都须要用CopyMemory将数据在指针和VB可以处理的变量之间来回拷贝,这须要很大的额外开销。所以不是全部C里的指针操做均可以移值到VB里来,咱们只应在须要的时候才在VB里使用指针。 
    1)、动态内存分配:彻底不可能、可能但不可行,VB标准     
    在C和C++里频繁使用指针的一个重要缘由是须要使用动态内存分配,用Malloc或New来从堆栈里动态分配内存,并获得指向这个内存的指针。在VB里咱们也能够本身 
用API来实现动态分配内存,而且实现象C里的指针链表。 
     但咱们不可能象C那样直接用指针来访问这样动态分配的内存,访问时咱们必须用CopyMemory将数据拷贝到VB的变量内,大量的使用这种技术必然会下降效率,以致于要象C那样用指针来使用动态内存根本就没有可行性。要象C、PASCAL那样实现动态数据结构,在VB里仍是应该老老实实用对象技术来实现。 
    本文配套代码中的LinkedList里有彻底用指针实现的链表,它是使用HeapAlloc从堆栈中动态分配内存,另有一个调用FindFirstUrlCacheEntry这个API来操做IE的Cache的小程序IECache,它使用了VirtualAlloc来动态分配内存。但实际上这都不是必须的,VB已经为咱们提供了标准的动态内存分配的方法,那就是: 
    对象、字符串和字节数组 
    限于篇幅,关于对象的技术这里不讲,LinkedList的源代码里有用对象实现的链表,你能够参考。 
    字符串能够用Space$函数来动态分配,VB的文档里就有详细的说明。 
    关于字节数组,这里要讲讲,它很是有用。咱们可用Redim来动态改变它的大小,并将指向它第一个元素的指针传给须要指针的API,以下: 
   
dim  ab() As Byte , ret As long 
    注释:传递Null值API会返回它所须要的缓冲区的长度。 
    ret = SomeApiNeedsBuffer(vbNullString) 
    注释:动态分配足够大小的内存缓冲区 
    ReDim ab(ret) As Byte 
    注释:再次把指针传给API,此时传字节数组第一个元素的指针。 
    SomeApiNeedsBuffer(ByVal VarPtr(ab(1)))  
    在本文配套程序中的IECache中,我也提供了用字节数组来实现动态分配缓冲区的版本,比用VirtualAlloc来实现更安全更简单。 
    2)、突破限制 
    下面是一个突破VB类型检查来实现特殊功能的经典应用,出自Bruce Mckinney的《HardCore Visual Basic》一书。 
    将一个Long长整数的低16位做为Interger型提取出来, 
【程序七】 注释:标准的方法,也是高效的方法,但不容易理解。
     Function LoWord(ByVal dw As Long) As Integer     
        If dw And &H8000& Then         
            LoWord = dw Or &HFFFF0000     
        Else         
            LoWord = dw And &HFFFF&     
        End If 
     End Function
【程序八】    注释:用指针来作效率虽不高,但思想清楚。 
     Function LoWord(ByVal dw As Long) As Integer     
         CopyMemory ByVal VarPtr(LoWord), ByVal VarPtr(dw), 2 
     End Function 
     3)、对数组进行批量操做 
     
用指针进行大批量数组数据的移动,从效率上考虑是颇有必要的,看下面的两个程序,它们功能都是将数组的前一半数据移到后一半中: 
【程序九】:注释:标准的移动数组的作法 
    Private Sub ShitArray(ab() As MyType) 
        Dim i As Long, n As Long 
        n = CLng(UBound(ab) / 2) 
        For i = 1 To n 
            Value(n + i) = Value(i) 
            Value(i).data = 0 
        Next 
    End Sub 
【程序十】:注释:用指针的作法 
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ 
           (ByVal dest As Long, ByVal source As Long, ByVal bytes As Long) 
    Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" _ 
           (ByVal dest As Long, ByVal numbytes As Long) 
    Private Declare Sub FillMemory Lib "kernel32" Alias "RtlFillMemory" _ 
           (ByVal dest As Long, ByVal Length As Long, ByVal Fill As Byte) 

    Private Sub ShitArrayByPtr(ab() As MyTpye) 
         Dim n As Long 
         n = CLng(UBound(ab) / 2) 
         Dim nLenth As Long 
         nLenth = Len(Value(1)) 
         注释:DebugBreak 
         CopyMemory ByVal VarPtr(Value(1 + n)), _ 
                    ByVal VarPtr(Value(1)), n * nLenth 
         ZeroMemory ByVal VarPtr(Value(1)), n * nLenth 
    End Sub 
    当数组较大,移动操做较多(好比用数组实现HashTable)时程序十比程序九性能上要好得多。 
    程序十中又介绍两个在指针操做中会用到的API: ZeroMemory是用来将内存清零;FillMemory用同一个字节来填充内存。固然,这两个API的功能,也彻底能够用CopyMemory来完成。象在C里同样,做为一个好习惯,在VB里咱们也能够明确的用ZeroMemory来对数组进行初始化,用FillMemory在不当即使用的内存中填入怪值,这有利于调试。 
    4)、最后的一点 
    固然,VB指针的应用决不止这些,还有什么应用就要靠本身去摸索了。对于对象指针和字符串指针的应用我会另写文章来谈,作为本文的结束和下一篇文章《VB字符串全攻略》的开始,我在这里给出交换两个字符串的最快的方法: 
   
【程序十一】注释:交换两个字符串最快的方法
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)  
    Sub SwapStrPtr3(sA As String, sB As String) 
        Dim lTmp As Long 
        Dim pTmp As Long, psA As Long, psB As Long 
        pTmp = StrPtr(sA): psA = VarPtr(sA): psB = VarPtr(sB) 
        CopyMemory ByVal psA, ByVal psB, 4 
        CopyMemory ByVal psB, pTmp, 4 
    End Sub
2、总结

a.什么是Access VBA?
  Access VBA是指VBA开发人员在ACCESS[1]编程中应用VBA,经过设计VBA代码bai来实现所须要的程序功能目标;VBA是应用程序开发语言VASUAL BASIC 的子集。
b、Access VBA与 VB的区别是什么?
  1. VB是设计用于建立标准的应用程序,而VBA是使已有的应用程序(Access、Excel、Word等)自动化;
 2. VB具备本身的开发环境,而VBA必须寄生于已有的应用程序;
 3. 要运行VB开发的应用程序,用户没必要安装VB,由于VB开发出的应用程序是可执行文件(*.EXE),而VBA开发的程序必须依赖于它的"父"应用程序。
c、Access开发软件必须学好VBA
  掌握了VBA,能够规范用户的操做,控制用户的操做行为;
  掌握了VBA,可让操做界面人性化,方便用户的操做;
  掌握了VBA,能够将多个步骤的手工操做经过一步来实现;
  掌握了VBA,能够完成一些没法实现的功能。
  使用ACCESS若是不会用VBA,那么就局限于表、查询、绑定窗体和简单报表的简单应用,更多地体如今自身的使用,在与同事间的数据收集、整理、分析、信息共享方面基本是处于一个初级阶段。
  为了能让自已开发软件更加驾轻就熟、给软件用户更方便与人性化的操做,咱们就须要学好VBA知识。
d、怎样去学习VBA?
  1.去书店购买相关的Access书籍,在购买时注意书籍的章节,要有关VBA代码的页数多一点的;
  2.利用免费的网络资源,例如百度搜索,经过搜索相关的关键词,检索出相关的文章;
  3.访问专业的技术网站、论坛,例如Access软件网,该网站由上海盟威软件有限公司[1]提供,有大量的VBA源代码示例可供学习。
  4.结合工做实践去学习,利用VBA解决工做上的问题,达到提升工做效率目标,从而越学越有兴趣。

                                改变本身,从如今作起-----------久馆          

相关文章
相关标签/搜索