回调函数

来源:互联网 发布:linux内核移植步骤 编辑:程序博客网 时间:2024/04/29 20:57
回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就 ?
是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可 ?
以在回调函数里完成你要做的事。 ?
?
capVideoStreamCallback ?这个回调函数,我没有做过,看了一下Help,应该是通过发送消息 ?
WM_CAP_SET_CALLBACK_VIDEOSTREAM,来设置的,或者调用宏capSetCallbackOnVideoStream ?
来完成的。这样设定之后,系统在进行图像捕捉的过程中,就会自动调用你写的回调函数。 ?
?
这个回调函数的函数体需要你自已来写,然后在另一函数中调用,即是说: ?
LRESULT ?CALLBACK ?capVideoStreamCallback(HWND ?hWnd,LPVIDEOHDR ?lpVHdr) ?
{ ?
?........ ?
} ?
//在另一函数中调用它(即以capVideoStreamCallback的地址作为一参数) ?
Function(1,......,capVideoStreamCallback,.....); ?
这就好像我们用定时器一样,在设置定时器时需要为定时器设置一回调函数: ?
::SetTimer(m_hWnd,1,1000,(TIMERPROC)TMProc);这里的TMProc就是回调函数 ?
?
?
模块A有一个函数foo,它向模块B传递foo的地址,然后在B里面发生某种事件(event)时,通过从A里面传递过来的foo的地址调用foo,通知A发生了什么事情,让A作出相应反应。 ?
? ? ? ?那么我们就把foo称为回调函数。 ?
?
? ? ? ?“这个回调函数不是VFW.h中声明的么,“ ?
? ? ? ?----那是声明了回调函数原型,是告诉你传递进来的回调函数必须和它定义的原型保持一致。 ?
?
? ? ? ?”为什么要自己写函数体呢?“ ?
? ? ? ?----比如在上面模块B里面,它只知道当event发生时,向模块A发出通知,具体怎么回应这个事件,不是B所关心的,也不是B所能预料到的。 ?
? ? ? ?你站在A的角度上思考,当然要你自己作出对event的反应,也就是你要自己写函数体。 ?
?
? ? ? ?你如果明白了C++里面的函数指针,就很容易理解回调函数了。 ?
?
"不知道系统调用后有什么结果,或者我怎么利用这个结果啊" ?
---如果你向系统传递一个回调函数地址,那么你的程序就相当于上面我说的模块A,系统就相当于模块B,系统只是调用你的函数,它根本不可能知道会有什么结果。 ?
? ? ?你怎么利用这个结果,看你是怎么定义这个回调函数的。 ? ? ? ? ? ? ?
回调函数和回调机制是不同的概念,。,,函数是被调用的,但是回调机制在不同的语言中不都是以函数指针来实现的。。。。比如c#...一般的在windows ?api ?中,会调都是使用函数指针实现的。。。 ?
?
?
?
函数调用方式 ?
? ?
我们知道在进行函数调用时,有几种调用方法,主要分为C式,Pascal式.在C和C++中 ?
C式调用是缺省的,类的成员函数缺省调用为_stdcall。二者是有区别的,下面我们用 ?
实例说明一下: ?
?
1. ?__cdecl ?:C和C++缺省调用方式 ?
例子: ? ?
void ?Input( ?int ?&m,int ?&n);/*相当于void ?__cdecl ?Input(int ?&m,int ?&n);*/ ?
以下是相应的汇编代码: ?
00401068 ?lea ?eax,[ebp-8] ?;取[ebp-8]地址(ebp-8),存到eax ?
0040106B ?push ?eax ?;然后压栈 ?
0040106C ?lea ?ecx,[ebp-4] ?;取[ebp-4]地址(ebp-4),存到ecx ?
0040106F ?push ?ecx ?;然后压栈 ?
00401070 ?call ?@ILT+5(Input) ?(0040100a);然后调用Input函数 ?
00401075 ?add ?esp,8 ?;恢复栈 ?
? ?
? ? ? ?从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然 ?
后压栈ebp-4,然后调用函数Input,最后Input函数调用结束后,利用esp+8恢复栈。由 ?
此可见,在C语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复 ?
堆栈。 ?
下面看一下:地址ebp-8和ebp-4是什么? ?
? ? ? ?在VC的VIEW下选debug ?windows,然后选Registers,显示寄存器变量值,然后在 ?
选debug ?windows下面的Memory,输入ebp-8的值和ebp-4的值(或直接输入ebp-8和-4), ?
看一下这两个地址实际存储的是什么值,实际上是变量 ?n ?的地址(ebp-8),m的地址 ?
(ebp-4),由此可以看出:在主调用函数中进行实参的压栈并且顺序是从右到左。另外, ?
由于实参是相应的变量的引用,也证明实际上引用传递的是变量的地址(类似指针)。 ?
总结:在C或C++语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且 ?
恢复堆栈,实参的压栈顺序是从右到左,最后由主调函数进行堆栈恢复。由于主调用 ?
函数管理堆栈,所以可以实现变参函数。另外,命名修饰方法是在函数前加一个下划 ?
线(_). ?
?
2. ?WINAPI ?(实际上就是PASCAL,CALLBACK,_stdcall) ?
例子: ? ?
void ?WINAPI ?Input( ?int ?&m,int ?&n); ?
看一下相应调用的汇编代码: ?
00401068 ?lea ?eax,[ebp-8] ?
0040106B ?push ?eax ?
0040106C ?lea ?ecx,[ebp-4] ?
0040106F ?push ?ecx ?
00401070 ?call ?@ILT+5(Input) ?(0040100a) ?
? ?
? ? ? ?从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然 ?
后压栈ebp-4,然后调用函数Input,在调用函数Input之后,没有相应的堆栈恢复工作 ?
(为其它的函数调用,所以我没有列出) ?
下面再列出Input函数本身的汇编代码:(实际此函数不大,但做汇编例子还是大了些, ?
大家可以只看前和后,中间代码与此例子无关) ? ?
39: ?void ?WINAPI ?Input( ?int ?&m,int ?&n) ?
40: ?{ ?
00401110 ?push ?ebp ?
00401111 ?mov ?ebp,esp ?
00401113 ?sub ?esp,48h ?
00401116 ?push ?ebx ?
00401117 ?push ?esi ?
00401118 ?push ?edi ?
00401119 ?lea ?edi,[ebp-48h] ?
0040111C ?mov ?ecx,12h ?
00401121 ?mov ?eax,0CCCCCCCCh ?
00401126 ?rep ?stos ?dword ?ptr ?[edi] ?
41: ?int ?s,i; ?
42: ?
43: ?while(1) ?
00401128 ?mov ?eax,1 ?
0040112D ?test ?eax,eax ?
0040112F ?je ?Input+0C1h ?(004011d1) ?
44: ?{ ?
45: ?printf("/nPlease ?input ?the ?first ?number ?m:"); ?
00401135 ?push ?offset ?string ?"/nPlease ?input ?the ?first ?number ?m"... ?(004260b8) ?
0040113A ?call ?printf ?(00401530) ?
0040113F ?add ?esp,4 ?
46: ?scanf("%d",&m); ?
00401142 ?mov ?ecx,dword ?ptr ?[ebp+8] ?
00401145 ?push ?ecx ?
00401146 ?push ?offset ?string ?"%d" ?(004260b4) ?
0040114B ?call ?scanf ?(004015f0) ?
00401150 ?add ?esp,8 ?
47: ?
48: ?if ?( ?m= ?s ?) ?
004011B3 ?mov ?eax,dword ?ptr ?[ebp+8] ?
004011B6 ?mov ?ecx,dword ?ptr ?[eax] ?
004011B8 ?cmp ?ecx,dword ?ptr ?[ebp-4] ?
004011BB ?jl ?Input+0AFh ?(004011bf) ?
57: ?break; ?
004011BD ?jmp ?Input+0C1h ?(004011d1) ?
58: ?else ?
59: ?printf(" ?m ?< ?n*(n+1)/2,Please ?input ?again!/n"); ?
004011BF ?push ?offset ?string ?" ?m ?< ?n*(n+1)/2,Please ?input ?agai"... ?(00426060) ?
004011C4 ?call ?printf ?(00401530) ?
004011C9 ?add ?esp,4 ?
60: ?} ?
004011CC ?jmp ?Input+18h ?(00401128) ?
61: ?
62: ?} ?
004011D1 ?pop ?edi ?
004011D2 ?pop ?esi ?
004011 ?
--------------------------------------------------------------- ?
?
?
Win32程序函数调用时堆栈变化情况分析 ?
? ?
2002-12-10 ?18:19:13 ? ? ?PCVC.NET ? ? ?halk ? ? ?阅读次数: ?4804 ? ?
? ?在经典的汇编语言教程中,函数调用时堆栈的使用都是着重讲解的问题。如今随着高级语言的越来越完善,单纯使用汇编开发的程序已经不多了。但对函数调用时堆栈动向的了解仍有助于我们明晰程序的执行流程,从而在程序编写和调试的过程中有一个清晰的思路。 ?
?
一.调用约定 ?
在Win32中,有关函数的调用主要有两种约定。 ?
1._stdcall ?
? ? ? ? ? ? ? ?以__stdcall方式调用的函数有以下特征: ?
? ? ? ?• ? ?参数由右至左压栈 ?
? ? ? ?• ?调用返回时,堆栈由被调函数调整 ?
2.__cdecl ?
__cdecl约定是C/C++函数的默认调用约定。它有以下特征: ?
? ? ? ?• ?参数由右至左压栈 ?
? ? ? ?• ?调用返回时,堆栈由调用者调整 ?
?
二.Win32函数调用过程 ?
?
1. ? ? ? ?压入参数 ?
这里依据以上的调用方式将调用者给出的参数一一压入堆栈。 ?
?
2. ? ? ? ?压入断点 ?
当程序执行到Call指令的时候,当前语句的地址作为断点地址压入堆栈。 ?
?
3. ? ? ? ?跳转 ?
eip的值被重新设置为被调函数的起始地址。 ?
?
4. ? ? ? ?mov ?ebp, ?esp ?
这里ebp被用来在堆栈中寻找调用者压入的参数,同时作为调用者堆栈指针的一个备份。在此前还应该执行一条: ?
push ?ebp ?
把ebp中原来的数值保存。 ?
?
5. ? ? ? ?sub ?esp,N ?
这里N是函数内局部变量的总字节数加上一个整数,一般为40。此后esp即为被调函数的堆栈指针了。 ?
?
6. ? ? ? ?初始化esp ?~ ?esp-N之间的N字节空间 ?
这是对堆栈中已分配给局部变量使用的内存空间的初始化,一般全部设置为0xcc。 ?
?
7. ? ? ? ?顺序执行函数内语句。 ?
此时函数的堆栈位于所有局部变量的内存空间之后,二者之间一般有40字节的隔离带。 ?
?
8.返回 ?
为保障调用的正常返回,函数内应当保证规范使用堆栈,使即将返回的时候esp的值恢复为执行第一条语句前的状态。说明白点,就是每一条push都要有相应的pop。 ?
调用返回的过程如下: ?
mov ?esp, ?ebp ?
执行后,esp恢复为调用者的堆栈指针,栈顶除断点地址外,还存有原ebp的值和调用时压入的参数。 ?
然后依次弹出ebp的值和断点地址。如果是__cdecl约定则直接返回调用者,调用者将负责调整堆栈,丢弃调先前压入的参数。如果是__stdcall则这个工作由被调函数来执行。 ?
?
程序样例如下: ?
…… ?
0040B8E8 ? ? ?push ? ? ? ? ? ? ? ?1 ? ? ? ? ? ? ? ? ? ? ? ?;压入参数 ?
0040B8EA ? ? ?call ? ? ? ? ? ? ? ?00401028 ? ? ? ? ? ? ? ?;调用函数 ?
…… ?
00401028 ? ? ?jmp ? ? ? ? ? ? ? ? ?0040b7c0 ? ? ? ? ? ? ? ?;跳转到函数入口 ?
…… ?
0040B7C0 ? ? ?push ? ? ? ? ? ? ? ?ebp ? ? ? ? ? ? ? ? ? ? ? ?;保存ebp ?
0040B7C1 ? ? ?mov ? ? ? ? ? ? ? ? ?ebp,esp ? ? ? ? ? ? ? ? ?
0040B7C3 ? ? ?sub ? ? ? ? ? ? ? ? ?esp,44h ? ? ? ? ? ? ? ?;设置函数的堆栈指针,此函数中有4 ?
;字节的局部变量 ?
0040B7C6 ? ? ?push ? ? ? ? ? ? ? ?ebx ?
0040B7C7 ? ? ?push ? ? ? ? ? ? ? ?esi ? ? ? ? ? ? ? ? ?
0040B7C8 ? ? ?push ? ? ? ? ? ? ? ?edi ?
0040B7C9 ? ? ?lea ? ? ? ? ? ? ? ? ?edi,[ebp-44h] ? ? ? ? ?
0040B7CC ? ? ?mov ? ? ? ? ? ? ? ? ?ecx,11h ?
0040B7D1 ? ? ?mov ? ? ? ? ? ? ? ? ?eax,0CCCCCCCCh ?
0040B7D6 ? ? ?rep ?stos ? ? ? ?dword ?ptr ?[edi] ? ? ? ?;初始化局部变量空间 ?
0040B7D8 ? ? ?mov ? ? ? ? ? ? ? ? ?eax,dwo
原创粉丝点击