extern "C" __stdcall对函数的使用声明如下:
1 extern "C":
在当调用别人写的库时,注意库是使用何种编译器,若是C的,则你在用VC中的C++编译器调用时就得加
#if defined(__cplusplus)
extern "C" {
#endif
..........声明被调用的函数名
#if defined(__cplusplus)
};
#endif
这样就可以让C++使用因编译器不同导致的名称不同的函数!!!
2 __stdcall: (部分转载)
__stdcall是最常用的方式(API)
__cdecl是C和C++程序的缺省调用方式。
__stdcall __cdecl使用的意义在于:当我们使用动态连接库的时候,需要声明类型,在调用的时候类型要一致,否则会应压栈等方式不同而可能错误如下:
DLL中:
int _declspec(dllexport) __stdcall IDTS_BV(BVinfo *Bvinforma)
调用程序中:
typedef int (*pIDTS_IDTS_SetBV)(BVinfo *Adinfo_para);//__stdcall 未添加
pIDTS_IDTS_SetBV IDTS_SetBV;
DllHandle = LoadLibrary(m_DriverName);
IDTS_SetBV = (pIDTS_IDTS_SetBV) GetProcAddress(DllHandle, "IDTS_BV"));
调用 可能 出错!这两种压栈方式一样,而且只有一个变量 好像不出错!!!!!!!!!!!!!!!!!!!!
至于这种函数被调用,则和普通的cdecl及stdcall调用函数一致。函数调用约定导致的常见问题如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两种常见的问题:
函数原型声明和函数体定义不一致
DLL导入函数时声明了不同的函数约定
以后者为例,假设我们在dll种声明了一种函数为:
__declspec(dllexport) int func(int a,int b);//注意,这里没有stdcall,使用的是
cdecl
使用时代码为:
typedef int (*WINAPI DLLFUNC)func(int a,int b);
hLib = LoadLibrary(...);
DLLFUNC func = (DLLFUNC)GetProcAddress(...)//这里修改了调用约定
result = func(1,2);//导致错误
由于调用者没有理解WINAPI的含义错误的增加了这个修饰,上述代码必然导致堆栈被破坏,MFC在编译时插入的checkesp函数将告诉你,堆栈被破坏了。
每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用__stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。
__stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。
__fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
#define cdecl _cdecl
#ifndef CDECL
#define CDECL _cdecl
#endif
几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,为什么??
首先,我们谈一下两者之间的区别:
WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清楚,这里就是问题的关键,如何清除??
如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。
如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现),如JNI。
那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度(如 typedef int (*MYPROC)(LPTSTR, ...); ),事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。
到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。
1.__cdecl
这是编译器默认的函数调用转换方式,它可以处理可变参数的函数调用。参数
的入栈顺序是从右向左。在函数运行结束后,由调用函数负责清理入栈的参数。
在编译时,在每个函数前面加上下划线(_),没有函数名大小写的转换。即
_functionname
2.__fastcall
有一些函数调用的参数被放入ECX,EDX中,而其它参数从右向左入栈。被调用
函数在它将要返回时负责清理入栈的参数。在内嵌汇编语言的时候,需要注意
寄存器的使用,以免与编译器使用的产生冲突。函数名字的转换是:
@functionname@number
没有函数名大小写的转换,number表示函数参数的字节数。由于有一些参数不
需要入栈,所以这种转换方式会在一定程度上提高函数调用的速度。
3.__stdcall
函数参数从右向左入栈,被调用函数负责入栈参数的清理工作。函数名转换格
式如下: