动态链接库理解(二)

时间:2021-05-10 09:39:37

1、在(一)我们提到C++编译器在导出函数的时候,会对函数做名字改编,这样的话,我们用C++编写的dll,拿给C语言,或C#语言的客户端程序调用就会出问题。因为名字发生了改变,会找不到。

所以我们希望在输出函数名的时候,名字最好不要发生改变。但是怎么办呢?

(1)只需要将(一)中的Dll1.h中的

#define DLL1_API _declspec(dllimport)
变为
#define DLL1_API extern "C" _declspec(dllimport)

(2)将Dll1.cpp中的

#define DLL1_API _declspec(dllexport)

   变为

#define DLL1_API extern "C" _declspec(dllexport)

我们可以用dumpbin查看,名字有没有改变(dumpbin使用,参考(一))。

2、但是extern "C"不能导出一个类的成员函数,只能导出成员函数。并且在导出函数的调用约定发生变化的时候,即使用了extern "C"那么函数的名字也会发生改变,extern "C"只能用于C调用约定的时候,如果是_stdcall标准调用约定,有extern "C",函数名也会发生改变。那么问题来了,怎么办呢?

使用模块定义文件的方式解决这个名字改编的问题。具体步骤:

(1)新建一个dll工程Dll2,在工程目录下建立一个.def文件,将文件加入到工程中,工程--添加到工程--文件--.def文件

(2).def文件中写:

LIBRARYDll2//工程名

EXPORTS //更多EXPORTS功能参看MSDN

add //要导出的函数名

subtract //要导出的函数名

3、如何动态加载动态链接库?

动态加载的好处:节省资源

坏处:需要手动添加,如果加入很多的dll,则会特别麻烦。

<span style="white-space:pre"></span>HINSTANCE hInst;
hInst=LoadLibrary("Dll3.dll");//加载dll
typedef int (/*_stdcall*/*ADDPROC)(int a,int b);//定义函数指针
//_stdcall则说明函数的定义使用的是标准调用约定,所以在这也需要声明_stdcall
ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");
//得到导出函数的地址
//ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));//用序号访问
if(!Add){MessageBox("获取函数地址失败!");return;}
CString str;str.Format("5+3=%d",Add(5,3));
MessageBox(str);FreeLibrary(hInst);