最近项目中使用到了DLL,因此就把最近一段时间的学习总结一下,以备不时之需。

一、相关概念

1、动态链接库

自从微软推出第一个版本的Windows操作系统以来,动态链接库(DLL)一直是Windows操作系统的基础。动态链接库通常都不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其它DLL调用来完成某项工作的函数。只有在其它模块调用动态链接库中的函数时,它才发挥作用。WindowsAPI中的所有函数都包含在DLL中。其中有3个最重要的DLL,Kernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。

2、静态库和动态库

静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)。在使用动态库的时候,往往提供两个文件:一个引入库和一个DLL。引入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL,访问DLL中导出的函数。

3、使用动态链接库的好处

可以采用多种编程语言来编写。
增强产品的功能。
提供二次开发的平台。
简化项目管理。
可以节省磁盘空间和内存。
有助于资源的共享。
有助于实现应用程序的本地化。

4、动态链接库被多个进程访问

5、动态链接库加载的两种方式

隐式链接
显示加载

二、动态链接库的使用

1、头文件内容

在头文件的最前面加上一下这样一段代码

/** dll.h */
/** 方法一 */
#ifdef DLL_EXPORT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif // DLL_EXPORT /** 方法二 */
#ifdef DLL_EXPORT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API
#endif // DLL_EXPORT

方法一的意思是根据是否定义DLL_EXPORT而将DLL_API分别定义为__declspec(dllexport)或是__declspec(dllimport) ,用来表示DLL_API修饰的部分是导入还是导出。因为DLL的头文件在编译DLL的时候需要

声明导出方式,当用户使用已发布的DLL链接库的时候,DLL的头文件应该作为导入方式使用,因此在编译DLL库的时候,需要在#include “dll.h”的前面加入一句#define DLL_EXPORT,而动态库的使用方则

不需要任何改变。

方法二将DLL_API定义为空,也可以,但是当导出类的静态对象的时候,后出现问题,因此推荐第一种方法。

2、声明导出函数

函数导出有两种方式,第一种使用__declspec(dllexport)声明导出函数,第二种使用使用.def文件声明。

  1. 使用 .DEF 文件的优缺点
  • 在 .DEF 文件中导出函数使您得以控制导出序号。当将附加的导出函数添加到 DLL 时,可以给它们分配更高的序号值(高于任何其他导出函数)。当您进行此操作时,使用隐式链接的应用程序

不必与包含新函数的新导入库重新链接。这非常重要,例如,在设计将由许多应用程序使用的第三方 DLL 时。可以通过添加附加的功能不断地增强 DLL,同时确保现有应用程序继续正常使用新的

DLL。MFC DLL 是用 .DEF 文件生成的。

  • 使用 .DEF 文件的另一个优点是:可以使用 NONAME 属性导出函数,该属性仅将序号放到 DLL 的导出表中。对具有大量导出函数的 DLL,使用 NONAME 属性可以减小 DLL 文件的大小。有关

编写模块定义语句的信息,请参见模块定义语句的规则。有关序号导出的更多信息,请参见按序号而不是按名称从 DLL 导出函数。

  • 使用 .DEF 文件的主要缺点是:在 C++ 文件中导出函数时,需要将修饰名放到 .DEF 文件中,或者通过使用外部“C”用标准 C 链接定义导出函数,以避免编译器进行名称修饰。
  • 如果需要将修饰名放到 .DEF 文件中,可以通过使用 Dumpbin 工具或通过使用 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将 Visual C++ 编译器产生的修饰名放到 .DEF 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样调用应用程序中的修饰名才能与 DLL 的 .DEF 文件中的导出名相匹配。

2.   使用 __declspec(dllexport) 的优缺点

使用 __declspec(dllexport) 非常方便,因为不需要考虑维护 .DEF 文件和获取导出函数的修饰名。但是,无法控制编译器生成的导出序号。此方法适合某些情况,例如,在设计要与控制的应用程序

一起使用的 DLL 时;如果用新导出重新生成 DLL,则还需要重新生成应用程序。

首先介绍第一种方法。

/** 导出全局函数 */
DLL_API int global_fun(); /** 导出整个类 */
class DLL_API dllBase
{
public:
dllBase ( void );
~dllBase ( void );
int get();
private:
static int base;
list<string> m_list;
}; /** 导出类的某个成员和方法 */
class dllBase
{
public:
dllBase ( void );
~dllBase ( void );
DLL_API int get();
private:
DLL_API int base;
list<string> m_list;
};

利用VS工具目录下的命令提示符进入相应DLL所在目录,然后用dumpbin命令查看导出函数,或者使用view Dependencies工具直接打开dll文件我们可以看到如图所示界面,界面介绍如图所示


命令:..\Debug> dumpbin -exports dll.dll

注意到先前的函数名get变为?get@dllBase@@QAEHXZ,因此这样的函数只能在由相同编译器编译的程序中进行调用,而其他程序调用则会出错。之所以篡改名字是为了C++的函数重载!

若要希望生成的函数名不变,则需要将之前#define DLL_API _declspec(dllexport)改变为:

#define DLL_API extern "C" _declspec(dllexport)

该方法的缺陷:只能对全局函数,而不能正确地导出类的成员函数。如果我们导出的函数的调用约定改变,即使我们用了extern "C"做声明,也会发生改编。

第二种方式导出函数:

在工程中新建.def文件,在其中写如下信息(其中get为要导出的函数的名称)
LIBRARY DLL   //DLL表示dll文件名
EXPORTS //表示要导出那些函数
get

注意:由此生成的文件不会改变导出函数名

从应用程序调用动态链接库的方法

3调用方法

1:隐式链接

1、将DLL文件夹DEBUG下生成的DLL文件,.lib文件拷贝到测试的应用程序(DLL_test)的工程目录下

2、在应用程序的开发界面中,项目->DLL_test的属性,在其属性页中,链接器->输入->附加的依赖项中添加刚才生成的DLL.lib导入文件。

3、在调用函数前,使用extern关键字声明调用的函数是来自外部的程序或者使用_declspec(dllimport)关键字。如本程序中:

//方法1:告诉编译器我们所引用的符号是外部程序 //效率相对方法2低
extern int add(int a,int b);
extern int sub(int a,int b);
//方法2:告诉编译器我们所引用的符号是从.lib导入文件中导入的动态链接库程序 //效率相对高
_declspec(dllimport) int add(int a,int b);
_declspec(dllimport) int sub(int a,int b);
//方法3:在设计DLL的时候就把方法2中的代码添加到头文件,然后在当前文件的#include中包含#include "..\DLL.h" //好处就是如果DLL文件和应用程序不是同一人编写的时候可以更好地调用!

#include "..\DLL.h"
//方法4:宏定义,这样可以用一个宏定义来完成_declspec(dllimport) _declspec(dllemport),而且可以使该宏不仅可以给动态链接库的开发方使用,也可以给动态链接库的调用方来使用
//在DLL.h(DLL设计文件)
#ifdef DLL_API
#else
#define DLL_API _declspec(dllimport)
#endif

我们采用第四种办法。

调用方法2,:动态调用

//动态调用动态链接库
HINSTANCE hInst;
hInst = LoadLibrary ( "Dll.dll" );
typedef int ( *ADDPROC ) ( int a, int b ); //如果在DLL设计过程中是用_stdcall的,那这里也要用_stdcall,否则系统将报错!
//typedef int (_stdcall *ADDPROC)(int a,int b); //"get"这里为用dumpbin –exports dll.dll中显示的名字,如果为?get@dllBase@@QAEHXZ,
//那么在这里也应该写ADDPROC Add=(ADDPROC)GetProcAddress(hInst," ?get@dllBase@@QAEHXZ ");
ADDPROC Get = ( ADDPROC ) GetProcAddress ( hInst, "get" ); //利用序号来调用
//ADDPROC Get=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));
if ( !Get )
{
MessageBox ( "获取函数地址失败!" );
return;
}
int res = Get(); FreeLibrary ( hInst );

DLL使用总结的更多相关文章

  1. dll文件32位64位检测工具以及Windows文件夹SysWow64的坑

    自从操作系统升级到64位以后,就要不断的需要面对32位.64位的问题.相信有很多人并不是很清楚32位程序与64位程序的区别,以及Program Files (x86),Program Files的区别 ...

  2. C#创建dll类库

    类库让我们的代码可复用,我们只需要在类库中声明变量一次,就能在接下来的过程中无数次地使用,而无需在每次使用前都要声明它.这样一来,就节省了我们的内存空间.而想要在类库添加什么类,还需取决于类库要实现哪 ...

  3. 关于Linux和Windows下部署mysql.data.dll的注册问题

    mysql ado.net connector下载地址: http://dev.mysql.com/downloads/connector/net/ 选择版本: Generally Available ...

  4. Windows平台Go调用DLL的坑

    最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...

  5. C#开发奇技淫巧三:把dll放在不同的目录让你的程序更整洁

    系列文章 C#开发奇技淫巧一:调试windows系统服务 C#开发奇技淫巧二:根据dll文件加载C++或者Delphi插件 C#开发奇技淫巧三:把dll放在不同的目录让你的程序更整洁 程序目录的整理 ...

  6. .Net使用Newtonsoft.Json.dll(JSON.NET)对象序列化成json、反序列化json示例教程

    JSON作为一种轻量级的数据交换格式,简单灵活,被很多系统用来数据交互,作为一名.NET开发人员,JSON.NET无疑是最好的序列化框架,支持XML和JSON序列化,高性能,免费开源,支持LINQ查询 ...

  7. 使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON、.net 的json的序列化与反序列化(一)

    在开发中,我非常喜欢动态语言和匿名对象带来的方便,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合.字典.动态对象和匿名对象 ...

  8. ILMerge合并多个DLL

    序言 如果你的项目要提供多个dll给别人用,那么不妨让你的dll合并为一个,让别人看起来简洁,引用起来不会过于繁琐. 本篇比较少,但也算是比较实用吧. 下载微软的辅助工具ILMerge Imerge下 ...

  9. VS2010中dll不可用问题

    最近做项目的时候,深圳那边提供了一个算法.算法在那边跑的好的很,但是在我这边怎么跑都跑不起来,总是报错:说找不到dll. 1.第一种想法:找不到dll,是不是dll放的位置不对.找了一下目录,导入的路 ...

  10. MVC默认路由实现分页-PagerExtend.dll

    这两天在群里有人咨询有没有现成的.net mvc分页方法,由此写了一个简单分页工具,这里简单分享下实现思路,代码,希望能对大家有些帮助,鼓励大家多造些轮子还是好的. A.效果(这里用了bootstra ...

随机推荐

  1. Setting Up the ADT Bundle

    Setting Up the ADT Bundle The ADT Bundle provides everything you need to start developing apps, incl ...

  2. 解决Mac下Sequel Pro 1.1 连接 Homebrew安装Mysql5.7.8的问题 Sequel Pro 1.1 encountered an unexpected error

    解决Mac下Sequel Pro 1.1 连接 Homebrew安装Mysql5.7.8的问题 Sequel Pro encountered an unexpected error Sequel Pr ...

  3. wuzhicms查找:当前页面使用的哪个文件

    要查看这个地址的模版.http://www.wuzhicms.com/item-34-2-1.html 首先,我们的这个地址需要是动态的.而不是生成的静态地址. 打开文件: /www/configs/ ...

  4. WCF入门到精通(二)——契约

    第一次接触WCF,如有写的不对的地方有望大家指出来,谢谢!! 本篇文章主要说下WCF中的契约的种类.契约的种类.如何定义契约等内容. 契约是一种双边或多边的协议,是利益相关方就某个问题达成的一种共识, ...

  5. matplotlib 初使用

    试玩了一下 matplotlib, 感觉是:很酥狐吖~ 完全不像 ggplot 那样云里雾里,但是后者展现出的图要漂亮优雅许多. x = linspace(0, 10, 100) //初始化一个 [0 ...

  6. java操作pdf添加页眉条码添加水印图片

    添加条码页眉以及图片水印 1. 引入jar包     1. itext-4.2.1.jar     2. itext-asian-5.2.0.jar     3. jbarcode-0.2.8.jar ...

  7. 从源码剖析一个Spark WordCount Job执行的全过程

      原文地址:http://mzorro.me/post/55c85d06e40daa9d022f3cbd   WordCount可以说是分布式数据处理框架的”Hello World”,我们可以以它为 ...

  8. linux设备驱动----利用mdev(udev)自动创建设备文件节点

    1.mdev的使用方法和原理: mdev是busybox 自带的一个简化版的udev,适合于嵌入式的应用埸合.其具有使用简单的特点.它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程 ...

  9. VPS选购及辨别vps虚拟化技术

    现在国内外的VPS(Virtual Private Server)服务商非常多,每个服务商使用的VPS架构都不同.VPS属于虚拟化服务器,中文名:虚拟专用服务器. 常见的VPS虚拟化架构有多种:Ope ...

  10. Tomcat内存溢出的原因

    在生产环境中tomcat内存设置不好很容易出现内存溢出.造成内存原因是不一样的,当然处理方式也不一样. 这里根据平时遇到的情况和相关资料进行一个总结.常见的一般会有下面三种情况: 1.OutOfMem ...