关于DLL的参数传递方式的问题(100分)

  • 主题发起人 主题发起人 O_O
  • 开始时间 开始时间
O

O_O

Unregistered / Unconfirmed
GUEST, unregistred user!
我在改造原来编的DLL中发现以下的问题:
原来我用的是BC编的,用的是C++语法,改到VC下后,当还是用C++的语法,
生成的DLL,我的Delphi无法调用,改为纯C编后,当用cdecl的参数传递方
式,问题解决,但不能用stdcall,不知为何原来可以(在BC下),看了帮助后
不得要领,它只是解释了参数在堆栈中由谁释放,因为VC不熟,不知是不是有
些编译开关要设置?另我所谓的用到C++的语法,也只是变量声明的方法,不知
VC对C/C++语法的编译开关在哪?难道只是根据.c或.cpp文件来判断的吗?
 
cdecl? 是不是大小写敏感的那种?
stdcall 好象是调用PASCAL型函数的(大小写不敏感, 即编译时全转成大写的).
估计你的c函数没指定类型(默认是大小写敏感的).
 
DLEPHI调用VC写的DLL,VC的输出函数应该用下面的形式:
#define DllExport __declspec(dllexport)
DllExport ReturnType WINAPI FunctionName(Arguments Lists);
并且在.DEF文件中Exports部分将要输出的函数名加上(否则编译器自动在输出函数名前面将加上"@"或"_",你可以用VC附带的DUMPBIN。EXE工具看DLL的输出函数名 DumpBin -exports YourDll.dll),如下:
//Your .DEF file
Exports
FunctionName
通过以上步骤,DELPHI的STDCALL才可以调用VC写的DLL(但是VC的EXTENSION型 DLL只可被VC的程序调用)
Good luck!
 
C++在处理函数调用时采用C方式,就是由函数自己清堆栈中的调用参数,
采用ADD ESP,n 的做法,n的大小由压入堆栈中的参数个数决定。
DELPHI的函数采用PASCAL调用方式,有函数调用者清堆栈。
所以象你所说的情况就形成了两次清堆栈,造成了堆栈指针错误。
最简单且保守的解决方法就是,在调用完函数后立刻用嵌入汇编
调整堆栈指针(采用SUB ESP,n的方法,n的大小由参数个数决定
比如如果参数为两个整数,那么n=8)。我就这么干过。:)
 
补充:在VC++中,如果把类的成员函数作为DLL的
输出函数,即使用DllExport说明该函数也不会
是PASCAL调用方式。
 
我想大家没明白我的意思,我举个小例子说明一下,
下面是一小段dll代码:
#include <windows.h>
int __declspec(dllexport) Test(int index);
BOOL APIENTRY DllEntryPoint(HANDLE hModule,
DWORD fdwReason,
LPVOID lpReserved )
{
return TRUE;
}
int Test(int index)
{
return index;
}
当我这代码存为.cpp文件后,生成的dll,用tdump查看exported name时发现输出
为?Test@@YAHH@Z(怪里怪气的),然后用delphi调用,死活调不进来.
而同样的代码另存为.c文件后,生成的dll,输出的函数为Test;这时用delphi来调,
只能用cdecl来声明,这时一切OK,用stdcall声明,一执行机器就死翘翘了.
我想问如下的几个问题:
1.在VC中是如何判断按C++还是标准C的语法来编译的?在BC中是有选项的,VC我找
不到,难道就是根据文件扩展名吗?
2.为何二种情况,输出的函数名不一样,有何区别?在第一种情况下调用要怎样才
行?是不是编译生成dll的问题?因为我用到别人的标准dll,输出的名字也是那样
奇怪的,但可以调进来.
3.对于第二种情况为何只能用cdecl来声明函数的原型?有什么讲究吗?
 
huhuhu我写的时候没看到你的回答,现有一些问题请教:
在VC中如何使DLL的输出函数是pascal调用规范?
用dllexport输出的是C方式还是PASCAL方式?它和原来的extern "C"有何区别?
 
应该是如下形式:
__declspec(dllexport) int WINAPI Test(int index);
并且应在.DEF文件中EXPORTS部分声名:
Test
另外
extern "C"{}
是在CPP中避免Name mangling 技术,也就是避免为Test生成为?Test@@YAHH@Z
这种名字的
 
我在原CPP文件中将输出函数声明改为如下:
extern "C"{
int __declspec(dllexport) Test(int index);
};
使其按C的方式输出,效果和C文件一样了,看来我原来理解错了.
这样就是说,如果在VC中编DLL,输出方式要按这种方式声明,并且在Delphi中
要用cdecl的方式来调用才行?
另:看VC的帮助,说用 __declspec(dllexport)声明后,不用再在.DEF文件中申明
了.
 

用 __declspec(dllexport)声明后,如果不用WINAPI声名,则不用在。DEF
中声名,如果用WINAPI声名,则函数名前会加下划线“_”,且参数顺序为
stdcall。
 
弄了半天总算搞清楚了这些关键字的意思了,谢谢大家了
 
本人对VC比较熟悉,平时习惯于在VC下写DLL,有Delphi调用。
本人认为这是比较好的用法,可充分发挥两种语言的优点。
在这里将本人摸索出来的经验加以介绍。
1.VC 中使用宏__cplusplus来区分使用c++还是c.
如果文件后缀不为c,默认为c格式。c++格式对所有函数要进行换名,因为c++
允许重载。
2.VC从DLL中输出函数有两种格式,即使用__declspec(dllexport)或者def文件。
这两种声明方法在VC中等价,即c++格式的函数使用def文件后输出的名字同样
还是怪名字,och.ocx的介绍好象不对。可以通过在c++中定义extern "C"来解决
这问题。较好用法如下:
#ifdef __cplusplus
extern "C" {
#endif
//here is declare of function to export
#ifdef __cplusplus
};
还有一种解决方案是在def文件中输出索引号,在Delphi中用索引号调用。如:
int __stdcall Text(int x)
{
return x;
}
在def文件中写上:
LIBRARY
EXPORTS
Text @ 1
然后在Delphi中通过:
function Text(x:integer):integer;stdcall;
external DLLNAME;index 1;
调用。
#endif
3.c/c++中函数默认调用格式为cdecl(c中区分大小写),在Dll中输出函数时,最好
将它声明为Windows下的标准调用格式,如:
int __stdcall Text(int x)
Microsoft已经重新定义了宏STDCALL和WINAPI,所以可以用它们替换__stdcall.
在Delphi中调用此函数时,要加上stdcall;的声明。
 
tdump 是个什么东东?适用于什么场合?
dumpbin.exe我的vc6怎么没有?它是不是只能查看vc写的dll?
 
后退
顶部