- C/C++ code
-
//DLL声明详细://DLL工程由VC6的向导选“Regular DLL using shared MFC DLL”生成extern"C" __declspec(dllexport) long Show2(long n){ AFX_MANAGE_STATE(AfxGetStaticModuleState()); CString strShow; strShow.Format("%d",n); AfxMessageBox(strShow); return0;}
- C/C++ code
-
//调用详细://VC6向导生成的dialog工程void CTestDlg::OnButton1() { typedef long (WINAPI * TESTDLL_FUNCTION)(long); HINSTANCE hDll; hDll = ::LoadLibrary ("mfcdll.dll"); if(hDll==NULL) { AfxMessageBox("Fail: mfcdll.dll"); } TESTDLL_FUNCTION proc; proc = (TESTDLL_FUNCTION)GetProcAddress (hDll,"Show2"); if(proc!=(TESTDLL_FUNCTION)NULL) long lR = (*proc)(123456); FreeLibrary(hDll); }
出错:
弹出框:
Module:
File: i386/chkesp.c
Line: 42
The value of ESP was not properly saved across a function call.
This is usually a result of calling a function declared with one
calling convention with a function pointer declared with a different calling convention.
(Press Retry to debug the application)
解决办法:
1.查看DLL的工程里的C/C++编译参数是否一至:
a.) Category --> Code Genarition --> Strut member alignmenet 项
b.) Project Options:里去掉(Debugj里去掉 /GZ release版里去掉 /O2 )
比对:
////////////////////////////////////////////
release
# ADD CPP /nologo /Zp1 /MD /W3 /GX /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafx.h" /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafx.h" /FD /c
debug
# ADD CPP /nologo /Zp1 /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FR /Yu"stdafx.h" /FD /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FR /Yu"stdafx.h" /FD /GZ /c
另外方法:
在DLL工程里的函数前加 __cdecl ; 使用DLL的工程里的 WINAPI 改为__cdecl
/////////////////////////////////////////////
VSS下Clear工程:
1.删*.opt
2.文本改*.dsp
3.双击*.dsp打开工程
////////////////////////////////////////////
函数调用规范__cdecl和__stdcall的区别一目了然(表格形式)
Posted on Tuesday, May 24, 2005 3:23 PM
#C & C++
__cdecl
|
__stdcall |
C 和 C++ 程序的缺省调用规范 |
为了使用这种调用规范,需要你明确的加上 __stdcall (或 WINAPI )文字。即 return-type
|
在被调用函数 (Callee) 返回后,由调用方 (Caller) 调整堆栈。
1. 调用方的函数调用
2. 被调用函数的执行
3. 被调用函数的结果返回
4. 调用方清除调整堆栈
|
在被调用函数 (Callee) 返回前,由被调用函数 (Callee) 调整堆栈。图示:
1. 调用方的函数调用
2. 被调用函数的执行
3. 被调用函数清除调整堆栈
4. 被调用函数的结果返回 |
因为每个调用的地方都需要生成一段调整堆栈的代码,所以最后生成的文件较大。
|
因为调整堆栈的代码只存在在一个地方(被调用函数的代码内),所以最后生成的文件较小。 |
函数的参数个数可变(就像 printf 函数一样),因为只有调用者才知道它传给被调用函数几个参数,才能在调用结束时适当地调整堆栈。
|
函数的参数个数不能是可变的。 |
对于定义在 C 程序文件中的输出函数,函数名会保持原样,不会被修饰。 对于定义在 C++ 程序文件中的输出函数,函数名会被修饰, MSDN 说 Underscore character (_) is prefixed to names . 我实际测试( VC4 和 VC6 )下来发现好像不是那么简单。 可通过在前面加上 extern “C” 以去除函数名修饰。也可通过 .def 文件去除函数名修饰。 |
不论是 C 程序文件中的输出函数还是 C++ 程序文件中的输出函数,函数名都会被修饰。 对于定义在 C 程序文件中的输出函数, An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list. 对于定义在 C++ 程序文件中的输出函数,好像更复杂,和 __cdecl 的情况类似。 好像只能通过 .def 文件去除函数名修饰。
|
_beginthread 需要 __cdecl 的线程函数地址
|
_beginthreadex 和 CreateThread 需要 __stdcall 的线程函数地址
|
两者的参数传递顺序都是从右向左。 为了让 VB 可以调用,需要用 __stdcall 调用规范来定义 C/C++ 函数。请参看Microsoft KB153586 当你 LoadLibrary 一个 DLL 文件后, 把 GetProcAddress 取得的函数地址传给上面三个线程生成函数时,请务必确认实际定义在 DLL 文件的输出函数符合调用规范要求。否则,编译成 Release 版后运行,可能会破坏堆栈,程序行为不可预测。 VC 中的相关编译开关:/Gd /Gr /Gz。另外,VC6中新增加的 /GZ 编译开关可以帮你检查堆栈问题。
原来的一些调用约定可以不再使用。它们被定义成调用约定_stdcall或者_cdecl。例如: #define CALLBACK __stdcall #define WINAPI __stdcall #define WINAPIV __cdecl #define APIENTRY WINAPI #define APIPRIVATE __stdcall #define PASCAL __stdcall |
_f@4(在外部汇编语言里可以用这个名字引用这个函数)
1、以“?”标识函数名的开始,后跟函数名;
2、函数名后面以“@@YG”标识参数表的开始,后跟参数表;
3、参数表以代号表示:
X--void ,
D--char,
E--unsigned char,
F--short,
H--int,
I--unsigned int,
J--long,
K--unsigned long,
M--float,
N--double,
_N--bool,
....
PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代
4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前
5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。
int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”
void Test2() -----“?Test2@@YGXXZ”