《静态库与动态库编程.ppt》由会员分享,可在线阅读,更多相关《静态库与动态库编程.ppt(53页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、静态库与动态库编程静态库与动态库编程主讲人:阙海忠 (Skin+作者、DirectUI首席架构师、UIPower CEO)第一节:前言第一节:前言什么是库什么是库 库从本质上来说是一种代码重用的方式,即预先编译为可执行代码的二进制格式,可以被载入内存中执行。比如我们熟悉的c运行库,里面就实现了很多基本的函数,我们无需再自己写一遍,直接调用接口使用即可,库分静态库和动态库两种。2第一节:前言第一节:前言静态库和动态库的区别静态库和动态库的区别1.静态函数库静态函数库 这类库的名字一般是xxx.lib;利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而
2、易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。3第一节:前言第一节:前言2.动态函数库动态函数库 这类库的名字一般是xxx.dll(也可以包含xxx.lib用于编译时候的链接处理,也可以不包含,直接动态调用,后面会讲区别);相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库
3、。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。4第二节:静态函数库第二节:静态函数库建立静态库工程建立静态库工程建立静态库工程:我们从最简单的开始,先建立一个win32的静态库工程,先新建工程,选择WIN32项目5第二节:静态函数库第二节:静态函数库我们创建一个testlib工程6第二节:静态函数库第二节:静态函数库7第二节:静态函数库第二节:静态函数库在“应用程序类型”选择选择静态库8第二节:静态函数库第二节:静态函数库 点完成之后我们会得到一个空白的新工程,勾选预编译头会自动生成stdafx.h,stdafx.cpp当然我们也可以不勾选。预编译的头的作用就不多说了,主要
4、目的是为了获得更快的编译速度。9第二节:静态函数库第二节:静态函数库添加静态库相关类添加静态库相关类我们如果需要提供一个类,那么最简单的,先在工程右键添加一个C+类,我们起名为CTestLib10第二节:静态函数库第二节:静态函数库 之后我们获得一个CtestLib的类,我们打开TestLib.h添加一个最简单的接口,做加法处理的,叫int Add(int a,int b)返回的值是a+b的结果11打开TestLib.h添加Add接口第二节:静态函数库第二节:静态函数库我们打开TestLib.cpp在CPP文件里面我们做Add的实现12第二节:静态函数库第二节:静态函数库可能有人会问,Add的
5、实现为什么不可以放.h里面呢?比如在.h里面写 13其实这是可以的,语法上没问题,但是这样就违背了一些初衷,比如我们不想把函数的具体实现暴露给库的使用者,只希望他们能使用接口来操作就行了,不关心实现细节第二节:静态函数库第二节:静态函数库添加静态库相关接口添加静态库相关接口对于要实现单独的接口,同样很简单,比如我们要实现一个全局的减法接口,比如int Sub(int a,int b);我们同样可以实现,为了最大范围的保证兼容,我们使用extern“C”关键字声明,同样的,我们为了演示方便,把接口也写在TestLib.h里14或者我们可以这样写:把所有需要实现的接口都写在extern“C”的括号
6、内第二节:静态函数库第二节:静态函数库在CPP里实现并无区别,我们如下写Sub即可15之后我们编译一下,在输出目录得到Testlib.lib这个就是我们编译好的静态库文件了第三节:使用静态库第三节:使用静态库对于上一节我们编译得到的库文件如何使用呢?因为我们在静态库提供的接口文件是TestLib.h,所以我们要用到的(对外发布的)就是TestLib.h和编译出来的TestLib.lib接下来我们再新建一个工程,为了最简化,我们建立一个控制台应用程序testexe16第三节:使用静态库第三节:使用静态库在应用程序类型选择控制台程序17第三节:使用静态库第三节:使用静态库有多种方式使用这样的Lib
7、,如果我们在同一个工作区来使用这个库,那么我们就可以在新建的工程里面设置依赖项目,这样系统会自动的为我们连接这个库文件,我们只需要包含一下提供静态库函数接口的.h文件即可。我们现在先假设这个静态库是单独发布给其他人用的,那么我们可以设置静态库和.h的目录,添加到VC工程的目录配置,这样就可以和使用系统库一样的使用了,另外我们为了演示方便,先把库文件放到testexe工程下面18第三节:使用静态库第三节:使用静态库我们在Testexe工程右键添加现有项把TestLib.h给加进工程19第三节:使用静态库第三节:使用静态库我们打开testexe.cpp写上相关代码20编译,这时候我们会得到一些链接
8、错误:1testexe.obj:error LNK2019:无法解析的外部符号public:_thiscall CTestLib:CTestLib(void)(?1CTestLibQAEXZ),该符号在函数_wmain 中被引用1testexe.obj:error LNK2019:无法解析的外部符号public:int _thiscall CTestLib:Add(int,int)(?AddCTestLibQAEHHHZ),该符号在函数_wmain 中被引用1testexe.obj:error LNK2019:无法解析的外部符号public:_thiscall CTestLib:CTestLi
9、b(void)(?0CTestLibQAEXZ),该符号在函数_wmain 中被引用第三节:使用静态库第三节:使用静态库因为我们只包含了接口,还没包含库文件,所以我们需要手动添加库文件第一个方法:我们右键点击testexe工程属性,打开属性页面板,选到链接器选项21第三节:使用静态库第三节:使用静态库点开链接器左边的加号,选到输入22第三节:使用静态库第三节:使用静态库附加依赖项里面我们输入TestLib.lib23第三节:使用静态库第三节:使用静态库点确定24第三节:使用静态库第三节:使用静态库再次编译程序,就顺利生成我们的执行程序了运行结果如下:25第三节:使用静态库第三节:使用静态库这就
10、是我们实现的一个最简单的功能,以此推广,无论多复杂的库都可以这样来做刚才我们说到使用静态库,还可以直接在代码里面这样写:26通过写显式链接的代码#pragma comment(lib,“TestLib.lib”)这样能显式的要求添加TestLib.lib到工程里面进行连接编译结果一样顺利通过,运行结果一样就不重复了第四节:动态库第四节:动态库相对于静态库,动态库的形式更灵活一些,大多情况下,我们编译动态库的时候,除了获得dll文件,依然会得到.lib文件,大多情况下,接口的定义还是需要通过.h来做,那么和静态库的工程,编译到底有多大区别呢?我们就来实际的通过建立动态库工程的例子再来慢慢讲述一下
11、动态库27第四节:动态库第四节:动态库新建一个工程TestDll28第四节:动态库第四节:动态库在应用程序设置里面,应用程序类型选到DLL然后完成 29第四节:动态库第四节:动态库我们得到这样一个项目 30第四节:动态库第四节:动态库Stdafx.h,stdafx.cpp依然是预编译头的相关文件,不用管,testdll.cpp里面为我们实现了一个DllMain函数 31第四节:动态库第四节:动态库很简单的只是返回一个TRUE的处理,我们先来讲述一下DllMain的作用。前面说过,动态库dll和静态库最大的区别就是,动态库是可以独立运行的文件,通俗一点讲,和可执行文件没有太大的本质区别,所以当其
12、他可执行文件(exe或者其他dll)调用到该dll的时候,系统会执行一个入口函数,做一些初始化之类的工作,当然这个入口函数和可执行文件exe有一个最大的区别就是这个入口函数不是必须的,也就是说没有这个入口函数依然能编译dll下面我们简单的先说一下系统会何时调用这个入口函数DllMain的第二个参数fdwReason指明了系统调用Dll的原因,它可能是::DLL_PROCESS_ATTACH、DLL_PROCESS_DETACH、DLL_THREAD_ATTACH、DLL_THREAD_DETACH。以下从这四种情况来分析系统何时调用了DllMain。32第四节:动态库第四节:动态库DLL_PR
13、OCESS_ATTACH大家都知道,一个程序要调用Dll里的函数,首先要先把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间,有两种方法:静态链接和动态链接的LoadLibrary或者LoadLibraryEx。当一个DLL文件被映射到进程的地址空间时,系统调用该DLL的DllMain函数,传递的fdwReason参数为DLL_PROCESS_ATTACH,这种调用只会发生在第一次映射时。如果同一个进程后来为已经映射进来的DLL再次 调用LoadLibrary或者LoadLibraryEx,操作系统只会增加DLL的使用次数,它不会再用DLL_PROCESS_ATTACH调
14、用 DLL的DllMain函数。不同进程用LoadLibrary同一个DLL时,每个进程的第一次映射都会用DLL_PROCESS_ATTACH调用DLL的DllMain函数。33第四节:动态库第四节:动态库DLL_PROCESS_DETACH当DLL被从进程的地址空间解除映射时,系统调用了它的DllMain,传递的fdwReason值是DLL_PROCESS_DETACH。当DLL处理该值时,它应该执行进程相关的清理工作。那么什么时候DLL被从进程的地址空间解除映射呢?两种情况:FreeLibrary解除DLL映射(有几个LoadLibrary,就要有几个FreeLibrary)进程结束而解除
15、DLL映射,在进程结束前还没有解除DLL的映射,进程结束后会解除DLL映射。(如果进程的终结 是因为调用了TerminateProcess,系统就不会用DLL_PROCESS_DETACH来调用DLL的DllMain函数。这就意味着DLL 在进程结束前没有机会执行任何清理工作。)注意:当用DLL_PROCESS_ATTACH调用DLL的DllMain函数时,如果返回FALSE,说明没有初始化成功,系统仍会用DLL_PROCESS_DETACH调用DLL的DllMain函数。因此,必须确保清理那些没有成功初始化的东西。34第四节:动态库第四节:动态库DLL_THREAD_ATTACH当进程创建一
16、线程时,系统查看当前映射到进程地址空间中的所有DLL文件映像,并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。新创建的线程负责执行这次的DLL的DllMain函数,只有当所有的DLL都处理完这一通知后,系统才允许进程开始执行它的线程函数。注意跟DLL_PROCESS_ATTACH的区别,我们在前面说过,第n(n=2)次以后地把DLL映像文件映射到进程的地址空间时,是不再用DLL_PROCESS_ATTACH调用DllMain的。而DLL_THREAD_ATTACH不同,进程中的每次建立线程,都会用值DLL_THREAD_ATTACH调用DllMain函数,哪怕是线程中建
17、立线程也一样。35第四节:动态库第四节:动态库DLL_THREAD_DETACH如果线程调用了ExitThread来结束线程(线程函数返回时,系统也会自动调用ExitThread),系统查看当前映射到进程空间中的所有DLL文件映像,并用DLL_THREAD_DETACH来调用DllMain函数,通知所有的DLL去执行线程级的清理工作。注意:如果线程的结束是因为系统中的一个线程调用了TerminateThread,系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数。36第四节:动态库第四节:动态库大多数情况下,我们并不用去实现DllMain的细节,即使是要关心的
18、,一般来说都只是DLL_PROCESS_ATTACH、DLL_PROCESS_DETACH、这两种方式,举个例子,如果做一个MFC相关的dll,我们可以在DLL_PROCESS_ATTACH的时候,去AfxInitExtensionModule初始化MFC的相关模块,DLL_PROCESS_DETACH的时候AfxTermExtensionModule 来注销相关模块,这些代码在建立MFC dll的工程可以看得到,所以大多情况下我们并不关心。接下来我们先看一下testdll工程的一些定义:37第四节:动态库第四节:动态库右键对工程点击属性-配置属性-C/C+-预处理器38第四节:动态库第四节:
19、动态库点击预处理器定义可以看到如下界面:39第四节:动态库第四节:动态库有一个我们关心的值:TESTDLL_EXPORTS,这个值是建立工程时候系统自动生成的,我们也可以改成自己喜欢的值,作为区分dll是导入还是导出的区别,我们先记录一下这个值我们先新建一个类CMyTestDll40第四节:动态库第四节:动态库之后的工程文件如下:41第四节:动态库第四节:动态库我们打开MyTestDll,我们都会先做一个define,通用的处理,比如:42第四节:动态库第四节:动态库定义的MY_TEST_API 是什么意义呢?TESTDLL_EXPORTS如同刚才我们说了,这个是建立工程时候系统自定义的,我们
20、可以改成我们喜欢的名字,这个不影响,只要对应得上工程所定义的即可_declspec(dllexport)和_declspec(dllimport)的区别,由于dll是独立执行的动态库,对于我们要使用到的接口,都希望有导入(import)来使用,对于dll自身来说,这类接口是导出的(export),所以在dll工程,我们希望编译为export,在其他用到我们的动态库的工程,希望得到import结果,所以很自然地,上面的定义做的就是这个事情对于我们要完整的使用CMyTestDll类,就只需要把MY_TEST_API加入class的声明就好了,如下:43第四节:动态库第四节:动态库同样的,我们依然添
21、加一个最简单的接口int Add(int a,int b)44在MyTestDll.cpp里面实现,和静态库方式没什么区别第四节:动态库第四节:动态库同样的,如果我们要做全局接口,为了演示方便,我们依然把这个全局接口放在MyTestDll.h里面:45可以看到除了前面多了MY_TEST_API的声明以外,形式和静态库的一样Cpp文件的实现和静态库的方式一样:第四节:动态库第四节:动态库接下来我们编译,会生成一个testdll.dll,testdll.lib46同样的,类似静态库的例子,我们创建一个testexe的工程,所有的操作都一样,唯一的区别就是我们多了个dll,把dll放到编译出来的ex
22、e目录,不然会提示找不到dll第四节:动态库第四节:动态库所以看到了,动态库和静态库的编译过程都差不多,静态链接的使用方式也大同小异,但是和静态库有所区别,动态库Dll还有另外一种链接的方式:LoadLibrary,LoadLibraryEx,功能都一样,我们默认使用LoadLibrary来讲一下,所谓的动态载入,就是程序运行时候不必一开始就载入dll,在程序运行过程中,在需要的时候,再动态加入进来,这样我们可以不再使用.lib来做静态链接,甚至可以连.h都不用了,直接用GetProcAddress来使用,但是这样的方式一般来说,都最好只用extern“C”开头的接口.我们可以通过Micros
23、oft Visual Studio 8Common7ToolsBin里面的一个工具Depends.Exe来看看我们刚才编译出来的dll有什么内容 47第四节:动态库第四节:动态库打开Depends.exe,然后把我们编译出来的dll拖入,会看到如下内容:48第四节:动态库第四节:动态库左边会看到我们的dll依赖的dll,右边中间部分就是我们的dll能导出的各种接口了,可以看到我们的CMyTestDll的比较复杂,而我们用extern“C”风格导出的全局接口就很简单了,和我们定义的名字一样,下面我们来试一下通过LoadLibrary的方式来使用这个dll代码如下:49第四节:动态库第四节:动态库
24、动态调用的函数,我们需要知道函数原形,于是我们定义typedef int(*FUNC_SUB)(int a,int b);的函数指针,在得到Sub函数的地址后,做强制转换,之后我们就可以和普通函数一样调用了,运行结果如下:50第四节:动态库第四节:动态库在大多情况下,我们一般都是使用.h+.lib+.dll的方式来开发,这样是一个最方便的方式,但是也有类似刚才说的我们使用LoadLibrary的方式来开发,一般作为什么情况才使用呢,比如我们要做一个程序,支持外部插件,这种时候就需要LoadLibrary这种方式了,我们只需要提供插件规范(插件必须要导出某些名字的函数),供主程序使用,这样对于插
25、件开发者来说,只需要按照规范,写dll导出相关函数,就可以实现扩展程序功能的作用了。51第五节:其他区别第五节:其他区别可能有人会问,我在CPP里面如果没实现int Sub(int a,int b)的内容一样能编译得到Testlib.lib,这是为什么呢?原因是这样的:静态库文件本身,他会把已经实现的代码都编译成二进制格式,因为静态库不具备独立执行的条件,所以最终必须要配合目标程序一起链接成可执行文件,在链接时候,如果发现Sub函数只做声明,没实现的话,会报Link error,这个时候你还可以发现如果在目标程序实现int Sub的功能,可以编译通过(同样的问题,比如在class CTestL
26、ib,有3个接口,比如叫interface_1,interface_2,interface_3,我们如果把interface_1和interface_2在静态库工程的CPP文件实现了,interface_3我们不管,依然可以编译成TestLib.lib,但是这个静态库,在配合目标程序使用的时候,会报错interface_3没实现,这样的情况下我们依然可以在目标程序实现CTestLib:interface_3的功能来通过编译,所以这个是和动态库最大的区别)关于静态库和动态库的内容大概就是这么多,如果需要建立和MFC兼容的静态库或者动态库,除了在工程设置上勾选MFC支持之外,没什么本质区别,所以这个就不重复了52结束语结束语本讲回顾本讲回顾第一节:前言第二节:静态函数库第三节:使用静态库第四节:动态库第五节:其他区别 本讲座总结本讲座总结静态库和动态库编程是Windows编程的重要技术之一,掌握好它将对以后的工作起到举足轻重的作用。希望大家在看完以后,要多加练习,只有把讲座里面提到的各种内容都熟练掌握,才能在实际工作中更好的应用。希望大家不要对MSDN和本系列讲座的PPT和Demo有任何的依赖,只有刻录在你大脑里的东西才是你真正的才能!53