凡是由你设计却由windows系统呼叫的函数,统称为callback函数。某些API函数要求以callback作为你参数之一。如SetTimer,LineDDA,EnumObjects。
回调函数是由开发者按照一定的原形进行定义的函数(每个回调函数都必须遵循这个原则来设计) 例如: ----------------------------------------
BOOL CALLBACK DialogProc( HWND hwndDlg, // handle of dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); ---------------------------------------- 说明: 回调函数必须有关键词 CALLBACK; 回调函数本身必须是全局函数或者静态函数,不可定义为某个特定的 类的成员函数 2 回调函数并不由开发者直接调用执行(只是使用系统接口API函数作为起点) 3 回调函数通常作为参数传递给系统API,由该API来调用 4 回调函数可能被系统API调用一次,也可能被循环调用多次
WINAPI见windef.h这个头文件
#define WINAPI __stdcall 默认情况下,我们的函数调用都是遵循__stdcall这个规则的。当然,也有诸如__cdecl、__pascal等规则。 使用__stdcall还是__cdecl或__pascal,在纯Windows编程下并非特别需要。 __stdcall: 1、进行函数调用,函数参数的入栈方式是最右边先入栈。 2、同时__stdcall规定,子函数负责栈的回收(调用者只负责压栈). 题外话:__pascal的调用规则是从左到右,正好与__stdcall相反。 3、C调用约定(即用__cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)当然,这些工作是 应用程序自己完成的,不需要编写者动手。汇编语言另当别论(POP SP POP BP等) 。另外,在函数名修饰约定方面也有所不同。 _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的 可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。
MFC缺省调用约定
在函数调用过程中,会使用栈。__stdcall与__cdecl是两种不同的函数调用约定,定义了函数参数入栈的顺序,由调用函数还是被调用函数将参数弹出栈,以及产生函数修饰名的方法。关于这两个调用约定的详细信息,读者可参看MSDN。对于参数个数可变的函数,例如printf,使用的是__cdecl调用约定,Win32的API函数都遵循__stdcall调用约定。在VC++开发环境中,默认的编译选项是__cdecl,对于那些需要__stdcall调用约定的函数,在声明时必须显式地加上__stdcall。在Windows程序中,回调函数必须遵循__stdcall调用约定,所以我们在声明回调函数时要使用CALLBACK。使用CALLBACK而不是__stdcall的原因是为了告诉我们这是一个回调函数。注意,在Windows 98和Windows 2000下,声明窗口过程函数时,即使不使用CALLBACK也不会出错,但在Windows NT4.0下,则会出错
WINAPI和CALLBACK这两个宏有什么区别呢?
_stdcall _cdecl _pascal _fastcall这些关键字是什么意思,有什么区别呢?
首先看MSDN里给出的解释,不过有些语焉不详哦
WINAPI
·Use in place of FAR PASCAL in API declarations. If you are writing a DLL with exported API entry points, you can use this for your own APIs.
CALLBACK
·Use in place of FAR PASCAL in application callback routines such as window procedures and dialog procedures.
再看看到底这两个宏的内容是什么吧
VC:WINDEF.h
#define CALLBACK PASCAL //=_pascal,VC已经不支持直接使用_pascal了
#define WINAPI CDECL //=_cdecl
BCB:windef.h
#define CALLBACK __stdcall
#define WINAPI __stdcall
引出了cdecl stdcall等一些可能很少见的关键字
那么cdecl、pascal、stdcall、fastcall等修饰符号到底什么意思呢?
非常简单,就是关于堆栈的一些说明,首先是函数参数压栈顺序,其次是
压入堆栈的内容由谁来清除,调用者还是函数自己?
这些开关用来告诉编译器产生什么样的汇编代码。
下面把区别列表如下:
Directive Parameter order Clean-up Passes parameters in registers?
register Left-to-right Routine Yes
pascal Left-to-right Routine No
cdecl Right-to-left Caller No
stdcall Right-to-left Routine No
safecall Right-to-left Routine No
简单说明:
__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。__cdecl可以写成_cdecl。
__stdcall调用约定用于调用Win32 API函数。采用__stdcal约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。__stdcall可以写成_stdcall。
__fastcall约定用于对性能要求非常高的场合。__fastcall约定将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall可以写成_fastcall。
·特别说明
1. 在默认情况下,采用__cdecl方式,因此可以省略.
2. WINAPI一般用于修饰动态链接库中导出函数
3. CALLBACK仅用于修饰回调函数
4. 你可能已经发现,VC下和BCB下对WINAPI的定义不同,那么你至少理解了
为什么不能直接从BCB下调用VC的dll的一个原因了。