COM编程之五 动静态链接
【1】静态链接
静态链接是指由链接器在链接时将库的内容加入到可执行程序中的做法。
链接器是一个独立程序,将一个或多个库或目标文件(先前由编译器或汇编器生成)链接到一块生成可执行程序。
函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。
在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块
组合起来创建最终的可执行文件(.EXE文件)。
具体实例步骤如下:
第一步:打开VS2010-->文件-->新建-->工程-->WIN32控制台项目-->确定(名称Static)-->静态库选项-->预编译头文件不选-->Finish
第二步:在这个空项目中,添加一个lib.h文件和一个lib.cpp文件
首先添加 lib.h 声明文件
1 // Static Project
2 // lib.h
3 // 声明函数
4
5 #ifndef LIB_H
6 #define LIB_H
7
8 extern "C" int Add(int a, int b);
9 extern "C" int Sub(int a, int b);
10
11 #endif
再添加 lib.cpp 实现文件
1 // Static project
2 // lib.cpp
3 // 实现函数逻辑
4
5 #include "lib.h"
6
7 int Add(int a, int b)
8 {
9 return a + b;
10 }
11 int Sub(int a, int b)
12 {
13 return a - b;
14 }
第三步:保证添加正确后编译整个工程,确保编译成功
第四步:在工程Debug文件夹下寻找生成的Static.lib文件,这个文件正是我们需要的静态链接库
第五步:在目前的工程下,新建空项目TestLib。用以测试静态链接库的使用效果:
(1)实现方式一
a. 将Static项目下的lib.h和Static.lib这两个文件复制到TestLib项目的目录下
b. 在工程目录中添加已存在的lib.h文件
c. 再新建一个Test.cpp文件。如下:
1 // TestLib project
2 // Test.cpp
3 // 测试静态链接库
4
5 #include "lib.h"
6 #include <stdlib.h>
7 #include <stdio.h>
8
9 #pragma comment(lib,"Static.lib")
10
11 void main()
12 {
13 printf("%d\n", Add(1, 2));
14 printf("%d\n", Sub(4, 3));
15 system("pause");
16 }
说明:#pragma comment( lib, "Static.lib" ),这一句是显式的导入静态链接库。
(2)实现方式二
a. 将Static项目下的lib.h文件复制到TestLib项目的目录下
b. 在工程目录中添加已存在的lib.h文件
c. 工程-->属性-->链接器-->输入-->附加依赖项输入“Static.lib”
d. 再新建一个Test.cpp文件。如上。
不论使用那种方式。最终编译 运行的结果都应该是正确链接并调用到两个函数。执行结果不做赘述。
【2】静态链接过程详解
静态链接即就是把被链接项目的.h文件和.cpp文件
首先编译成为一个静态链接库文件.lib
然后在链接项目的源文件目录添加
编译生成的静态链接库文件.lib 以及 被链接项目的.h文件
最后在链接项目的源文件中引入.h文件 并显式导入静态链接库
即可成功执行链接项目调用被链接项目的接口功能
其实形象理解如下:
即就是别人把它的产品零件(.h和.cpp文件)
通过车床(静态链接库工程)
进行加工 (编译)
生成一个机器产品 (debug文件)
抛弃生成的不必要成分 (debug中除过.lib的其他文件)
提取对客户有用的核心部分 (debug中的.lib文件)
当然这个核心的部分是独一无二的(别人拥有版权的权利)
为了人性化的推销使用他们的产品,他们要把产品说明书(.h文件)附加
送给我们,以便于我们的正确高效使用!
【3】动态链接
动态链接库链接分为两种方式:
(1)载入时动态链接
即是:编译之前已经明确知道要调用DLL中的哪几个函数并且编译时在目标文件中只保留必要的链接信息,而不含DLL函数的代码。
当程序执行时,利用链接信息加载DLL函数代码并在内存中将其链接入调用程序的执行空间中,其主要目的是便于代码共享。
实例如下:
第一步:打开VS2010-->文件-->新建-->工程-->WIN32控制台项目-->确定(名称DLL)-->DLL选项->Finish
第二步:查看新建项目是否存在dllmain.cpp文件,确保为DLL项目。然后在项目中添加以下文件:
a. dll.h
1 // DLL project
2 // DLL.h
3 // 声明导出函数
4
5 #ifndef DLL_H
6 #define DLL_H
7
8 extern "C" __declspec(dllexport) int add(int x, int y);
9
10 #endif
b. dll.cpp
1 // DLL project
2 // DLL.cpp
3 // DLL 函数实现
4 #include "stdafx.h"
5 #include "dll.h"
6
7 int add(int x, int y)
8 {
9 return x + y;
10 }
编译整个DLL项目,工程Debug目录下会生成DLL.lib和DLL.dll文件。装入时动态链接必需二者。
第三步:在目前的工程下,新建空项目TestDLL。用以测试装入时动态链接库的效果:
将DLL项目Debug目录下的DLL.dll和DLL.lib这两个文件复制到TestDLL项目的目录下。
再新建一个TestDll.cpp文件。如下:
1 //ps:该文件属于TestDll项目,与DLL项目同属于一个工程
2 // TestDll project
3 // TestDll.cpp
4 // 静态调用DLL实现
5
6 #include<stdio.h>
7
8 #pragma comment(lib,"dll.lib")
9 extern "C" __declspec(dllimport) int add(int x, int y);
10
11 void main( )
12 {
13 int result = add(20, 30);
14 printf("%d", result);
15 }
第四步:编译TestDLL项目。执行结果应该正确调用add函数功能。
过程进行完毕,但是过程中存在很多疑问。且听分析:
1. .lib文件详解
.lib 文件为Windows系统中的库文件,相当于Linux中的.a 或 .o、.so文件
lib有 静态lib 和 动态lib之分
a: 静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序。(如上静态链接模块中编译后得到的lib文件)
b: 动态lib相当于一个.h文件,是对实现部分(.dll文件)的导出部分的声明。
编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持。
一个lib文件是obj文件的集合。
当然,其中还夹杂着其他一些辅助信息,目的是为了让编译器能够准确找到对应的obj文件。
2. 如下两句的作用:
1 extern "C" __declspec(dllexport) int add(int x, int y);
2 extern "C" __declspec(dllimport) int add(int x, int y);
在动态链接库中__declspec(dllexport)作用是导出声明,__declspec(dllimport)作用是导入声明。
关于导出导入声明还有另外一种方式即采用模块定义文件(.def),下面将示例。
3. #pragma comment(lib,"dllTest.lib")作用是告诉编译器与DLL相对应的.lib文件所在的路径及文件名。
程序员在建立一个DLL文件时,连接器会自动为其生成一个对应的.lib文件。
该文件包含了DLL导出函数的符号名及序号(并不含有实际的代码)。
在应用程序里,.lib文件将作为DLL的替代文件参与编译。
总而言之,静态链接方式编译生成应用程序时,应用程序中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE文件中,.lib文件中所包含的与之对应
的DLL文件的文件名也被编译器存储在EXE文件内部。
当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL函数的动态链接。
这样,EXE将能直接通过函数名调用DLL的输出函数,就像调用程序内部的其它函数一样。
(2)运行时动态链接
即是:编译之前并不知道将会调用哪些DLL函数,完全是在运行过程中根据需要决定应调用哪个函数,并用LoadLibrary和GetProcAddress动态获得DLL函数的入口地址。
实例如下:
第一步:打开VS2010-->文件-->新建-->工程-->WIN32控制台项目-->确定(名称DLL)-->DLL选项->Finish
第二步:查看新建项目是否存在dllmain.cpp文件,确保为DLL项目。然后在项目中添加以下文件:
a. dll.cpp
1 // DLL.cpp
2 // 实现文件
3
4 #include "stdafx.h"
5
6 int add(int a, int b)
7 {
8 return a + b;
9 }
b. dll.def
1 ; Dll.def : 导出DLL函数
2 LIBRARY DLL
3 EXPORTS
4 add @ 1
编译整个DLL项目,工程Debug目录下会生成DLL.lib和DLL.dll文件。装入时动态链接必需二者。
如果VS2010环境下只有.dll文件没有生成.lib文件
需要在项目-->属性-->连接器-->输入-->模块定义文件中添加指定模块定义文件
第三步:在目前的工程下,新建空项目TestDLL。用以测试装入时动态链接库的效果:
新建一个TestDll.cpp文件。如下:
1 // TestDLL project
2 // TestDLL.cpp 测试动态链接
3
4 #include <stdio.h>
5 #include <Windows.h>
6 using namespace std;
7
8 typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
9
10 int main(int argc, char *argv[])
11 {
12 HINSTANCE hDll; //DLL句柄
13 lpAddFun addFun; //函数指针
14 wchar_t wchar[] = L"..\\Debug\\DLL.dll";
15 hDll = LoadLibrary(wchar);
16 if (hDll != NULL)
17 {
18 addFun = (lpAddFun)GetProcAddress(hDll, "add");
19 if (addFun != NULL)
20 {
21 int result = addFun(20, 30);
22 printf("%d\n", result);
23 }
24 FreeLibrary(hDll);
25 }
26 return 0;
27 }
第四步:编译TestDLL项目。执行结果应该正确调用add函数功能。
1. def文件
a: 模块定义 (.def) 文件为衔接器提供有关被链接程序的导出、属性及其他方面的信息。生成 DLL 时,.def 文件最有用。
b: .def文件的规则为:
(1) LIBRARY语句说明.def文件相应的DLL;
(2) EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
(3) .def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
2. LoadLibrary 加载动态库; GetProcAddress 获取DLL函数的地址; FreeLibrary 释放动态库。
【4】动态链接分析
相比静态链接,动态链接有几个优点:
1.多个进程在同一基地址装载相同DLL,只需要一个共享DLL即可,它在物理地址空间内共享。
2.DLL内的函数变化,只要其输入输入参数、调用格式没有变,应用程序自身不必重新编译。静态链接需要重新编译。
3.DLL提供异步支持。比如应用程序装载DLL时,用DLL支持一个无效设备。
4.不同语言编写的应用程序可以用约定的调用格式调用DLL函数。
潜在缺点是应用程序不是相对独立的。
启动装载动态链接时,DLL不存在,程序不被装载。
运行装载时,如果DLL不存在,由程序处理。
【5】静态链接 与 动态链接 总结
静态链接是在生成可执行程序的时候就把库中的内容加入到程序中。
载入时动态链接是在将功能模块读入内存时把动态库中调用到的相关模块的内容载入内存。
运行时动态链接是在执行程序调用到模块内容时再将动态库中的相应模块载入到内存。
这里有两个时间问题。一是载入时间,二是执行时间。
静态链接由于是在一开始就把所有模块都加载进入内存,所以如果模块很多的话效率就会被大大拉低。
载入时动态链接是分别载入,当把一个模块载入内存时检查有调用关系的模块载入,比静态链接节省了许多开销。
运行时动态链接则是把当前模块调用的模块推迟到调用的时候再载入。
三者之间的关系就像是机枪,冲锋枪和shou枪
机枪在一开始就把所有子弹装好,开枪的时候很快,但也很笨重。
冲锋枪则是将子弹分弹夹存好,轻便了许多,但是会有一段装填时间。
shou枪就更加轻便了,但是弹夹容量更小,装填的频率又变高了。
【6】组件与动态链接库
动态链接库简称为DLL。一个组件实际上并不是一个DLL。
DLL只是一个组件服务器。或者说是一种发行组件的方式。
组件实质上应看成是在DLL中所实现的接口集。
DLL只是一种形式,而组件才是实质。
而组件需要放入DLL中,在客户获取某个组件接口指针时:
它必须先将相应的DLL装载到其进程空间并创建此组件。
CreateInstance可以建立一个组件实例并给客户返回一个IUnknown接口指针。
CreateInstance是DLL中唯一需要客户显式链接的函数。
而客户对于组件所需的所有函数通过一个接口指针而访问到。
Good Good Study, Day Day Up.
顺序 选择 循环 总结