C语言dll文件的说明以及生成、使用方法

  最近在搞一些小项目,因为要涉及到跟其它语言进行交互,动态连接库变成了不二的选择。为此也查阅了不少资料,将动态连接库的相关知识在此作一个整理。ios

  1、动态连接库概述编程

 

  动态连接库(Dynamic Link Library )是一种不可执行的二进制程序文件,它容许多个程序共享执行特殊任务所必需的代码和其余资源。Windows 中,DLL 多数状况下是带有 ".dll" 扩展名的文件,但也多是 ".ocx"或其余扩展名;Linux系统中经常是 ".so" 的文件。动态连接提供了一种方法,使进程能够调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、连接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。使用动态连接库能够更为容易地将更新应用于各个模块,而不会影响该程序的其余部分。是开发大型项目必不可少的部分。windows

  2、优缺点模块化

  优势:(1)节省内存和代码重用:当多个程序使用同一个函数库时,DLL能够减小在磁盘和物理内存中加载代码的重复量,且有助于代码的重用。函数

 

     (2)模块化:DLL有助于促进模块式程序开发。模块化容许仅仅更改几个应用程序共享使用的一个DLL中的代码和数据而不须要更改应用程序自身。适用于大规模的软件开发,使开发过程独立、耦合度小,便于不一样开发者和开发组织之间进行开发和测试。这种模块化的基本形式容许如Microsoft Office、Microsoft Visual Studio、甚至windows自身这样大的应用程序使用较为紧凑的补丁和服务包。测试

       (3)扩展了应用程序的特性,使用dll文件可使得应用程序能很方便的进行功能的扩展,不少程序的插件机制就是经过dll文件实现的。spa

     (4)能够用多种语言来编译和调用,因为各类语言都有本身独特的开发优点,在处理某类事务方面具备着独特的优点,因此在多种语言编程的过程当中,能够利用dll文件做为桥梁,能够发挥多种语言的优势。插件

 

  缺点:DLL Hell:即DLL地狱,指几个应用程序在使用同一个共享的DLL库时发生版本冲突。线程

 

  究其缘由,八个字:成也共用,败也共用。由于DLL Hell正是因为动态连接库可与其余程序共用函数、资源所致使。debug

 

  主要有两种状况

 

  设想这样一个场景:程序A会使用1.0版本的动态连接库X,则在程序A安装到系统时,会同时安装该1.0版本的动态连接库X。假设另外一个程序B也会使用到动态连接库X,那么程序B直接复制到硬盘中便可正常运行,由于动态连接库已经存在于系统中。然而有一天,另外一程序C也要使用动态连接库X,可是因为程序C开发的时间较晚,其须要较新版本---2.0版本的动态连接库X。则在程序C被安装到系统时,2.0版本的动态连接库X 也必须随之安装到系统中,此时系统中1.0版本的动态连接库将被2.0版本所取代(替换)。

 

  状况1:新版本的动态连接库不兼容旧版本。如,A何B须要X所提供的功能,在升级到2.0后,新版本的X居然把此功能取消了(很难想象吧,呵呵但有时候就是如此....)。则此时虽然C能正常运行,但A和B均没法工做了。

 

  状况2:新版本的动态连接库兼容旧版本,可是存在一个bug。

  3、入口点

  就跟应用程序的main函数同样,dll文件也有入口函数,叫作DllMain(),它的原型是这样的:

 1 BOOL APIENTRY DllMain(
 2     HANDLE hModule,             // DLL模块的句柄
 3     DWORD ul_reason_for_call,   // 调用本函数的缘由
 4     LPVOID lpReserved           // 保留
 5 ) {
 6     switch (ul_reason_for_call)
 7     {
 8         case DLL_PROCESS_ATTACH:
 9             //进程正在加载本DLL
10         break;
11         case DLL_THREAD_ATTACH:
12             //一个线程被建立
13         break;
14         case DLL_THREAD_DETACH:
15             //一个线程正常退出
16         break;
17         case DLL_PROCESS_DETACH:
18             //进程正在卸载本DLL
19         break;
20     }
21     return TRUE;            //返回TRUE,表示成功执行本函数
22 }

  入口点函数只应执行简单的初始化任务,不该调用任何其余 DLL 加载函数或终止函数。例如,在入口点函数中,不该直接或间接调用 LoadLibrary 函数或LoadLibraryEx 函数。此外,不该在进程终止时调用 FreeLibrary函数。

  4、生成DLL文件

  下面来生成一个DLL文件,为方便起见,只定义一个简单函数。

  生成DLL文件须要用到两个文件,一个头文件,dll_add.h,和一个源文件,dll_add.c

  头文件内容:

1 #ifndef _DLL_DEMO_H_
2 #define _DLL_DEMO_H_
3 #ifdef DLLDEMO_EXPORTS
4 #define DLL_DEMO _declspec( dllexport )
5 #else
6 #define DLL_DEMO _declspec(dllimport)
7 #endif
8 extern "C" DLL_DEMO int Add(int a, int b);
9 #endif

  源文件内容:

1 #include "dll_demo.h"
2 
3 int Add(int a, int b)
4 {
5     return (a + b);
6 }

  这里由于不须要对函数载入与卸载做特殊处理,因此能够不使用入口函数。

  使用的是vs2015,在debug模式或者release模式下调试后会在相应目录下生成dll文件,便可使用。

  5、调用DLL文件

  生成DLL天然是为了调用,调用DLL有两种方式。

  静态调用:使用.h+.lib+.dll

  

 1 #include <windows.h>
 2 #include <iostream>
 3 #include "DLL_DEMO.h"
 4 using namespace std;
 5 #pragma comment(lib, "DLL_DEMO.lib")
 6 
 7 extern "C" _declspec(dllimport) int Add(int a, int b);
 8 int main(int argc, char *argv[])
 9 {
10     cout << Add(2, 3) << endl;
11     system("pause");
12     return 0;
13 }

  把头文件和lib文件、dll文件都放到跟源文件同一目录下便可使用。固然,路径能够从新设置。

  动态调用:仅使用dll文件

 1 #include <windows.h>
 2 #include <iostream>
 3 using namespace std;
 4 typedef int (*AddFunc)(int a, int b);
 5 int main(int argc, char *argv[])
 6 {
 7       HMODULE hDll = LoadLibrary(L"DLL_DEMO.dll");
 8       if (hDll != NULL)
 9       {
10             AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
11             if (add != NULL)
12             {
13                   cout<<add(2, 3)<<endl;
14             }
15             FreeLibrary(hDll);
16       }
17 }

  在字符串前加一个L做用: unicode字符集是两个字节组成的。L告示编译器使用两个字节的 unicode 字符集。

  也可使用dll来实现类和变量的共享,还能够实现内存共享,由于研究很少,因此这里暂不介绍。

相关文章
相关标签/搜索