vs2017 linux so导出函数,VS2017 动态链接库的制作及调用---从动态链接库中导出函数和类并调用...

参考文献: 孙鑫. VC++深入详解.修订版[M]. 电子工业出版社, 2012. 第19章

Step#1: 新建动态链接库文件

文件->新建->项目->选择Visual C+±>Windows桌面->动态链接库

名称DLL-1。新建成功之后, 平台设置为Debug x64平台。

d66a699db2785790426ca00d1b5ef185.png

默认生成以下文件:

41b3dabe4914da875c175f7a3e0f130a.png

在Dll-1.cpp中输入如下代码:

#include “stdafx.h”

int add(int a, int b)

{

return a + b;

}

int subtract(int a, int b)

{

return a - b;

}

点击: 生成→生成解决方案

959c1ed70c8769a44a02217562d7e5c1.png

显示输出结果: 生成:成功1个;

815580540930488392230d38a2463e80.png

在E:\1-C++\Dll-1\x64\Debug中可以看到生成的Dll-1.dll文件。

86b1830e019242ac6867b5cdc59a9ddf.png

应用程序如果想要访问某个DLL中的函数,那么该函数必须是已经被导出的函数,为了查看一个DLL中有哪些导出函数,可以利用Visual Studio提供的命令行工具:Dumpbin来实现,如何实现可参考文献:https://blog.csdn.net/tpz789/article/details/89635334 。

利用Dumpbin命令查看Dll-1.dll的信息,发现没有任何与函数有关的信息,这说明Dll-1.dll中没有导出函数。如下图。

9eb31ce248ff2634dfe74a4a42f41e84.png

为了让DLL导出一些函数,需要在每一个将要被导出的函数前面添加标识符:_declspec(dllexport)。于是修改dll-1.cpp文件中的代码,在add函数和subtract函数的定义前面加上_declspec(dllexport)。

// Dll-1.cpp : 定义 DLL 应用程序的导出函数。

#include “stdafx.h”

_declspec(dllexport) int add(int a, int b)

{

return a + b;

}

_declspec(dllexport) int subtract(int a, int b)

{

return a - b;

}

重新生成Dll-1动态链接库,输出窗口中显示如下信息:

40953a78760b4a00a7b8068edfb90185.png

可以看到,这时又生成了两个新文件,其中Dll-1.lib文件是引入库文件,在文件中保存的是Dll-1.dll中导出的函数和变量的符号名;Dll.exp文件是一个输出库文件,这里,该文件并不重要。

然后,再次利用Dumpbin命令查看Dll-1.dll导出函数的信息,结果如下图所示。

ff344192c880a0d3d507d7d7005a0ac6.png

可以看到,这时多了一些输出信息,其中有这么一段信息:

421f6a823d2f1ab41ef161b34059fda0.png

这就是导出函数的信息。具体含义参考 《孙鑫. VC++深入详解.修订版[M]. 电子工业出版社, 2012. 第19章》

Step#2: 测试Dll-1.dll

新建一个空项目:文件->新建->项目

名称:Dll-1-test

b12c205969deb6edd9a085e408c763db.png

平台选择Debug x64

新建一个源文件Dll-1-test.cpp,在其中输入如下代码:

#include #include

using namespace std;

extern int add(int a, int b); //利用extern声明外部函数;

extern int subtract(int a, int b); //利用extern声明外部函数;

void main() {

int x = 3;

int y = 6;

int m = add(x, y);

int n = subtract(x, y);

cout << “m:” << m << endl;

cout << “n:” << n << endl;

system(“PAUSE”);

}

生成→生成解决方案

3265827eb493131aa976e122bb27ac9d.png

失败1个,这时会出现三个错误:

136abec6fee738febec26226f419704c.png

d41dd3db681df8ead24b17b154826b5d.png

可以看到,Dll-1-test程序编译成功通过,产生的三个错误是在程序链接时发生的。因为这里调用的add和subtract函数都已经作了声明,所以编译可以通过。当链接时,链接器需要知道这两个函数到底是在哪个地方实现的,它要找到这两个函数的实现。正因为没有找到该信息,所以链接时就出错了。

为了解决这个问题,就需要利用动态链接库的引入库文件了。在Dll-1.dll文件所在目录下,复制Dll-1.lib文件到Dll-1-test程序所在目录下,这个文件中就包含了Dll-1.dll中导出函数的符号名。然后在Dll-1程序中进行链接器的属性配置。在Debug|64文件夹下,双击Microsoft.Cpp.x64.user,在链接器→常规→附加库目录中输入Dll-1.lib的路径:E:\1-C++\Dll-1-test\Dll-1-test,在链接器→输入→附加依赖项中输入Dll-1.lib

3af0c15db915f64117fd473abf330ad1.png

dc8dd2f3de42bd335d357990db51fb1c.png

c21526fa0d433e844282d7bd4b5589d6.png

重新生成解决方案:

e19f22c9b8bd62792f3dc785b457c704.png

这时会成功生成Dll-1.exe文件。也就是说,当应用程序需要调用某个动态链接库提供的函数时,在程序链接时只需要包含该动态链接库提供的输入库文件就可以了。

92dfc0ca0ce9c9659fe7d58f36d0e36f.png

此时也可以利用Dumpbin命令查看可执行程序的输入信息,参考文献:https://blog.csdn.net/tpz789/article/details/89635968

回到E:\1-C++\Dll-1-test\x64\Debug,可以看到生成的Dll-1.exe文件,

67be7a9cfe78fca57f99932291ebfcd4.png

双击运行。

77167976e0fa342b095e1e6ede4c795c.png

弹出错误信息,提示在指定的路径下无法找到动态链接库Dll-1.dll文件。

动态链接库的搜索顺序为:

①程序的执行目录:

本例子的执行目录为:E:\1-C++\Dll-1-test\x64\Debug

②当前目录

例如将Dll-1-test.exe拷到一个新创建的文件夹中,这个新文件夹就是当前目录。

③系统目录

依次是:C:\Windows\System32、C:\Windows\SysWOW64

④path环境变量中所列出的路径

本例中将Dll-1.dll文件复制到执行目录,双击Dll-1-test.exe,执行成功。

f9574ad138d993aa8976bf6f40a1f3e7.png

Step#3: 利用_declspec(dllimport)声明外部函数

除了使用extern关键字表明函数时外部定义的之外,还可以使用标识符:_declspec(dllimport)来表明函数是从动态链接库中引入的。在Dll-1-test程序中,将extern的两个函数注释起来,然后在其后添加如下代码:

_declspec(dllimport) int add(int a, int b);//表明函数是从动态链接库中引入的

_declspec(dllimport) int subtract(int a, int b);//表明函数是从动态链接库中引入的

重新Build,最后发现程序运行时一样的。

如果调用的函数来自于动态链接库,则与使用extern关键字相比,使用_declspec(dllimport)标识符声明外部函数时,编译器可以生产运行效率更高的代码。

Step#4: 完善Dll-1,利用头文件

对上述Dll-1-test的例子来说,因为该程序使用的动态链接库Dll-1.dll是我们自己编写的,所以我们清楚该Dll-1.dll中的导出函数。如果DLL程序的实现者和使用者不是同一个人,那么后者只能通过一些工具来查看该DLL提供的导出函数,并猜测这些函数的原型。这种做法对DLL的调用不是很方便。通常在编写动态链接库时,都会提供一个头文件,在此文件中提供DLL导出函数原型的声明,以及函数的有关注释文档。

接下来,我们就为Dll-1工程添加一个头文件:Dll-1.h,并在其中添加如下的代码:

_declspec(dllimport) int add(int a, int b);

_declspec(dllimport) int subtract(int a, int b)

下面,对Dll-1.h进行改造,使其不仅能够为调用动态链接库的客户端程序服务,同时也能够由动态链接库程序本身来使用。改造后的Dll-1.h文件内容如下所示:

#ifdef Dll_1_API

#else

#define Dll_1_API _declspec(dllimport)

#endif

Dll_1_API int add(int a, int b);

Dll_1_API int subtract(int a, int b);

在该文件中,首先使用条件编译指令判断是否定义了DLL-1-API符号,如果已经定义了该符号,则不作任何处理;否则定义该符号,将其定义为:_declspec(dllimport)。然后使用所定义的DLL-1-API宏来代替add函数和subtract函数声明前面的_declspec(dllimport)标识符。

接下来,在动态链接库的源程序:Dll-1.cpp文件中,首先利用#define指令定义DLL-1-API宏,然后利用#include指令包含Dll-1.h头文件。之后,在定义add和subtract函数时不再需要指定declspec(dllexport)标识符了,所以将其删除,这时的Dll-1.h.cpp文件内容如下所示:

#include “stdafx.h”

#define Dll_1_API _declspec(dllexport)

#include “Dll-1.h”

int add(int a, int b)

{

return a + b;

}

int subtract(int a, int b)

{

return a - b;

}

在程序编译时,头文件不参与编译,源文件单独编译。因此,在编译Dll-1.cpp文件时,首先定义DLL-1-API宏,将其定义为:_declspec(dllexport)。然后包含Dll-1.h这一头文件,这将展开该头文件。展开之后,首先判断DLL-1-API是否已经定义了。因为这时已经定义了这个宏,所以不再定义该宏,直接编译其后的add和subtract函数的声明。因为在声明这两个函数时,都使用了DLL-1-API宏,而且这时该宏的定义是:_declspec(dllexport),表明这两个函数是动态链接库的导出函数。

之后,将这个DLL交由其他程序使用时,只要后者没有定义DLL-1-API宏,那么该宏的定义就是:_declspec(dllimport),即add函数和subtract函数是导入函数。通过上述方法,Dll-1.h这个头文件既可以由实现DLL的程序使用,也可以由调用该DLL的客户端程序使用。

重新生成Dll-1.lib和Dll-1.dll文件,将Dll-1.dll复制到E:\1-C++\Dll-1-test\x64\Debug文件夹下,将Dll-1.lib和Dll-1.h复制到工程根目录下E:\1-C++\Dll-1-test\Dll-1-test,另外再将通用属性配置一下,即附加库目录和附加依赖项。

修改Dll-1-test.cpp,代码如下:

#include #include

#include “Dll-1.h”

using namespace std;

void main() {

int x = 3;

int y = 6;

int m = add(x, y);

int n = subtract(x, y);

cout << “m:” << m << endl;

cout << “n:” << n << endl;

system(“PAUSE”);

}

运行成功,结果也是正确的。

Step#5: 从DLL中导出C++类

在一个动态链接库中还可以导出一个C++类。为了实现这样的功能,仍以Dll-1为例,首先打开Dll-1工程,然后在Dll-1.h文件中添加如下代码:

#ifdef Dll_1_API

#else

#define Dll_1_API _declspec(dllimport)

#endif

Dll_1_API int add(int a, int b);

Dll_1_API int subtract(int a, int b);

class Dll_1_API Point {

public:

void output(int x, int y);

};

在Dll-1.cpp中添加如下代码:

#include “stdafx.h”

#include

#define Dll_1_API _declspec(dllexport)

#include “Dll-1.h”

int add(int a, int b)

{

return a + b;

}

int subtract(int a, int b)

{

return a - b;

}

void Point::output(int x, int y) {

std::cout << x << y << std::endl;

}

重新生成解决方案,成功。然后将Dll-1.dll复制到E:\1-C++\Dll-1-test\x64\Debug中,

将Dll-1.lib、Dll-1.h复制到E:\1-C++\Dll-1-test\Dll-1-test中,然后配置属性,再将Dll-1.h添加到Dll-1-test项目中(也可以不添加)。

修改Dll-1-test.cpp的代码为:

#include #include

#include “Dll-1.h”

using namespace std;

void main() {

int x = 3;

int y = 6;

int m = add(x, y);

int n = subtract(x, y);

cout << “m:” << m << endl;

cout << “n:” << n << endl;

Point pt;

pt.output(5, 3);

system(“PAUSE”);

}

运行成功。

5f67b4a02afb3b6502c5a3a6726df012.png

此外,也可以从类中导出部分函数,例如:在Dll-1.h中将Point类再添加一个成员函数:test

class Point {

public:

void Dll_1_API output(int x, int y);

void test();

};

在Dll-1.cpp中添加test函数的实现,该函数不作任何处理。

void Point::test()

{

}

然后生成Dll-1.dll,利用Dumpbin命令的exports选项查看最新的Dll-1.dll的导出信息。

5094a31139c6566d9a4c41d3a67ea8f2.png

可以看到,对Point类来说,Dll-1.dll仅导出了它的Output成员函数。

重新编译运行,最后的结果是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值