动态链接库通常不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其他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. 【修改帐号信息】Eclipse中修改SVN用户名和密码方法

    由于在svn 的界面中并没有为我们提供直接更换用户名密码的地方,所以一旦我们需要更换用户名的就需要自己想一些办法. 解决方案: 在Eclipse 使用SVN 的过程中大多数人往往习惯把访问SVN 的用 ...

  2. org.apache.axis2.AxisFault: unknown

    遇到这个异常懵逼了很长时间才解决,axis2框架个人感觉进行接口相互调用还是比较麻烦的, 调了很长时间,我由a项目调用b项目的接口时,一直报这个错,在网上找了很长时间,也没找到 解决的办法,自己慢慢的 ...

  3. Quartz.NET教程:(01) 使用Quartz

    使用调度器 (scheduler) 之前要先用 ISchedulerFactory 的一个实现来实例化调度器(scheduler).一旦调度器实例化完成,则它可以被启动.置于备用模式或者关闭.需要注意 ...

  4. React Native之进度条ProgressViewIOS的使用

    import React,{Component}from 'react'; import { AppRegistry, StyleSheet, Text, View, ProgressViewIOS, ...

  5. codevs 1082 线段树练习 3 区间更新+延迟标记

    题目描述 Description 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述 Input Description 第一行一个正整数n,接下 ...

  6. 通过gevent实现【单线程】下的多socket并发

    server import sys import socket import time import gevent from gevent import socket,monkey monkey.pa ...

  7. go 语言字典遍历

    package main import "fmt" func main() { var countryCapitalMap map[string]string /*创建集合 */ ...

  8. python获取文件扩展名的方法

    主要介绍了python获取文件扩展名的方法,涉及Python针对文件路径的相关操作技巧 import os.path def file_extension(path): ] print file_ex ...

  9. canvas+js实现荧光字符效果

    一个小玩意,代码来源于网络. 效果图如下 代码如下 <html> <head> <style> * { margin: 0; padding: 0; } html, ...

  10. arcgis for silverlight 地图放大到某个点或者几何对象

    http://blog.csdn.net/xuan444150/article/details/7727866   分类: silverlight王国 GIS王国 2012-07-09 08:50 1 ...