动态链接库通常不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其他DLL调用来完成某项工作的函数。只有在其他模块调用动态链接库中的函数时,它才发挥作用。实际编程时,可把完成某种功能的函数放在一个动态链接库中,提供给其他程序调用。

Windows API中所有函数都包含在DLL中,比较重要的有3个,分别为:

1、Kernel32.dll

包含用于管理内存、进程和线程的函数。

2、User32.dll

包含用于执行用户界面任务(如窗口创建和消息传递的函数等)的函数。

3、GDI32.dll

包含用于画图和显示文本的函数。

静态库和动态库

1、静态库

函数和数据被编译成一个二进制文件,扩展名为.LIB。使用静态库时,编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件。发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。

2、动态库

需要提供2个文件:引入库.lib文件和DLL文件。动态库的引入库.lib文件与静态库虽然后缀相同,但含义完全不同。引入库文件.lib包含DLL导出的函数和变量的符号名,.dll文件包含DLL实际的函数和数据。编译链接时,只需要链接DLL的引入库文件,DLL中的函数代码和数据并不复制到可执行文件中,直到可执行程序运行时,才去加载需要的DLL,将该DLL映射到进程的地址空间,然后访问DLL中的导出函数。发布产品时,除了发布可执行文件,还要同时发布动态链接库。

如果多个应用程序使用同一个DLL,该DLL的页面只需要放入内存一次,所有的应用程序都可以共享它的页面。

有两种加载动态链接库的方式:隐式链接和显式加载。

应用程序如果想访问某个DLL中的函数,该函数必须是已经被导出的函数。为了让DLL导出一些函数,需要在每一个将要被导出的函数前面添加标志符_declspec(dllexport)。

客户调用DLL的导出函数时,必须先申明该函数是外部的extern。除了使用extern关键字表明函数是外部定义的之外,还可以使用_declspec(dllimport)来表明函数是从动态链接库中引入的。使用_declspec(dllimport)声明外部函数,能够明确告诉编译器函数是从动态链接库中引入的,编译器可以生成运行效率更高的代码。

通常在编写动态链接库时,都会提供一个头文件,在此文件中提供DLL导出函数原型的声明,以及函数的有关注释文档。程序编译时,头文件不参与编译,源文件单独编译。

dll_1.h文件

#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport)
#endif DLL1_API int add(int a, int b);
DLL1_API int subtract(int a, int b);

dll_1.cpp文件

#define DLL1_API _declspec(dllexport)
#include "dll_1.h" DLL1_API int add(int a, int b)
{
return a + b;
} DLL1_API int subtract(int a, int b)
{
return a - b;
}

动态链接库还可以导出C++类。

dll_1.h文件

#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport)
#endif DLL1_API int add(int a, int b);
DLL1_API int subtract(int a, int b); class DLL1_API point {
public:
void Output(int x, int y);
};

dll_1.cpp文件

#define DLL1_API _declspec(dllexport)
#include "dll_1.h" DLL1_API int add(int a, int b)
{
return a + b;
} DLL1_API int subtract(int a, int b)
{
return a - b;
} void point::Output(int x, int y)
{
HWND hwnd = GetForegroundWindow();
HDC hdc = GetDC(hwnd);
TCHAR buf[] = { '\0' };
swprintf(buf, , _T("x = %d, y = %d"), x, y);
TextOut(hdc, , , buf, wcslen(buf));
ReleaseDC(hwnd, hdc);
}

如果隐式链接加载DLL,.dll更新后,需要将新的.dll和.lib文件复制到相应工程目录。

动态链接库还可以不导出整个C++类,而只导出C++类的某些函数。

dll_1.h文件

class/* DLL1_API*/ point {
public:
void DLL1_API Output(int x, int y);
void test();
};

如果声明类时,指定了导出标志,该类中的所有函数都将被导出,否则只有那些声明时指定了导出标志的类成员函数才被导出。

名字改编问题

C++编译器生成DLL时,会对导出函数的名字进行改编,不同编译器使用的改编规则不同,改编后的名字是不一样的。如果利用不同的编译器分别生成DLL和访问DLL的客户端程序,客户端程序访问DLL的导出函数时就会出现问题。因此,希望动态链接库文件编译时,导出函数的名称不要发生改变。做法是,定义导出函数时,加上限定符extern “C”,字母C大写。

dll_1.h文件

#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport)
#endif DLL1_API int add(int a, int b);
DLL1_API int subtract(int a, int b);

dll_1.cpp文件

#define DLL1_API extern "C" _declspec(dllexport)
#include "dll_1.h" DLL1_API int add(int a, int b)
{
return a + b;
} DLL1_API int subtract(int a, int b)
{
return a - b;
}

extern “C”可以解决C++和C语言之间相互调用时函数命名的问题,但该方法不能用于导出一个类的成员函数,只能用于导出全局函数。如果导出函数的调用约定发生改变,即使使用限定符extern “C”,函数的名字仍会发生改变。

显示加载方式加载DLL

LoadLibrary的作用是将指定的可执行模块映射到调用进程的地址空间,返回值是加载模块的句柄。GetProcAddress函数用来获得导出函数的地址。动态加载DLL时,客户端不再需要包含导出函数声明的头文件和引入库文件,只需要.dll文件即可。

void CMFCApplication1Dlg::OnBnClickedBtnSub()
{
HMODULE HInst = LoadLibrary(_T("ConsoleApplication1.dll"));
if (HInst == NULL)
return;
typedef int (*ADDPROC)(int a, int b); ADDPROC Add = (ADDPROC)GetProcAddress(HInst, "add");
if (!Add) {
MessageBox(_T("获取函数地址失败!"));
return;
}
CString str;
str.Format(_T("5 + 3 = %d."), Add(, ));
MessageBox(str);
}

实际上采用隐式链接方式访问DLL时,程序启动时也是通过调用LoadLibrary函数加载该进程的动态链接库的。

如果采用动态加载方式使用DLL,访问DLL的客户端程序,如果对DLL的访问已经完成,不再需要访问该DLL时,应调用FreeLibrary函数释放该DLL。FreeLibrary主要减少被加载的DLL的引用计数。当计数变为0时,该DLL模块将从调用进程的地址空间卸载。

DllMain函数

DLL的入口函数是DllMain,该函数是可选的。编写DLL程序时,可以提供也可以不提供DllMain函数。如果提供了DllMain函数,系统加载该DLL时,就会调用该函数。

不应该在DllMain函数中进行复杂的调用,因为加载该动态链接库时,可能还有一些核心动态链接库没有被加载。如果自己编写的DllMain函数需要调用核心动态链接库中的某些函数,就会导致程序终止。

参考资料:

1、《VC++深入详解》

动态链接库DLL_第1篇的更多相关文章

  1. 【Linux】-NO.87.Assembly.1.滴水逆向.1.001-【介绍】-

    1.0.0 Summary Tittle:[Linux]-NO.87.Assembly.1.滴水逆向.1.001-[基础]- Style:Java Series:Log4j Since:2017-04 ...

  2. 关于linux下的.a文件与 .so 文件

    连续几天终于将一个又一个问题解决了,这里说其中一个问题 描述问题:使用多线程pthread的时候,(我用的IDE,CODEBOLCKS)编译后发现直接弹出窗口,程序还没有被Build..巴拉巴拉,然后 ...

  3. Linux的.a、.so和.o文件 对比 window下的dll,lib,exe文件

    连续几天终于将一个又一个问题解决了,这里说其中一个问题 描述问题:使用多线程pthread的时候,(我用的IDE,CODEBOLCKS)编译后发现直接弹出窗口,程序还没有被Build..巴拉巴拉,然后 ...

  4. TGL站长关于常见问题的回复

    问题地址: http://www.thegrouplet.com/thread-112923-1-1.html 问题: 网站配有太多的模板是否影响网站加载速度 月光答复: wp不需要删除其他的模板,不 ...

  5. 如何得到动态链接库的输出函数tdump命令(225篇博文)

    有的时候,我们需要查看一个动态链接库的输出函数列表,有很多软件可以满足此要求,比如说 exeScope.不过,去下载一个软件总归是很麻烦,Delphi 本身就自带一个类似的工具,那就是 tdump.e ...

  6. linux动态链接库---一篇讲尽

    一般我们在Linux下执行某些外部程序的时候可能会提示找不到共享库的错误, 比如: tmux: error while loading shared libraries: libevent-1.4.s ...

  7. 编译可供C#调用的C/C++动态链接库dll文件

    编译可供C#调用的C/C++动态链接库dll文件,C语言控制台应用程序,探索生成dll过程 由于项目需求,需要公司另一个团队提供相关算法支持,是用C语言编译好的dll库提供给我们进行调用. 但是拿到d ...

  8. QT总结第3篇:如何在QT中添加.lib,.dll还有.h文件

    因为我在工作的过程中,使用的是第三方提供的库,但是如何将这些库添加到QT的工程中,是个问题,让我恼火了很久,怎么弄都是错的. 下面,我会对这个问题,进行叙述,希望其他人第一次遇到这种问题的时候,可以轻 ...

  9. [转载:]C#与Fortran混合编程之本地调用Fortran动态链接库

    前言 C#发展到现在,已是一门相当完善的语言,他基于C语言风格,演化于C++.并依靠强大的.NET底层框架.C#可以用来快速构建桌面及Web应用.然而在我们的实际工作中,尽管C#已经非常完善,但还是不 ...

随机推荐

  1. 论文笔记——Deep Model Compression Distilling Knowledge from Noisy Teachers

    论文地址:https://arxiv.org/abs/1610.09650 主要思想 这篇文章就是用teacher-student模型,用一个teacher模型来训练一个student模型,同时对te ...

  2. UVa 10201 Adventures in Moving - Part IV

    https://vjudge.net/problem/UVA-10201 题意: 给出到达终点的距离和每个加油站的距离和油费,初始油箱里有100升油,计算到达终点时油箱内剩100升油所需的最少花费. ...

  3. BZOJ 2669 【CQOI2012】 局部极小值

    题目链接:局部极小值 这是一道\(dp\)好题. 由于需要保证某些位置比周围都要小,那么我们可以从小到大把每个数依次填入,保证每个局部极小值填入之前周围都不能填,就只需要在加入的时候计数了. 由于局部 ...

  4. Springboot 学习笔记 之 Day 2

    “约定大于配置”这样一句话,就是说系统,类库,框架应该假定合理的默认值,而非要求提供不必要的配置,可是使用Spring或者SpringMVC的话依然有许多这样的东西需要我们进行配置,这样不仅徒增工作量 ...

  5. MongoDB(课时17 更新函数)

    3.4.3 数据更新操作 MongoDB数据存的是副本数据, 最终的数据还要保存在传统的数据库里,所以如果关系型数据库里数据变了,最好的方法是删除里面的MongoDB数据重新插入. 在MongoDB里 ...

  6. django QueryDict对象

    类的原型:class QueryDict[source] 在HttpRequest对象中,GET和POST属性都是一个django.http.QueryDict的实例.也就是说你可以按本文下面提供的方 ...

  7. select 操作

    一:JavaScript 1:拿到select对象: var myselect=document.getElementById(“test”); 2:拿到选中项的索引:var index=mysele ...

  8. 如何查看DNS?

    查看dns服务ip:    命令: ipconfig/all  查看dns缓存:    命令:ipconfig/displaydns     强制更新缓存:    命令:ipconfig /flush ...

  9. Style样式的四种使用(包括用C#代码动态加载资源文件并设置样式)

    Posted on 2012-03-23 11:21 祥叔 阅读(2886) 评论(6) 编辑 收藏 在Web开发中,我们通过CSS来控制页面元素的样式,一般常用三种方式: 1.       内联样式 ...

  10. 20170612xlVBA含方框文档填表

    Sub mainProc() Application.ScreenUpdating = False Application.DisplayAlerts = wdAlertsNone 'Dim xlAp ...