VB真是想不到系列之三:VB指针葵花宝典之函数指针[1]

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 adambear 的 blog

《vb真是想不到系列》
    每次看大师的东西到了精彩之处,我就会拍案叫绝:"哇噻,真是想不到!"。在经过很多次这种感慨之后,我发现只要我们动了脑筋,我们自己也能有让别人想不到的东西。于是想到要把这些想不到的东拿出来和大家一起分享,希望抛砖引玉,能引出更多让人想不到的东西。
本系列文章可见:
    http://www.csdn.net/develop/list_article.asp?author=adambear

               vb真是想不到系列之三:vb指针葵花宝典之函数指针
关键字:vb、hcak、指针、函数指针、效率、数组、对象、排序
难度:中级
要求:熟悉vb,了解基本的排序算法,会用vc更好。

引言:   
    不知大家在修习过本系列第二篇《vb指针葵花宝典》后有什么感想,是不是觉得宝典过于偏重内功心法,而少了厉害的招式。所以,今天本文将少讲道理,多讲招式。不过,还是请大家从名门正派的内功心法开始学起,否则会把九阴真经练成九阴白骨爪。
    今天,我们重点来谈谈函数指针的实际应用。
    接着上一篇文章,关于字串的问题,听csdn上各位网友的建议,我不准备写什么《vb字符串全攻略》了,关于bstr的结构,关于调用api时字串在unicode和ansi之间的转换问题,请参考msdn的partial books里的《win32 api programming with visual basic》里的第六章《strings》。今天就让我们先忘掉字符串,专注于函数指针的处理上来。

    一、函数指针
    addressof得到一个vb内部的函数指针,我们可以将这个函数指针传递给需要回调这个函数的api,它的作用就是让外部的程序可以调用vb内部的函数。
    但是vb里函数指针的应用,远不象c里应用那么广泛,因为vb文档里仅介绍了如何将函数指针传递给api以实现回调,并没指出函数指针诸多神奇的功能,因为vb是不鼓励使用指针的,函数指针也不例外。
    首先让我们对函数指针的使用方式来分个类。
    1、回调。这是最基本也是最重要的功能。比如vb文档里介绍过的子类派生技术,它的核心就是两个api:setwindowlong和callwindowproc。
    我们可以使setwindowlong这个api来将原来的窗口函数指针换成自己的函数指针,并将原来的窗口函数指针保存下来。这样窗口消息就可以发到我们自己的函数里来,并且我们随时可以用callwindowproc来调用前面保存下来的窗口指针,以调用原来的窗口函数。这样,我们可以在不破坏原有窗口功能的前提下处理钩入的消息。
    具体的处理,我们应该很熟悉了,vb文档也讲得很清楚了。这里需要注意的就是callwindowproc这个api,在后面我们将看到它的妙用。
    在这里我们称回调为让"外部调用内部的函数指针"。
    2、程序内部使用。比如在c里我们可以将c函数指针作为参数传递给一个需要函数指针的c函数,如后面还要讲到的c库函数qsort,它的声明如下:
    #define int (__cdecl *compare)(const void *elem1, const void *elem2)
    void qsort(void *base, size_t num, size_t width,
         compare pfncompare);
它需要一个compare类型函数指针,用来比较两个变量大小的,这样排序函数可以调用这个函数指针来比较不同类型的变量,所以qsort可以对不同类型的变量数组进行排序。
    我们姑且称这种应用为"从内部调用内部的函数指针"。
    3、调用外部的函数
    也许你会问,用api不就是调用外部的函数吗?是的,但有时候我们还是需要直接获取外部函数的指针。比如通过loadlibrary动态加载dll,然后再通过getprocaddress得到我们需要的函数入口指针,然后再通过这个函数指针来调用外部的函数,这种动态载入dll的技术可以让我们更灵活的调用外部函数。
    我们称这种方式为"从内部调用外部的函数指针"
    4、不用说,就是我们也可控制"从外部调用外部的函数指针"。不是没有,比如我们可以加载多个dll,将其中一个dll中的函数指针传到另一个dll里的函数内。
    上面所分的"内"和"外"都是相对而言(dll实际上还是在进程内),这样分类有助于以后我们谈问题,请记住我上面的分类,因为以后的文章也会用到这个分类来分析问题。

    函数指针的使用不外乎上面四种方式。但在实际使用中却是灵活多变的。比如在c++里继承和多态,在com里的接口,都是一种叫vtable的函数指针表的巧妙应用。使用函数指针,可以使程序的处理方式更加高效、灵活。
    vb文档里除了介绍过第一方式外,对其它方式都没有介绍,并且还明确指出不支持“basic 到 basic”的函数指针(也就是上面说的第二种方式),实际上,通过一定的hack,上面四种方式均可以实现。今天,我们就来看看如何来实现第二种方式,因为实现它相对来说比较简单,我们先从简单的入手。至于如何在vb内调用外部的函数指针,如何在vb里通过处理vtable接口函数指针跳转表来实现各种函数指针的巧妙应用,由于这将涉及com内部原理,我将另文详述。
    其实vb的文档并没有说错,vb的确不支持“basic 到 basic”的函数指针,但是我们可以绕个弯子来实现,那就是先从"basic到api",然后再用第一种方式"外部调用内部的函数指针"来从"api到basic",这样就达到了第二种方式从"basic 到 basic"的目的,这种技术我们可以称之为"强制回调",只有vb里才会有这种古怪的技术。
    说得有点绕口,但是仔细想想窗口子类派生技术里callwindowproc,我们可以用callwindowproc来强制外部的操作系统调用我们原来的保存的窗口函数指针,同样我们也完全可以用它来强制调用我们内部的函数指针。
    呵呵,前面说过要少讲原理多讲招式,现在我们就来开始学习招式吧!
    考虑我们在vb里来实现和c里一样支持多关键字比较的qsort。完整的源代码见本文配套代码,此处仅给出函数指针应用相关的代码。   
    '当然少不了的copymemory,不用any的版本。
    declare sub copymemory lib "kernel32" alias _
"rtlmovememory" (byval dest as long, byval source as long, _
                 byval numbytes as long)

    '嘿嘿,看下面是如何将callwindowproc的声明做成compare声明的。
    declare function compare lib "user32" alias _
"callwindowproca" (byval pfncompare as long, byval pelem1 as long, _
                   byval pelem2 as long, byval unused1 as long,  _
                   byval unused2 as long) as integer
'注:byval xxxxx as long ,还记得吧!这是标准的指针声明方法。
    
    '声明需要比较的数组元素的结构
    public type temployee
        name as string
        salary as currency
    end type 

    '再来看看我们的比较函数
    '先按薪水比较,再按姓名比较
    function comparesalaryname(elem1 as temployee, _

本文关键:VB、HCAK、指针、函数指针、效率、数组、对象、排序
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top