动态链接库通常不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其他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. 51NOD 1066 Bash游戏

    1066 Bash游戏 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题   有一堆石子共有N个.A B两个人轮流拿,A先拿.每次最少拿1颗,最多拿K颗,拿到最后1颗石子的 ...

  2. 4、CommonChunkPlugin提取公共js-提取多个

    cnpm install css-loader --save-dev    //css-loader 是将css打包进js cnpm install style-loader --save-dev   ...

  3. Ubuntu 12.04 安装JDK

    为了在Ubuntu上安装好eclipse,按照步骤先进行JDK的安装. (1) 新建java文件夹 命令行操作: (2) 下载解压JDK安装包后无法移动文件夹至File System 移动时提示:Pe ...

  4. stm32 pwm 电调 电机

    先上代码 python 树莓派版本,通俗表现原理.stm32 C语言版本在后面 import RPi.GPIO as GPIO import time mode=2 IN1=11 def setup( ...

  5. plsql过期解决方法

    1.首先,登陆PL/SQL Developer,PL/SQL Developer要到期了 2.输入指令“regedit”打开注册表,如图所示 3.然后,在注册表里按HKEY_CURRENT_USER\ ...

  6. Java中的正则表达式Pattern与Matcher

    一般来说比起功能有限的String类,我们更愿意构造功能强大的正则表达式.我们可以通过Pattern 与 Matcher 来构建功能强大的正则表达式 import java.io.File; impo ...

  7. http post发送请求

    一: 用java自带URL发送 public synchronized JSONObject getJSON(String url2, String param) { try { URL url = ...

  8. 退出shell 脚本

    #!/bin/bash export TOP_PID=$$ trap 'exit 1' TERM exit_script(){ kill -s TERM $TOP_PID } echo "b ...

  9. awk 提取数字

    echo b1c2d3d1e8f9 | awk ' { string=$0 len=length(string) for(i=0; i<=len; i++) { tmp=substr(strin ...

  10. 《A_Pancers》第一次作业:团队亮相

    一.团队及团队成员介绍 1> 队名:A_Pancers 2> 团队成员组成: 201571030310/龙正圆(小组长) 201571030329/杨环宇      20157103030 ...