c++动态库封装及调用(2、windows下动态库创建)

DLL即动态链接库(Dynamic-Link Libaray)的缩写,相当于Linux下的共享对象。Windows系统中大量采用了DLL机制,甚至内核的结构很大程度依赖与DLL机制。Windows下的DLL文件和EXE文件实际上是一个概念,都是PE格式的二进制文件。一般的动态库程序有lib文件和dll文件,lib文件是编译时期连接到应用程序中的,而dll文件是运行时才会被调用的。

为了更好的理解DLL,首先介绍一下导出和导入的概念。

(1)导出与导入

在ELF(Linux下动态库的格式),共享库中所有的全局函数和变量在默认情况下都可以被其他模块使用,即ELF默认导出所有的全局符号。DLL不同,需要显式地“告诉”编译器需要导出某个符号,否则编译器默认所有的符号都不导出。

程序使用DLL的过程其实是引用DLL中导出函数和符号的过程,即导入过程。对于从其他DLL导入的符号,需要使用“__declspec(dllimport)”显式声明某个符号为导入符号。在ELF中,使用外部符号时,不需要额外声明该符号是从其他共享对象导入的。

指定符号的导入导出一般有如下两种方法:

1)MSVC编译器提供了一系列C/C++的扩展来指定符号的导入导出,即__declspec属性关键字

__declspec(dllexport) 表示该符号是从本DLL导出的符号

__declspec(dllimport) 表示该符号是从别的DLL中导入的

2)使用“.def”文件来声明导入到导出符号,详细参考《程序员的自我修养--链接、装载与库》。

应用程序使用DLL可以采用两种方式:一种是隐式链接(调用),另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息

4、DLL创建

下面是头文件内容:创建工程时有默认的导出函数,这里将其删除掉重新写的。

4、DLL创建

以下是vs生成的代码和我添加的测试导出函数  

  1 // 下列 ifdef 块是创建使从 DLL 导出更简单的  
  2 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 MYDLL_EXPORTS  
  3 // 符号编译的。在使用此 DLL 的  
  4 // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将  
  5 // MYDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的  
  6 // 符号视为是被导出的。  
  7 #ifdef MYDLL_EXPORTS  
  8 #define MYDLL_API __declspec(dllexport)  
  9 #else  
 10 #define MYDLL_API __declspec(dllimport)  
 11 #endif  
 12   
 13 
 14 class MYDLL_API CmyDll{
 15 public:
 16        CmyDll(void);     
 17        //TODO:    在此添加您的方法
 18   
 19 }
 20 
 21 extern MYDLL_API int nmyDll;
 22 
 23 MYDLL_API int fnmyDll(void);
 24 
 25 
 26 //--------myDll.cpp文件代码如下-----------------
 27 //    myDll.cpp   : 定义DLL应用程序的导出函数。
 28 
 29 #include "stdafx.h"
 30 #include "myDll.h"
 31   
 32 //这是导出变量的一个示例
 33 MYDLL_API int nmyDll = 0;
 34 
 35 //这是导出函数的一个示例
 36 MYDLL_API int fnmyDll(void)
 37 {
 38        return 42;
 39 }
 40 
 41 //这是已导出类的构造函数
 42 //有关类定义的信息,请参阅myDll.h
 43 CmyDll::CmyDll()
 44 {
 45        return;
 46 }
 47 
 48 
 49 //------------------------------------------------
 50 //-----------以上是通过vs2015生成的原始代码
 51 
 52 
 53 //----------------以下是直接在生成的源码上修改的版本
 54 
 55 //---------------------方式一
 56 
 57 class MYDLL_API CmyDll{
 58 public:
 59        CmyDll(void);     
 60        //TODO:    在此添加您的方法
 61 
 62       //第一种方式在类里面导出函数
 63       int Add(int x, int y);  
 64 }
 65 
 66 extern MYDLL_API int nmyDll;
 67 
 68 MYDLL_API int fnmyDll(void);
 69 
 70 //----------------方式二 通过extern 导出函数
 71 
 72 class MYDLL_API CmyDll{
 73 public:
 74        CmyDll(void);     
 75        //TODO:    在此添加您的方法
 76 }
 77 
 78 extern MYDLL_API int nmyDll;
 79 
 80 MYDLL_API int fnmyDll(void);
 81 
 82 
 83 //第一种添加extern "C"
 84 extern "C" int Add(int x, int y);
 85 
 86 //第二种情况为当有很多函数导出时,为省略extern “C”,可以将导出函数包括在{}内
 87 extern "C"
 88 {
 89         int MYDLL_API Add(int x, int y);
 90 }
 91 
 92 
 93 //--------此时相对应的myDll.cpp文件代码如下-----------------
 94 //    myDll.cpp   : 定义DLL应用程序的导出函数。
 95 
 96 #include "stdafx.h"
 97 #include "myDll.h"
 98   
 99 //这是导出变量的一个示例
100 MYDLL_API int nmyDll = 0;
101 
102 //这是导出函数的一个示例
103 MYDLL_API int fnmyDll(void)
104 {
105        return 42;
106 }
107 
108 //这是已导出类的构造函数
109 //有关类定义的信息,请参阅myDll.h
110 CmyDll::CmyDll()
111 {
112        return;
113 }
114 
115 //通过类导出,
116 int CmyDll::Add(int x, int y)
117 {
118        return x+y;
119 }
120 
121 //通过extern导出,此时方式一和方式二都是如此,
122 int Add(int x, int y)
123 {
124        return x+y;
125 }
126 
127 //函数实现上,前面都不需要添加MYDLL_API

这里将Add函数声明为导出函数;

当定义了符号MYDLL_EXPORTS,MYDLL_API被设置为 __declspec(dllexport)修饰符,This modifier enables the function to be exported by the DLL so that it can be used by other applications。若未定义则TMYDLL_API被设置为__declspec(dllimport),This modifier enables the compiler to optimize the importing of the function from the DLL for use in other applications。当DLL项目生成时,MYDLL_EXPORTS默认是定义的,所以默认设置的是__declspec(dllexport) 修饰符。 

然后编译就会生成对应的dll文件,同时也会生成对应的lib文件。 

注意:a.DLL中导出函数的声明有两种方式:在函数声明中加上__declspec(dllexport);采用模块定义(.def)文件声明。详见:http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html 
  b.对于C文件创建dll时或者想使用C编译器创建dll时,建议使用 extern “C” 标志,参见extern "C"的简单解析。 

下一次讲如何调用dll导出的函数

 
原文地址:https://www.cnblogs.com/marblemm/p/7803946.html