您现在的位置:中国下载站学院中心网络编程Visual Basic教程Visual Basic开发技巧 → 文章列表

VisualBasic变态用法之函数指针

作者:佚名  来源:不详  发布时间:2006-12-29 13:29:01   

减小字体 增大字体

 
 
一、函数指针

  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,它的声明如下:

->#defineint(__cdecl*COMPARE)(constvoid*elem1,constvoid*elem2)
voidqsort(void*base,size_tnum,size_twidth,
COMPAREpfnCompare);->

  它需要一个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的版本。
DeclareSubCopyMemoryLib"kernel32"Alias_
"RtlMoveMemory"(ByValdestAsLong,ByValsourceAsLong,_
ByValnumBytesAsLong)

'嘿嘿,看下面是如何将CallWindowProc的声明做成Compare声明的。
DeclareFunctionCompareLib"user32"Alias_
"CallWindowProcA"(ByValpfnCompareAsLong,ByValpElem1AsLong,_
ByValpElem2AsLong,ByValunused1AsLong,_
ByValunused2AsLong)AsInteger
'注:ByValxxxxxAsLong,还记得吧!这是标准的指针声明方法。

'声明需要比较的数组元素的结构
PublicTypeTEmployee
 NameAsString
 SalaryAsCurrency
EndType

'再来看看我们的比较函数
'先按薪水比较,再按姓名比较
FunctionCompareSalaryName(Elem1AsTEmployee,_
     Elem2AsTEmployee,_
     unused1AsLong,_
     unused2AsLong)AsInteger
 DimRetAsInteger
 Ret=Sgn(Elem1.Salary-Elem2.Salary)
 IfRet=0Then
  Ret=StrComp(Elem1.Name,Elem2.Name,vbTextCompare)
 EndIf
 CompareSalaryName=Ret
EndFunction

'先按姓名比较,再按薪水比较
FunctionCompareNameSalary(Elem1AsTEmployee,_
     Elem2AsTEmployee,_
     unused1AsLong,_
     unused2AsLong)AsInteger
 DimRetAsInteger
 Ret=StrComp(Elem1.Name,Elem2.Name,vbTextCompare)
 IfRet=0Then
  Ret=Sgn(Elem1.Salary-Elem2.Salary)
 EndIf
 CompareNameSalary=Ret
EndFunction->

  最后再看看我们来看看我们最终的qsort的声明。

->Subqsort(ByValArrayPtrAsLong,ByValnCountAsLong,_
ByValnElemSizeAsInteger,ByValpfnCompareAsLong)->

  上面的ArrayPtr是需要排序数组的第一个元素的指针,nCount是数组的元素个数,nElemSize是每个元素大小,pfnCompare就是我们的比较函数指针。这个声明和C库函数里的qsort是极为相似的。

  和C一样,我们完全可以将Basic的函数指针传递给Basic的qsort函数。

  使用方式如下:

->DimEmployees(1To10000)AsTEmployee
'假设下面的调用对Employees数组进行了赋值初始化。
CallInitArray()
'现在就可以调用我们的qsort来进行排序了。
Callqsort(VarPtr(Employees(1)),UBound(Employees),_
LenB(Employees(1)),AddressOfCompareSalaryName)
'或者先按姓名排,再按薪水排
Callqsort(VarPtr(Employees(1)),UBound(Employees),_
LenB(Employees(1)),AddressOfCompareNameSalary)->

  聪明的朋友们,你们是不是已经看出这里的奥妙了呢?作为一个测验,你能现在就给出在qsort里使用函数指针的方法吗?比如现在我们要通过调用函数指针来比较数组的第i个元素和第j个元素的大小。

  没错,当然要使用前面声明的Compare(其实就是CallWindowProc)这个API来进行强制回调。

  具体的实现如下:

->Subqsort(ByValArrayPtrAsLong,ByValnCountAsLong,_
ByValnElemSizeAsInteger,ByValpfnCompareAsLong)
 DimiAsLong,jAsLong
 '这里省略快速排序算法的具体实现,仅给出比较两个元素的方法。
 IfCompare(pfnCompare,ArrayPtr (i-1)*nElemSize,_
   ArrayPtr (j-1)*nElemSize,0,0)>0Then
   '如果第i个元素比第j个元素大则用CopyMemory来交换这两个元素。
 EndIF
EndSub->



itbulo.com/post.php?action=newthread&fid=51&extra=page%3D1" title="如有错误,麻烦请及时告诉我们,谢谢。" target="_blank">我要纠错】【itbulo.com/" target="_blank">进入论坛交流】【关闭此页】【iTbulo.net/" class="lblue" target="_blank">进入博客】

  招式介绍完了,明白了吗?我再来简单地讲解一下上面Compare的意思,它非常巧妙地利用了CallWindowProc这个API。这个API需要五个参数,第一个参数就是一个普通的函数指针,这个API能够强马上回调这个函数指针,并将这个API的后四个Long型的参数传递给这个函数指针所指向的函数。这就是为什么我们的比较函数必须要有四个参数的原因,因为CallWindowProc这个API要求传递给的函数指针必须符合WndProc函数原形,WndProc的原形如下:

->LRESULT(CALLBACK*WNDPROC)(HWND,UINT,WPARAM,LPARAM);->

  上面的LRESULT、HWND、UIN

在百度中搜索更多VisualBasic变态用法之函数指针相关网页 转贴于:中国下载站

  • 上一篇文章:在VB中用DAO实现数据库编程
  • 下一篇文章:在VB中调用CHM帮助的几种方法
  • 阅读统计:[]
  • 中国下载站】【设为主页】【收藏本页】【打印本文】【回到顶部】【关闭此页

    相关文章
    文章评论(评论内容只代表网友观点,与本站立场无关!)

    用户名: 查看更多评论

    分 值:100分 85分 70分 55分 40分 25分 10分 0分

    内 容:

             (注“”为必填内容。) 验证码: 验证码,看不清楚?请点击刷新验证码


    设为首页 - 关于我们 - 广告服务 - 网站地图 - 加入收藏 - 网站声明 - 网站帮助 - 友情链接

    • Copyright (C) 2006-2008 www.cndownz.com All Rights Reserved.
      中国下载站 版权所有. 粤ICP备05141802号. 对本站有任何建议、意见或投诉,请来信:cndownzcom@yahoo.com.cn.
      喜欢中国下载站(cndownz.com),请把中国下载站(cndownz.com)告诉你QQ上的5位好友,多谢支持!