动态链接库DLL_第1篇
动态链接库通常不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其他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篇的更多相关文章
- 【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 ...
- 关于linux下的.a文件与 .so 文件
连续几天终于将一个又一个问题解决了,这里说其中一个问题 描述问题:使用多线程pthread的时候,(我用的IDE,CODEBOLCKS)编译后发现直接弹出窗口,程序还没有被Build..巴拉巴拉,然后 ...
- Linux的.a、.so和.o文件 对比 window下的dll,lib,exe文件
连续几天终于将一个又一个问题解决了,这里说其中一个问题 描述问题:使用多线程pthread的时候,(我用的IDE,CODEBOLCKS)编译后发现直接弹出窗口,程序还没有被Build..巴拉巴拉,然后 ...
- TGL站长关于常见问题的回复
问题地址: http://www.thegrouplet.com/thread-112923-1-1.html 问题: 网站配有太多的模板是否影响网站加载速度 月光答复: wp不需要删除其他的模板,不 ...
- 如何得到动态链接库的输出函数tdump命令(225篇博文)
有的时候,我们需要查看一个动态链接库的输出函数列表,有很多软件可以满足此要求,比如说 exeScope.不过,去下载一个软件总归是很麻烦,Delphi 本身就自带一个类似的工具,那就是 tdump.e ...
- linux动态链接库---一篇讲尽
一般我们在Linux下执行某些外部程序的时候可能会提示找不到共享库的错误, 比如: tmux: error while loading shared libraries: libevent-1.4.s ...
- 编译可供C#调用的C/C++动态链接库dll文件
编译可供C#调用的C/C++动态链接库dll文件,C语言控制台应用程序,探索生成dll过程 由于项目需求,需要公司另一个团队提供相关算法支持,是用C语言编译好的dll库提供给我们进行调用. 但是拿到d ...
- QT总结第3篇:如何在QT中添加.lib,.dll还有.h文件
因为我在工作的过程中,使用的是第三方提供的库,但是如何将这些库添加到QT的工程中,是个问题,让我恼火了很久,怎么弄都是错的. 下面,我会对这个问题,进行叙述,希望其他人第一次遇到这种问题的时候,可以轻 ...
- [转载:]C#与Fortran混合编程之本地调用Fortran动态链接库
前言 C#发展到现在,已是一门相当完善的语言,他基于C语言风格,演化于C++.并依靠强大的.NET底层框架.C#可以用来快速构建桌面及Web应用.然而在我们的实际工作中,尽管C#已经非常完善,但还是不 ...
随机推荐
- C# Byte[] 数组操作
byte[] Strbyte = Encoding.GetEncoding("big5").GetBytes(str); if (Strbyte.Length ...
- 【异常记录(九)】 System.Threading.ThreadAbortException: 正在中止线程
报错如下: System.Threading.ThreadAbortException: Thread was being aborted. at System.Threading.Thread.Ab ...
- C# string[]转List<string>
List<string> ltProduct = new List<string>(Product.Split('|'));
- illumina support
http://support.illumina.com/help/BaseSpace_App_WGS_BWA_help/Content/Vault/Informatics/Sequencing_Ana ...
- java中Scanner类nextInt之后用nextLine无法读取输入
http://blog.csdn.net/wjy1090233191/article/details/42080029 这篇文章写得非常详细和准确
- codevs 1191 数轴染色 区间更新加延迟标记
题目描述 Description 在一条数轴上有N个点,分别是1-N.一开始所有的点都被染成黑色.接着我们进行M次操作,第i次操作将[Li,Ri]这些点染成白色.请输出每个操作执行后剩余黑色点的个数. ...
- 将正在使用的Ubuntu14.04 制作成镜像文件
remastersys 是一个能够备份你的ubuntu系统的工具. 源码在github上能找到:Remastersys Source 可以直接 apt 安装: sudo add-apt-reposit ...
- 【Golang】 可以自动生成测试用例的库--gotests
简介 gotests是一个Golang命令行工具,它可以使编写Go的测试代码变得容易.它能基于目标源文件的函数和方法生成数据驱动测试用例,并且在此过程会自动导入任何依赖. 下面是gotests在使用S ...
- English trip -- VC(情景课)9 C What are they doing? 他们在做什么?
Grammar focus 语法点: 以What 开头的问句 What is/is/are he/she/they doing? Cutting the grass. Walking the dog. ...
- 使用Laravel提交POST请求出现The page has expired due to inactivity错误
任何指向 web 中 POST, PUT 或 DELETE 路由的 HTML 表单请求都应该包含一个 CSRF 令牌(CSRF token),否则,这个请求将会被拒绝.