Dll 导出 别人总结

Q:使用了extern "C" 定义的dll导出函数,函数名仍然被加上修饰符,导致动态链接时找不到对应函数 ?

A: 官方原文如下:
extern "C" merely ensures that the symbol is compatible with C code. __cdecl and __stdcall are separate directives that can be applied to fully decorated c++ functions
解决办法有二种:
1、Export as extern "C" ensuring that the __cdecl calling convention is used
2、Export via a .DEF file
================下面为别人总结的=================
有关DLL的问题现在资料很多,但是很多人写DLL时经常出现调用程序无法找到相关的导出函数的问题,这里主要的原因是DLL在声明时出的问题。
在这里主要有两个问题,一个是调用约定的问题,一个是函数名修饰的问题,而这两个问题又是相互影响的。
一:声明为:extern "C" int __declspec(dllexport)add(int x, int y);
这种声明是强制用C语言方式进行修饰,且用C的默认约定,即__cdecl方式。这种方式编译产生的DLL中有一个导出函数:add,不加任何修饰。
二:声明为:extern "C" int __declspec(dllexport) __stdcall add(int x, int y);
这种声明是强制用C语言方式进行修饰,且用stdcall约定,这种方式编译产生的DLL中有一个导出函数:_add@8,即前面有“_”,后面加了参数长。
三:声明为:int __declspec(dllexport) __stdcall add(int x, int y);
这种声明不强制用C语言方式进行修饰,但是用stdcall约定,这种方式编译产生的DLL中有一个导出函数:?add@@YGHHH@Z。这个名字很怪,后面的不好理解。
四:声明为:int __declspec(dllexport) __cdecl add(int x, int y);
这种声明是不强制用C语言修饰,且用cdecl约定,这种方式编译产生的DLL中有一个导出函数:?add@@YAHHH@Z,注意看,和第三种方有一点不同。

实验一:显式调用方式调用DLL中的add函数。
#include <stdio.h>
#include <windows.h>
typedef  int(_stdcall *lpAddFun)(int, int); //宏定义函数指针类型
int main(int argc, char *argv[])
{
HINSTANCE hDll; //DLL句柄 
lpAddFun addFun; //函数指针
hDll = LoadLibrary("1.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, "add");
if (addFun != NULL)
{
int result = addFun(2, 3);
printf("%d", result);
}
else
printf("No Function");
}
else
printf("NO DLL");
FreeLibrary(hDll);
return 0;
}
方式一:调用成功。另外三种方式全部出错
实验二:隐式调用DLL中的add函数
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"1.lib") 
extern "C" int __declspec(dllimport) add(int x, int y);//声明方式随着DLL中的声明方式改变
int main(int argc, char *argv[])
{
int result = add(2, 3);
printf("%d", result);
return 0;
}
方式一:调用成功。另外发现一个奇怪现象:在调用程序中
声明函数时extern "C" int __declspec(dllimport) add(int x, int y);
写作:extern "C" int __declspec(dllecprot) add(int x, int y);同样成功,将__declspec(…)去掉也同样成功。换句话说,在调用DLL的程序中,导入是没有必要加的。
方式二:调用成功。同样出现上面导入标识可以不加的现象。
方式三:调用成功,同样也出现上面导入标识可以不加的现象。
方式四:调用成功,同样也出现上面导入标识可以不加的现象。
总结:对于DLL导出函数声明的四种写法,在动态调用时,
声明成这样:extern "C" int __declspec(dllimport) add(int x, int y);是最好的,其它声明方式调用都没有成功。但是众所周知,windows默认的调用约定是stdcall方式,如果想别的语言能用DLL的话,最好是将调用约定写成stdcall方式,但是这种方式又不能动态调用。
在隐式调用时,四种声明方式都是可以的,只要调用者的声明方式和DLL声明时的方式一致即可。另外,在调用程序中对于导入的声明是可以去掉的,大量书籍中关于导入、导出的问题都是利用宏来处理的,如:在头文件中写作:
#ifdef DLL_FILE
extern "C" int __declspec(dllexport) add(int x, int y);
#else
extern "C" int __declspec(dlleximport) add(int x, int y);
这样这个头文件既可以用在DLL工程中,又可以用在调用程序中,但是经过实验发现,这个根本就没有必要,在调用者程序中不管是写作__declspce(dllexport)还是写作__declspec(dllimport)或者不写都能成功调用。
关于DEF文件
在DLL工程中引用DEF文件,内容如下:
LIBRARY 1
EXPORTS
add @ 1
通过depends查看导出函数全是add,但是隐式方式调用时,还是要求调用者的声明方式和DLL中声明方式相同。
对于动态调用实验结果:
方式一:成功。方式二:不成功,但是将函数指针改为typedef int(_stdcall *lpAddFun)(int, int);成功,即调用者要声明约定方式与DLL中声明的调用约定方式相同,否则报错。
方式三:同方式二,同样要将函数指针改为typedef int(_stdcall *lpAddFun)(int, int);才成功完成调用。
方式四:成功。
总结:通过DEF文件来导出函数,调用者同样也要声明相同的调用约定,即_stdcall或是_cdecl必须要相同,其中_cdecl是C语言默认方式。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值