文章转载自:http://blog.csdn.net/xiaoding133/article/details/8973436
以下以vs2013开发环境做出演示:
一、动态链接库的创建和引用
首先在vs2013中创建一个空的DLL1项目,添加DLL1.h与DLL.cpp
头文件DLL1.h内容如下:
#ifndef DLL1_H
#define DLL1_H
#ifdef BUILD_DLL
#define PORT_DLL __declspec(dllexport)
#else
#define PORT_DLL __declspec(dllimport)
#endif
int PORT_DLL add(int a, int b); int PORT_DLL subtract(int a,int b);
#endif // DLL1_H
源文件DLL.cpp内容如下:
#define BUILD_DLL
#include"DLL1.h"
PORT_DLL int add(int a,int b)
{
return a+b;
}
PORT_DLL int subtract(int a,int b)
{
return a-b;
}
编译之后Debug目录下会生成两个文件,分别为动态链接库DLL1.dll和动态链接库的引导库DLL1.lib。
使用Dependency walker分析工具查看.dll文件内容如下:
由于C++支持函数重载,对于重载的函数名是一样的,为了方便区别,在编译连接的时候,C++会按照自己的规则修改函数的名字,称为“名字编写”。
接下来在控制台程序中引用该项目,将DLL1.h拷贝到工程目录下,在工程目录下新建名为Bin和Lib的文件夹,Bin文件夹内存放.dll文件,Lib文件夹内存放引导库.lib文件。
在调用的程序直接包含该头文件#include”DLL1.h”即可。
#include <stdio.h>
#include"DLL1.h"
int main() {
int a = 2;
int b = 1;
printf("a=%d, b=%d\n", a,b);
printf("add: %d\n", add(a,b));
printf("subtract: %d\n", subtract(a,b));
getchar();
return 0;
}
直接编译会出现如下情况(error LNK 2019):
说明没有告诉编译器引导库.lib的位置,添加附加依赖项如下:
如果出现如下错误(error LNK 1104):
说明编译器没有找到附加依赖项的位置,需要在配置属性->VC++目录->库目录下添加.lib文件所在的文件夹。
另外也可以采用#pragma comment(lib,”dll1.lib”)的方式来引用链接库:
#include <stdio.h>
#include "DLL1.h"
#pragma comment(lib,"dll1.lib") //这行代码的作用相当于在链接器中输入附加依赖项
int main() {
int a = 2;
int b = 1;
printf("a=%d, b=%d\n", a, b);
printf("add: %d\n", add(a, b));
printf("subtract: %d\n", subtract(a, b));
Point pt;
printf("Point: %d\n", pt.Max(a, b));
getchar();
return 0;
}
DLL中导出C++类:
首先在DLL1.h的头文件中定义一个类:
#ifndef DLL1_H
#define DLL1_H
#ifdef BUILD_DLL
#define PORT_DLL _declspec(dllexport)
#else
#define PORT_DLL _declspec(dllimport)
#endif
int PORT_DLL add(int a, int b);
int PORT_DLL subtract(int a,int b);
class PORT_DLL Point
{
public:
int Max(int a,int b);
};
#endif // DLL1_H
在DLL1.CPP文件中定义函数:
#define BUILD_DLL
#include"DLL1.h"
PORT_DLL int add(int a,int b)
{
return a+b;
}
PORT_DLL int subtract(int a,int b)
{
return a-b;
}
int Point::Max(int a, int b)
{
return a>=b?a:b;
}
编译之后,将DLL1.lib,DLL1.dll和Dll1.h拷贝到当前项目下,然后进行调用:
#include <stdio.h>
#include "DLL1.h"
int main() {
int a = 2;
int b = 1;
printf("a=%d, b=%d\n", a,b);
printf("add: %d\n", add(a,b));
printf("subtract: %d\n", subtract(a,b));
Point pt;
printf("Point: %d\n",pt.Max(a,b));
getchar();
return 0;
}
导出一个类的某个函数:
class /*PORT_DLL*/ Point
{
public:
PORT_DLL int Max(int a,int b);
};
名字改编问题:
C++编译器在生成DLL时,会对导出的函数进行名字修改,并且不同的编译器使用的改编规则不同。如果利用不同的编译器分别生成DLL和访问该DLL的客户端程序的话,则访问DLL的导出函数就会出现问题。如果用C++语言编写了一个DLL,那么用C语言编写的客户端访问该DLL的函数就会出现问题。因为后者使用函数原始名称来调用DLL中的函数,而C++编译器已经对该名称进行了改编,所以C语言编写的客户端程序就找不到所需DLL中的函数。
对于C++ 与C中的兼容,我们希望动态链接库在编译的时候,导出函数的名称不要发生变化,可以在定义导出函数时候,加上extern “C”,C大写。如下:
#ifndef DLL1_H
#define DLL1_H
#ifdef BUILD_DLL
#define PORT_DLL extern"C" _declspec(dllexport)
#else
#define PORT_DLL extern"C" _declspec(dllimport)
#endif
PORT_DLL int add(int a, int b);
PORT_DLL int subtract(int a, int b);
PORT_DLL class Point
{
public:
int Max(int a, int b);
};
#endif // DLL1_H
extern “C”可以解决C++与C语言之间的相互调用的函数命名问题。但是有一个缺陷,就是不能导出一个类的成员函数。
如果导出函数的调用约定发生改变,即使使用了extern ”C“,该函数的名字仍然会发生改变。
下面采用标准调用约定,即在声明这些函数时添加_stdcall关键字;
头文件:
#ifndef DLL1_H
#define DLL1_H
#ifdef BUILD_DLL
#define PORT_DLL extern"C" _declspec(dllexport)
#else
#define PORT_DLL extern "C" _declspec(dllimport)
#endif
PORT_DLL int _stdcall add(int a, int b);
PORT_DLL int _stdcall subtract(int a,int b);
/*class PORT_DLL Point
{
public:
int Max(int a,int b);
};*/
#endif // DLL1_H
源文件:
#define BUILD_DLL
#include"DLL1.h"
PORT_DLL int _stdcall add(int a,int b)
{
return a+b;
}
PORT_DLL int _stdcall subtract(int a,int b)
{
return a-b;
}
/*int Point::Max(int a, int b)
{
return a>=b?a:b;
}*/
从上图可以看到,add函数的名字变为_add@8,在add前面有一个下划线,后面跟一个@,接着是数字8,该数字表示add函数的参数所占的字节数,因为有两个int 参数。
通过上图可以发现,如果函数的调用约定发生了变化,即使在声明这些导出函数时使用了extern “C”,它的名字仍然会发生变化。C语言和Delphi语言的调用约定不一样,后者使用pascal调用约定,即标准调用约定_stdcall.解决这种问题,可以定义一个模块定义文件(DEF)来解决名字改编问题。
使用DEF文件导出函数。首先创建一个新项目DLL2,在 DLL2.cpp中添加代码:
extern"C" int add(int a, int b)
{
return a + b;
}
extern"C" int subtract(int a, int b)
{
return a - b;
}
在该工程目录下,新建一个Dll2.DEF空文件,后缀名为def,然后通过下面方式加入到工程:项目——属性——连接器——输入——模块定义文件 中输入你所定义的def文件名
在Dll2.def加入如下的代码(如果想要导出的符号名和源文件中定义的函数名不一样,可以按照下面方法定义,比如要将add函数导出为myadd函数):
LIBRARY DLL2
EXPORTS
myadd=add
subtract
使用DEF文件来导出函数不需要使用_stdcall 和 _declspec(dllexport)
二、静态链接库的创建与使用
首先在vs2013中创建一个静态库项目,取消预编译头,新建一个头文件add.h和源文件add.cpp
头文件
#ifndef ADD_H
#define ADD_H
extern"C" int add(int a, int b);
#endif // ADD_H
源文件
#include "add.h"
int add(int a, int b) {
return a + b;
}
然后编译,在Debug目录下生成LIB1.lib
引用静态链接库
方法一:在当前解决方案下再添加一个项目并设置为启动项,调用代码如下:
#include "../LIB1/add.h"
#include <stdio.h>
extern int add(int a, int b);
int main() {
int a = 2;
int b = 1;
printf("a=%d, b=%d\n", a,b);
printf("add: %d\n", add(a,b));
getchar();
return 0;
}
项目->属性->通用属性->引用->添加新引用:
方法二:将add.lib拖放到项目的资源文件下
方法三:在属性中设置库目录,添加附加依赖项
方法四:在属性中设置库目录,使用#pragma comment( lib, “add.lib”)