VC++回调函数Word下载.docx

上传人:b****2 文档编号:6119531 上传时间:2023-05-06 格式:DOCX 页数:21 大小:31.23KB
下载 相关 举报
VC++回调函数Word下载.docx_第1页
第1页 / 共21页
VC++回调函数Word下载.docx_第2页
第2页 / 共21页
VC++回调函数Word下载.docx_第3页
第3页 / 共21页
VC++回调函数Word下载.docx_第4页
第4页 / 共21页
VC++回调函数Word下载.docx_第5页
第5页 / 共21页
VC++回调函数Word下载.docx_第6页
第6页 / 共21页
VC++回调函数Word下载.docx_第7页
第7页 / 共21页
VC++回调函数Word下载.docx_第8页
第8页 / 共21页
VC++回调函数Word下载.docx_第9页
第9页 / 共21页
VC++回调函数Word下载.docx_第10页
第10页 / 共21页
VC++回调函数Word下载.docx_第11页
第11页 / 共21页
VC++回调函数Word下载.docx_第12页
第12页 / 共21页
VC++回调函数Word下载.docx_第13页
第13页 / 共21页
VC++回调函数Word下载.docx_第14页
第14页 / 共21页
VC++回调函数Word下载.docx_第15页
第15页 / 共21页
VC++回调函数Word下载.docx_第16页
第16页 / 共21页
VC++回调函数Word下载.docx_第17页
第17页 / 共21页
VC++回调函数Word下载.docx_第18页
第18页 / 共21页
VC++回调函数Word下载.docx_第19页
第19页 / 共21页
VC++回调函数Word下载.docx_第20页
第20页 / 共21页
亲,该文档总共21页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

VC++回调函数Word下载.docx

《VC++回调函数Word下载.docx》由会员分享,可在线阅读,更多相关《VC++回调函数Word下载.docx(21页珍藏版)》请在冰点文库上搜索。

VC++回调函数Word下载.docx

服务执行者和服务方式制定者。

1.服务执行者先制定服务规范;

2.服务方式制定者然后按照规范制定服务方式;

3.然后执行者按照这个方式提供服务。

回调函数的方式是把函数指针的作为参数传递进去,所以规范就是约定函数的参数类型,个数。

这篇文章如下:

调用(calling)机制从汇编时代起已经大量使用:

准备一段现成的代码,调用者可以随时跳转至此段代码的起始地址,执行完后再返回跳转时的后续地址。

CPU为此准备了现成的调用指令,调用时可以压栈保护现场,调用结束后从堆栈中弹出现场地址,以便自动返回。

借堆栈保护现场真是一项绝妙的发明,它使调用者和被调者可以互不相识,于是才有了后来的函数和构件,使吾辈编程者如此轻松愉快。

若评选对人类影响最大之发明,在火与车轮之后,笔者当推压栈调用。

话虽这样说,此调用机制并非完美。

回调函数就是一例。

函数之类本是为调用者准备的美餐,其烹制者应对食客了如指掌,但实情并非如此。

例如,写一个快速排序函数供他人调用,其中必包含比较大小。

麻烦来了:

此时并不知要比较的是何类数据--整数、浮点数、字符串?

于是只好为每类数据制作一个不同的排序函数。

更通行的办法是在函数参数中列一个回调函数地址,并通知调用者:

君需自己准备一个比较函数,其中包含两个指针类参数,函数要比较此二指针所指数据之大小,并由函数返回值说明比较结果。

排序函数借此调用者提供的函数来比较大小,借指针传递参数,可以全然不管所比较的数据类型。

被调用者回头调用调用者的函数(够咬嘴的),故称其为回调(callback)。

回调函数使程序结构乱了许多。

WindowsAPI函数集中有不少回调函数,尽管有详尽说明,仍使初学者一头雾水。

恐怕这也是无奈之举。

无论何种事物,能以树形结构单向描述毕竟让人舒服些。

如果某家族中孙辈又是某祖辈的祖辈,恐怕无人能理清其中的头绪。

但数据处理之复杂往往需要构成网状结构,非简单的客户/服务器关系能穷尽。

Windows系统还包含着另一种更为广泛的回调机制,即消息机制。

消息本是Windows的基本控制手段,乍看与函数调用无关,其实是一种变相的函数调用。

发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。

消息所附带的WParam和LParam相当于函数的参数,只不过比普通参数更通用一些。

应用程序可以主动发送消息,更多情况下是坐等Windows发送消息。

一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。

操作系统本是为应用程序服务,由应用程序来调用。

而应用程序一旦启动,却要反过来等待操作系统的调用。

这分明也是一种回调,或者说是一种广义回调。

其实,应用程序之间也可以形成这种回调。

假如进程B收到进程A发来的消息,启动了一段代码,其中又向进程A发送消息,这就形成了回调。

这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会循环不已,直至把程序搞垮。

若是故意编写成此递归调用,并设好终止条件,倒是很有意思。

但这种程序结构太隐蔽,除非十分必要,还是不用为好。

利用消息也可以构成狭义回调。

上面所举排序函数一例,可以把回调函数地址换成窗口handle。

如此,当需要比较数据大小时,不是去调用回调函数,而是借API函数SendMessage向指定窗口发送消息。

收到消息方负责比较数据大小,把比较结果通过消息本身的返回值传给消息发送方。

所实现的功能与回调函数并无不同。

当然,此例中改为消息纯属画蛇添脚,反倒把程序搞得很慢。

但其他情况下并非总是如此,特别是需要异步调用时,发送消息是一种不错的选择。

假如回调函数中包含文件处理之类的低速处理,调用方等不得,需要把同步调用改为异步调用,去启动一个单独的线程,然后马上执行后续代码,其余的事让线程慢慢去做。

一个替代办法是借API函数PostMessage发送一个异步消息,然后立即执行后续代码。

这要比自己搞个线程省事许多,而且更安全。

如今我们是活在一个object时代。

只要与编程有关,无论何事都离不开object。

但object并未消除回调,反而把它发扬光大,弄得到处都是,只不过大都以事件(event)的身份出现,镶嵌在某个结构之中,显得更正统,更容易被人接受。

应用程序要使用某个构件,总要先弄清构件的属性、方法和事件,然后给构件属性赋值,在适当的时候调用适当的构件方法,还要给事件编写处理例程,以备构件代码来调用。

何谓事件?

它不过是一个指向事件例程的地址,与回调函数地址没什么区别。

不过,此种回调方式比传统回调函数要高明许多。

首先,它把让人不太舒服的回调函数变成一种自然而然的处理例程,使编程者顿觉气顺。

再者,地址是一个危险的东西,用好了可使程序加速,用不好处处是陷阱,程序随时都会崩溃。

现代编程方式总是想法把地址隐藏起来(隐藏比较彻底的如VB和Java),其代价是降低了程序效率。

事件例程使编程者无需直接操作地址,但并不会使程序减速。

更妙的是,此一改变,本是有损程序结构之奇技怪巧变成一种崭新设计理念,不仅免去被人抨击,而且逼得吾等凡人净手更衣,细细研读,仰慕至今。

只是偶然静心思虑,发觉不过一瓶旧酒而已,故引得此番议论,让诸君见笑了。

事件驱动程序设计是围绕着消息基础形成的,发生一个事件,伴随着一大堆的消息。

我理解“回调机制”是window在执行某个API函数的过程中,调用指定的一个函数。

我们可以模拟一下:

假设ms提供一个函数叫做EnumFont,该函数是得到所有的字体,假设它的实现是

EnumFont()

{

while((f=FindNextFont())!

=NULL)

{

printf("

fontname:

"

+f.name);

}

}

这样就循环显示出所有的字体名称。

但是,开发者可能对字体信息另有用处,那么如何才能让开发者能使用这些信息呢,于是做改进:

EnumFont(void*userFunc)

printf("

if(userFunc!

userFunc(f);

假设userFunc是一个函数voidf(FontObjectfont).这样使用者只需要定义一个函数:

voidmyfunc(FontObjectfont)

listCtrl.Addstring(font.name);

通过使用EnumFont(myfunc)就可以将所有额字体信息添加到一个列表框中。

那么我们称myfunc是一个回调函数,即让某个系统函数调用的函数。

因此可以得出结论:

1回调函数是由开发者按照一定的原型进行定义的函数。

2回调函数并不由发者直接调用执行。

3回调函数通常作为参数传递给系统API,由该API来调用。

4回调函数可能被系统API调用一次,也可能被循环调用多次。

比如函数intEnumFontFamilies(

HDChdc,//handletodevicecontrol

LPCTSTRlpszFamily,//pointertofamily-namestring

FONTENUMPROClpEnumFontFamProc,

//pointertocallbackfunction

LPARAMlParam//pointertoapplication-supplieddata

);

其中的FONTENUMPROClpEnumFontFamProc就是一个回调函数,该函数遵照格式

IntCALLBACKEnumFontFamProc(ENUMLOGFONTFAR*lpelf,NEWTEXTMETRICFAR*lpntm,intFontType,LPARAMlParam)进行定义。

如同mutant所说,回调函数主要用于一些比较费时的操作,或响应不知道何时将会发生的事件,回调函数提供了一种异步的机制,相对于同步执行,提高了效率。

前者的例子如WriteFileEx,ReadFileEx等,函数的最后一个参数是一个回调函数的指针,程序中调用WriteFileEx以后,就直接返回了,可以继续进行其他工作,系统在读写操作完成后通知程序作善后处理.后者的例子就是windows的事件机制回调函数的另一个用途,是用于一些枚举函数,如EnumDisplayModes等,每找到一种支持的显示模式,就通知回调函数,由回调函数具体处理,这是因为EnumDisplayModes本身并不知道用户要如何处。

用户提供回调函数,定制系统的功能,这样,不同的用户提供不同的回调函数,可以使系统具有不同的功能.这就是所谓的plugin。

使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。

而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。

至于如何定义回调函数,跟具体使用的API函数有关,一般在帮助中有说明回调函数的参数和返回值等。

4.一个C++简单的回调函数实现

下面创建了一个sort.dll的动态链接库,它导出了一个名为CompareFunction的类型--typedefint(__stdcall*CompareFunction)(constbyte*,constbyte*),它就是回调函数的类型。

另外,它也导出了两个方法:

Bubblesort()和Quicksort(),这两个方法原型相同,但实现了不同的排序算法。

VoidDLLDIR__stdcallBubblesort(byte*array,intsize,intelem_size,CompareFunctioncmpFunc);

VoidDLLDIR__stdcallQuicksort(byte*array,intsize,intelem_size,CompareFunctioncmpFunc);

  这两个函数接受以下参数:

  ·

byte*array:

指向元素数组的指针(任意类型)。

intsize:

数组中元素的个数。

intelem_size:

数组中一个元素的大小,以字节为单位。

CompareFunctioncmpFunc:

带有上述原型的指向回调函数的指针。

这两个函数的会对数组进行某种排序,但每次都需决定两个元素哪个排在前面,而函数中有一个回调函数,其地址是作为一个参数传递进来的。

对编写者来说,不必介意函数在何处实现,或它怎样被实现的,所需在意的只是两个用于比较的元素的地址,并返回以下的某个值(库的编写者和使用者都必须遵守这个约定):

·

-1:

如果第一个元素较小,那它在已排序好的数组中,应该排在第二个元素前面。

0:

如果两个元素相等,那么它们的相对位置并不重要,在已排序好的数组中,谁在前面都无所谓。

1:

如果第一个元素较大,那在已排序好的数组中,它应该排第二个元素后面。

基于以上约定,函数Bubblesort()的实现如下,Quicksort()就稍微复杂一点:

voidDLLDIR__stdcallBubblesort(byte*array,intsize,intelem_size,CompareFunctioncmpFunc)

 for(inti=0;

i<

size;

i++)

 {

  for(intj=0;

j<

size-1;

j++)

  {

   //回调比较函数

   if(1==(*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size))

   {

    //两个相比较的元素相交换

    byte*temp=newbyte[elem_size];

    memcpy(temp,array+j*elem_size,elem_size);

    memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);

    memcpy(array+(j+1)*elem_size,temp,elem_size);

    delete[]temp;

   }

  }

 }

注意:

因为实现中使用了memcpy(),所以函数在使用的数据类型方面,会有所局限。

对使用者来说,必须有一个回调函数,其地址要传递给Bubblesort()函数。

下面有二个简单的示例,一个比较两个整数,而另一个比较两个字符串:

int__stdcallCompareInts(constbyte*velem1,constbyte*velem2)

 intelem1=*(int*)velem1;

 intelem2=*(int*)velem2;

 if(elem1<

elem2)

  return-1;

 if(elem1>

  return1;

 return0;

int__stdcallCompareStrings(constbyte*velem1,constbyte*velem2)

 constchar*elem1=(char*)velem1;

 constchar*elem2=(char*)velem2;

 returnstrcmp(elem1,elem2);

下面另有一个程序,用于测试以上所有的代码,它传递了一个有5个元素的数组给Bubblesort()和Quicksort(),同时还传递了一个指向回调函数的指针。

intmain(intargc,char*argv[])

 inti;

 intarray[]={5432,4321,3210,2109,1098};

 cout<

<

BeforesortingintswithBubblesort\n"

;

 for(i=0;

5;

  cout<

array[i]<

'

\n'

 Bubblesort((byte*)array,5,sizeof(array[0]),&

CompareInts);

Afterthesorting\n"

 constcharstr[5][10]={"

estella"

"

danielle"

crissy"

bo"

angie"

};

BeforesortingstringswithQuicksort\n"

str[i]<

 Quicksort((byte*)str,5,10,&

CompareStrings);

如果想进行降序排序(大元素在先),就只需修改回调函数的代码,或使用另一个回调函数,这样编程起来灵活性就比较大了。

上面的代码中,可在函数原型中找到__stdcall,因为它以双下划线打头,所以它是一个特定于编译器的扩展,说到底也就是微软的实现。

任何支持开发基于Win32的程序都必须支持这个扩展或其等价物。

以__stdcall标识的函数使用了标准调用约定,为什么叫标准约定呢,因为所有的Win32API(除了个别接受可变参数的除外)都使用它。

标准调用约定的函数在它们返回到调用者之前,都会从堆栈中移除掉参数,这也是Pascal的标准约定。

但在C/C++中,调用约定是调用者负责清理堆栈,而不是被调用函数;

为强制函数使用C/C++调用约定,可使用__cdecl。

另外,可变参数函数也使用C/C++调用约定。

Windows操作系统采用了标准调用约定(Pascal约定),因为其可减小代码的体积。

这点对早期的Windows来说非常重要,因为那时它运行在只有640KB内存的电脑上。

如果你不喜欢__stdcall,还可以使用CALLBACK宏,它定义在windef.h中:

#defineCALLBACK__stdcallor

#defineCALLBACKPASCAL//而PASCAL在此被#defined成__stdcall

作为回调函数的C++方法

因为平时很可能会使用到C++编写代码,也许会想到把回调函数写成类中的一个方法,但先来看看以下的代码:

classCCallbackTester

 public:

 intCALLBACKCompareInts(constbyte*velem1,constbyte*velem2);

Bubblesort((byte*)array,5,sizeof(array[0]),

&

CCallbackTester:

:

如果使用微软的编译器,将会得到下面这个编译错误:

errorC2664:

Bubblesort'

:

cannotconvertparameter4from'

int(__stdcallCCallbackTester:

*)(constunsignedchar*,constunsignedchar*)'

to'

int(__stdcall*)(constunsignedchar*,constunsignedchar*)'

Thereisnocontextinwhichthisconversionispossible

这是因为非静态成员函数有一个额外的参数:

this指针,这将迫使你在成员函数前面加上static。

当然,还有几种方法可以解决这个问题,但限于篇幅,就不再论述了。

5.VC++中回调函数使用的变身大法 

对于回调函数的编写始终是写特殊处理功能程序时用到的技巧之一。

先介绍一下回调的使用基本方法与原理。

1、在这里设:

回调函数为A()(这是最简单的情况,不带参数,但我们应用的实际情况常常很会复杂),使用回调函数的操作函数为B(),但B函数是需要参数的,这个参数就是指向函数A的地址变量,这个变量一般就是函数指针。

使用方法为:

intA(char*p);

//回调函数

typedefint(*CallBack)(char*p);

//声明CallBack类型的函数指针

CallBackmyCallBack;

//声明函数指针变量

myCallBack=A;

//得到了函数A的地址

B函数一般会写为B(CallBacklpCall,char*P,........);

//此处省略了p后的参数形式。

所以回调机制可解为,函数B要完成一定功能,但他自己是无法实现全部功能的。

需要借助于函数A来完成,也就是回调函数。

B的实现为:

B(CallBacklpCall,char*pProvide)

 ...........//B的自己实现功能语句

 lpCall(PpProvide);

//借助回调完成的功能,也就是A函数来处理的。

//--------------使用例子-------------

char*p="

hello!

"

myCallBack=A;

B(A,p);

以上就是回调的基本应用,本文所说的变身,其实是利用传入不同的函数地址,实现调用者类与回调函数所在类的不同转换。

1、问题描述

CUploadFile类完成数据上传,与相应的界面进度显示。

主要函数Send(...)和回调函数GetCurState();

classCUploadFile:

publicCDialog

 ......

 intSend(LPCTSTRlpS

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工程科技 > 信息与通信

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2