深入浅出Visual C++动态链接库(Dll)编程之一:基本概念_wanxiaohong1350的专栏-程序员宝宝

技术标签: c++  library  exe  dll  vc程序设计  编程  mfc  

1.概论

  先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。

  对动态链接库,我们还需建立如下概念:

  (1)DLL 的编制与具体的编程语言及编译器无关

  只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。

  (2)动态链接库随处可见

  我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。

  一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见DLL对我们来说其实并不陌生。

  (3)VC动态链接库的分类

  Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。

  非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。

  由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。

  问:本文主要讲解什么内容?

  答:本文详细介绍了DLL编程的方方面面,努力学完本文应可以对DLL有较全面的掌握,并能编写大多数DLL程序。

  问:如何看本文?

  答:本文每一个主题的讲解都附带了源代码例程,可以随文下载(每个工程都经WINRAR压缩)。所有这些例程都由笔者编写并在VC++6.0中调试通过。

  当然看懂本文不是读者的最终目的,读者应亲自动手实践才能真正掌握DLL的奥妙。

  问:学习本文需要什么样的基础知识?

  答:如果你掌握了C,并大致掌握了C++,了解一点MFC的知识,就可以轻松地看懂本文。

   2.静态链接库

  对静态链接库的讲解不是本文的重点,但是在具体讲解DLL之前,通过一个静态链接库的例子可以快速地帮助我们建立“库”的概念。


图1 建立一个静态链接库

  如图1,在VC++6.0中new一个名称为libTest的static library工程(单击此处下载本工程),并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下:

//文件:lib.h

#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y);   //声明为C编译、连接方式的外部函数
#endif

//文件:lib.cpp

#include "lib.h"
int add(int x,int y)
{
 return x + y;
}

  编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add函数了。

  标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。

  下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:

#include <stdio.h>
#include "..\lib.h"
#pragma comment( lib, "..\\debug\\libTest.lib" )  //指定与静态库一起连接

int main(int argc, char* argv[])
{
 printf( "2 + 3 = %d", add( 2, 3 ) );
}

  静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本文件生成的.obj文件应与libTest.lib一起连接。如果不用#pragma comment指定,则可以直接在VC++中设置,如图2,依次选择tools、options、directories、library files菜单或选项,填入库文件路径。图2中加红圈的部分为我们添加的libTest.lib文件的路径。


图2 在VC中设置库文件路径

  这个静态链接库的例子至少让我们明白了库函数是怎么回事,它们是哪来的。我们现在有下列模糊认识了:

  (1)库不是个怪物,编写库的程序和编写一般的程序区别不大,只是库不能单独执行;

  (2)库提供一些可以给别的程序调用的东东,别的程序要调用它必须以某种方式指明它要调用之。

  以上从静态链接库分析而得到的对库的懵懂概念可以直接引申到动态链接库中,动态链接库与静态链接库在编写和调用上的不同体现在库的外部接口定义及调用方式略有差异。
3.库的调试与查看

  在具体进入各类DLL的详细阐述之前,有必要对库文件的调试与查看方法进行一下介绍,因为从下一节开始我们将面对大量的例子工程。

  由于库文件不能单独执行,因而在按下F5(开始debug模式执行)或CTRL+F5(运行)执行时,其弹出如图3所示的对话框,要求用户输入可执行文件的路径来启动库函数的执行。这个时候我们输入要调用该库的EXE文件的路径就可以对库进行调试了,其调试技巧与一般应用工程的调试一样。


图3 库的调试与“运行”

  通常有比上述做法更好的调试途径,那就是将库工程和应用工程(调用库的工程)放置在同一VC工作区,只对应用工程进行调试,在应用工程调用库中函数的语句处设置断点,执行后按下F11,这样就单步进入了库中的函数。第2节中的libTest和libCall工程就放在了同一工作区,其工程结构如图4所示。


图4 把库工程和调用库的工程放入同一工作区进行调试

  上述调试方法对静态链接库和动态链接库而言是一致的。所以本文提供下载的所有源代码中都包含了库工程和调用库的工程,这二者都被包含在一个工作区内,这是笔者提供这种打包下载的用意所在。

  动态链接库中的导出接口可以使用Visual C++的Depends工具进行查看,让我们用Depends打开系统目录中的user32.dll,看到了吧?红圈内的就是几个版本的MessageBox了!原来它真的在这里啊,原来它就在这里啊!


图5 用Depends查看DLL

  当然Depends工具也可以显示DLL的层次结构,若用它打开一个可执行文件则可以看出这个可执行文件调用了哪些DLL。

  好,让我们正式进入动态链接库的世界,先来看看最一般的DLL,即非MFC DLL。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wanxiaohong1350/article/details/7564090

智能推荐

docker本地部署memcached附带docker-compose部署memcached/elasticsearch/kibana/php/nginx脚本_Code Metaverse-程序员宝宝

docker本地部署memcached附带docker-compose部署memcached/elasticsearch/kibana/php/nginx脚本一、什么是memcachedMemcached 是一个通用的分布式内存缓存系统。 它通常用于通过在 RAM 中缓存数据和对象来加速动态数据库驱动的网站,以减少必须读取外部数据源(例如数据库或 API)的次数。Memcached 的 API 提供了一个分布在多台机器上的非常大的哈希表。 当表已满时,后续插入会导致以最近最少使用的顺序清除旧数据。 使

Blackfin-uClinux编译_dianbo9422的博客-程序员宝宝

使用bfin-uclinux-gcc -o hello hello.c 编译的时候会出现hello:applet not found应该使用bfin-uclinux-gcc -o hello hello.c -elf2flt 进行编译转载于:https://www.cnblogs.com/junmao/archive/2011/01/06/1927118.html...

基于迁移学习的语义分割算法分享与代码复现_华为云官方博客-程序员宝宝_语义分割迁移学习

摘要:语义分割的数据集是比较大的,因此训练的时候需要非常强大的硬件支持。

关于java中密码的加密(使用PasswordEncoderFactories))_一名奋斗的程序猿的博客-程序员宝宝_java passwordencoder

这里使用的是Spring Security 5中 的PasswordEncoder增加 PasswordEncoderFactories 类提供一个静态方法 createDelegatingPasswordEncoder()方法, 使用委托方式创建PasswordEncoder的实现PasswordEncoderFactories.createDelegatingPasswordEncode...

8.18练手 腾讯50题 124_cs学徒的周记-程序员宝宝

Leetcode 124.Binary Tree Maximum Path SumGiven anon-emptybinary tree, find the maximum path sum.For this problem, a path is defined as any sequence of nodes from some starting node to any node...

推荐系统和搜索系统和信息流2_cxzhq2002的杂记-程序员宝宝

产品逻辑之美信息架构:信息组织模式,让信息更好在产品和用户之间流动。先问:给谁看内容是什么什么情况下看。然后才是信息怎么组织和解决方案、然后才是可视化的方案。分类系统的难度(严格),标签对于信息组织的意义(松散)...

随便推点

VMware更换网络连接模式后局域网其他电脑无法ping通本机虚拟机_df1067的博客-程序员宝宝

本机可以ping通虚拟机,但局域网其他电脑无法ping通本机虚拟机的解决办法:1.虚拟机-设置-网络适配器-桥接模式2.更换网络连接模式后查看ip地址,再ping一下ip地址# 查看ip地址ifconfig

python运行器_python三大器_weixin_39605296的博客-程序员宝宝

一.迭代器迭代:迭代是一个重复的过程,每一次重复都是基于上一次的结果而来下面给出两个例子区分:# 单纯的重复,每一次的重复都与上次的结果无关,因此不叫迭代while True:print("----------")# 此例子属于迭代,因为每次重复的结果都和上次相关l = ['a','b','c']n = 0while n < len(l):print(l[n])n +=1可迭代对象:定义:在pyt...

【Linux-Vim】Vim常用操作_dandelionela的博客-程序员宝宝

Vim保存与退出 命令 简单说明 :w 保存编辑后的文件内容,但不退出vim编辑器。这个命令的作用是把内存缓冲区中的数据写到启动vim时指定的文件中。 :w! 强制写文件,即强制覆盖原有文件。如果原有文件的访问权限不允许写入文件,例如,原有的文件为只读文件,则可使用这个命令强制写入。但是,这种命令用法仅当用户是文件的属主时才适用,而超级用户则不受此限制。 :wq ...

Android控件UI高级进阶_cxjwpp的博客-程序员宝宝

RecyclerView1.关于ItemDecorationhttps://www.jianshu.com/p/9a796bb23a472.获取View的正确位置https://www.jianshu.com/p/b22a338667363.自定义控件(启舰自定义控件三部曲)https://blog.csdn.net/harvic880925/article/details/50995268...

Excel设置行高列宽单元格为正方形,行列比例_世间好物不坚牢,彩云易散琉璃脆。-程序员宝宝_excel列宽行高正方形比例

Excel一个大毛病就是行列单位不统一,好像还不能调整,我去百度了一下,没有说在选项里可以调整的,我自己也没发现。那么既然单位不统一,就给它按照比例统一了。行高100长度为36.5mm,列宽10是22.5mm,也就是说行高1长度为0.365mm,列宽1是2.25mm,比例就是0.365 : 2.25,比值大约是6,所以就是行高42,列宽就是7,你可以试试将其乘1000,精度小于0.2,几乎就是正方形了,如果你想让误差更小,就按照比值6.16来算,完整的数字是6.164383561643835616438

【转载】verilog中$readmemb和$readmemh的使用_秋予川-程序员宝宝

readmemb和readmemh用来从文件中读取数据到存储器中。读取的内容只包括:空白位置(空格、换行、制表格(tab和form-feeds),注释行、二进制或十六进制的数字。数字中不能包含位宽说明和格式说明,其中readmemb要求每个数字是二进制数,readmemh要求每个数字必须是十六进制数字。数字中不定值x或X,高阻值z或Z,和下划线(_)的使用方法和代表意义与一般Verilog HDL程序中的用法一致。在Verilog语法中,一共有以下六种用法:(1)$readmemb("&lt

推荐文章

热门文章

相关标签