dll和lib(包括静态链接库和与dll同时生成的lib)
转:http://blog.csdn.net/galaxy_li/article/details/7411956
1:神马是Dll和Lib,神马是静态链接和动态链接
大家都懂的,DLL就是动态链接库,LIB是静态链接库。DLL其实就是EXE,只不过没main。
动态链接是相对于静态链接而言的。所谓静态链接就是把函数或过程直接链接到可执行文件中,成为可执行程序中的一部分,当多个程序调用同样的函数时,内存里就会有这个函数的多个拷贝,浪费内存资源。而动态链接则是提供了一个函数的描述信息给可执行文件(并没有内存拷贝),当程序被夹在到内存里开始运行的时候,系统会在底层创建DLL和应用程序之间的连接关系,当执行期间需要调用DLL函数时,系统才会真正根据链接的定位信息去执行DLL中的函数代码。
在WINDOWS32系统底下,每个进程有自己的32位的线性地址空间,若一个DLL被进程使用,则该DLL首先会被调入WIN32系统的全局堆栈,然后通过内存映射文件方式映射到这个DLL的进程地址空间。若一个DLL被多个进程调用,则每个进程都会接收到该DLL的一个映像,而非多份的拷贝。但,在WIN16系统下,每个进程需要拥有自己的一份DLL空间,可以理解为何静态链接没啥区别。
2:DLL和LIB区别和联系。
DLL是程序在运行阶段才需要的文件。
LIB是程序编译时需要链接的文件。
DLL只有一种,其中一定是函数和过程的实现。
LIB是有两种。若只生成LIB的话,则这个LIB是静态编译出来的,它内部包含了函数索引以及实现,这个LIB会比较大。若生成DLL的话,则也会生成一个LIB,这个LIB和刚才那个LIB不同,它是只有函数索引,没有实现的,它很小。但是这俩LIB依然遵循上个原则,是在编译时候是需要被链接的。若不链接第一个LIB的话,在程序运行时会无法找到函数实现,当掉。若不链接第二个LIB的话,在程序运行时依然会无法找到函数实现。但第二种LIB有一种替代方式,就是在程序里,使用LoadLibrary,GetProcAddress替代第二个LIB的功能。第一种LIB生成的EXE文件会很大,因为LIB所有信息被静态链接进EXE里了。第二种LIB生成的EXE文件会比较小,因为函数过程实现依旧在DLL内。
(啰嗦了一堆,某志希望大家能够明白两个LIB的区别。要再不行的话,我们可以将静态编译的LIB称为 静态链接库。但动态编译的LIB称为 引入库。可能会比较好一些。)
静态链接LIB的优点是免除挂接动态链接库,缺点是EXE大,版本控制麻烦些。
动态链接DLL的优点是文件小,版本更换时换DLL就好了,缺点是多了点文件。动态链接若是被多个进程使用,会更加方便和节省内存。
3:为什么编译DLL时总会同时生成一个LIB?这个LIB有用吗?
若我们不是用静态链接,而使用DLL,那么我们也需要一个LIB,这个LIB的作用是被链接到程序里,在程序运行时告诉系统你需要什么DLL文件。这个LIB里保存的是DLL的名字和输出函数入口的顺序表。它是有意义的。
当然,若我们的应用程序里不链接这个LIB,则可以使用LoadLibrary,GetProcAddress来告诉系统我们在运行时需要怎么着DLL以及其内的函数。
4:DLL意义。
1:DLL真正实现了跨语言。各种语言都可以生成DLL,而对系统以及应用程序来说,哪种语言生成的DLL是没有区别的。
2:DLL有足够的封装性,对于版本更新有很大好处。因为DLL是运行期间才会使用,所以,即使DLL内函数实现有变化(只要参数和返回值不发生变化),程序是不需要进行编译的。大大提高了软件开发和维护的效率。
3:DLL被多个进程使用,因为有内存映射机制,无需占用更多内存。
5:创建DLL。(注意:某志就不再讲解使用MFC AppWizard[dll] 方式创建DLL了。有兴趣的自己去百度。这里创建DLL只指使用Win32 Dynamic-link Library创建Non-MFC DLL。呃,DLL的三种类型就不解释了,依旧那句话:百度一下你就知道。)
每个应用程序必须有一个main或者winmain函数作为入口,DLL一样,有自己的缺省的入口函数,就是DllMain。函数如下
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: // 进程被调用
case DLL_THREAD_ATTACH: // 线程被调用
case DLL_THREAD_DETACH: // 线程被停止
case DLL_PROCESS_DETACH: // 进程被停止
break;
}
return TRUE;
}
一般情况下,我们不需要对这个缺省的入口函数进行什么修改,它就会使动态链接库得到正确的初始化。但是,当我们的DLL需要额外分配内存或者资源的时候,或者,DLL希望对调用自己的进程或线程进行初始化或清除的额外操作时,可以在上述代码case中加一些自己感冒的东东。(懒……不想细写了- -Orz,现在是晚上2点了,明天还一堆的事情)
DLL对于导出类和导出函数没啥不同。只要加上 __declspec( dllexport ) 修饰函数或者类就好了。
但是有查看过DLL代码的人员都会经常见到这么一段代码
#ifdef FK_DLL_EXPORTS
#define FK_DLL __declspec( dllexport )
#else
#define FK_DLL __declspec( dllimport )
#endif
意义很明显,但是,问题是 FK_DLL_EXPORTS 这个宏是应该在哪儿定义呢?在DLL项目内,还是在使用DLL的应用程序内?
这点某志曾迷糊很久,呵呵~其实后来想想,还是蛮明确的。export是导出。import是导入。对于DLL来说,是要导出这些函数给其他应用程序使用的,所以应当定义 FK_DLL_EXPORTS 宏。对于使用DLL的应用程序来说,是导入,是无需定义的。
使用时候也很简单。
class FK_DLL CMyDllClass{} ;
则整个类被导出。
FK_DLL void MyTestFun( int a );
则该函数被导出。
但是有时我们可以见到这样的代码
extern "C" FK_DLL void MyTestFun2( float b );
其中extern "C"的原理就是标示该函数要求以C形式去进行编译,不要以C++形式去编译。具体的编译原理就不罗嗦了,简而言之,被extern "C"定义函数,可以被C以及其他语言进行DLL调用,而未被extern "C"定义的函数,C是无法访问DLL中这个函数的。
在VS中开发DLL还有一种方式,使用.def文件。
新建个文本文档,改后缀为FKDll.def,加入到工程里。
FKDll.def里加入以下代码
LIBRARY FKDll
EXPORTS
就可以了。其中,LIBRARY语句是说明.def文件是属于FKDll这个Dll的。EXPORTS下面是我们需要导出的函数名。后面加的@+数字,是表示导出函数的顺序编号。这样就足够了。(详细的自己百度,好困,zzzZZZ)
6:使用DLL
使用DLL有两种方式。显式链接和隐式链接。
隐式链接很容易。直接#progam comment(lib, "FKDll.lib") 就可以。当然,也可以在项目工程->属性->链接库里加上库和路径(相对路径和绝对路径都可以)。
显式链接则麻烦些。在程序中使用LoadLibrary加载DLL,再GetProcAddress获取函数实现,在程序退出之前,调用FreeLibrary来动态释放掉链接库。
例如:
void Main()
{
typedef void (*FKDllFun1)(int a);
FKDllFun1 pFun1;
HINSTANCE hDLL = LoadLibrary("FKDll.dll"); // 若hDll为空则读取Dll失败。
pFun1 = (pFun1)GetProcAddress(hDll, "MyTestFun1" ); // 从应用程序中的DLL镜像中获取名为 MyTestFun1 的函数指针
pFun1( 100 );
FreeLibrary(hDll);
}
当然,我们刚才.def里面还指定了导出函数的导出顺序,那么我们可以修改里面获取函数指针那一段为
pFun1 = (pFun1)GetProcAddress(hDll, MAKEINTERSOURCE(1) ); // 1 是刚才指定的MyTestFun1函数导出顺序编号。
这样可以更快,但是别将编号记混了,会导致诡异的错误。
7:比较显式链接和隐式链接。
可能的话,尽量使用显式链接。
显式链接可以在程序执行时动态的加载DLL和卸载DLL文件,隐式链接是做不到的。
显式链接LoadLibrary,GetProcAddress时能获知是否加载失败,我们可以对其进行检查错误处理。而显式链接可能是一个很恶劣的提示或是程序崩溃的结果。
对于有些Ex类型的加强函数,显式链接可以允许我们找到替代方案。也包括选择D3d9.dll和OpenGL.dll时也可采用同样处理。
例如:
if( GetProcAddress( hDll, "FKDllFunEx") == NULL )
{
pFun = GetProcAddress( hDll, "FKDllFun"); // 然后使用pFun进行处理
}
8:导出类和导出函数
类和函数的导出方式上面给出了说明,原本极其类似的。
我们说下使用导出类。
若我们隐式的使用了一个导出类,则我们在应用程序里继承它的时候,就如同该类就在应用程序代码里一样,无需任何处理。
例如:
class FK_DLL CMyDllClass{} ; // Dll文件内的代码
-----------------------
class CAppClass : public CMyDllClass // 应用程序内代码,无需做任何处理。
{
....
}
也可以直接使用DLL导出类
void main
{
CMyDllClass* pClass = new CMyDllClass ();
}
但是,若应用程序声明或者分类一个DLL中导出类的对象时会存在一个很讨厌的问题:这个操作会使内存跟踪系统失效,使其错误的报告内存分配和释放情况。
为解决这个问题,我们可以给出两个接口函数对DLL导出类进行创建销毁支持,就可以使内存跟踪系统正常了。例如
class FK_DLL CMyDllClass{} ;
额外增加俩函数
FK_DLL CMyDllClass* CreateMyDllClass(){ return new CMyDllClass(); }
FK_DLL void DestoryMyDllClass( CMyDllClass* p_pClass ){ delete p_pClass; }
-----------------------------------------------
上面的方法可以正确进行内存跟踪了,但是,因为DLL导出类CMyDllClass依旧是导出的状态,用户同样可以跳过我们提供的接口直接使用。那么怎么办呢。方法是不再对类进行DLL导出,而对类内的函数全部进行DLL导出即可,
-----------------------------------------------
但是若仅仅提供上面两个接口函数以及类内全部函数,的确功能可以实现,却无法进行类继承了。若这个类继承很重要,必须开放,那么就需要使用新的内存跟踪程序替换应用程序内的原有内存跟踪程序。或者使用下面的一个方法。(见模块9:复杂问题)
-----------------------------------------------
同样,我们也可以发现,在不导出DLL类本身,而只导出DLL类内函数也有一些好处,一些我们不希望外界知道的函数可以不设置导出标记,这进一步保护了DLL内函数的安全性。
9:复杂问题。
若我们使用LoadLibrary显式加载一个DLL,并尝试在应用程序中调用一个类内成员函数的话,无论该函数是否在头文件中有声明,VS会给出一个"unresolved external symbol(未解析的外部符号)"的错误。我们此时可以将项目属性中的内联函数扩展选项修改为"Only __inline"或"Any Suitable"即可。但,我们可能在调试连编的时候期望关闭内联函数扩展,那么另一种解决方案是,将希望导出的函数声明为虚函数,例如
class CMyDllClass
{
FK_DLL virtual void MyTestFun( int a ){ dosth(); }
// 用上面代码替换 FK_DLL void MyTestFun( int a ){ dosth(); }
}
这样做还有一个额外的好处。将导出的类成员函数设置为虚函数之后,该虚函数所在的类在应用程序中也如同被声明一样,可以接受继承。
例如若是上面的做法,应用程序就可以进行顺利继承,而不必要求CMyDllClass 被标示为导出。(原理不知,希望精通底层的高手协助解释。)
class CAppClass : public CMyDllClass // 应用程序内代码,无需做任何处理。
{
....
}
dll和lib(包括静态链接库和与dll同时生成的lib)的更多相关文章
- (C#)WPF:.h(头文件)、.lib(静态链接库文件)和.dll(动态链接库文件)之间的区别与联系
静态链接库(Lib)与动态链接库(DLL)的区别 静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件:动态链接就是把调用的函数所在文件模块(DLL)和 ...
- Makefile常用万能模板(包括静态链接库、动态链接库、可执行文件)
本文把makefile 分成了三份:生成可执行文件的makefile,生成静态链接库的makefile,生成动态链接库的makefile. 这些makefile都很简单,一般都是一看就会用,用法也很容 ...
- VC++ DLL 2 静态链接库
这一篇以VS2013为例子介绍怎样编写一个静态链接库和调用. 1.打开VS2013,新建Visual C++ 的win32项目: 新建后工程分支如下: 添加头文件和源文件: 编写头文件和源文件内容: ...
- VS2010编译Boost 1.57 静态链接库
http://www.cnblogs.com/cuish/p/4175491.html 0.前提 Boost库版本 1.57.0 下载地址 http://www.boost.org/users/his ...
- 介绍静态链接库和动态链接库的差别,及在VC++6.0中的建立和使用
首先介绍一下链接库:链接库分为动态链接库和静态链接库两种 LIB是静态链接库,在程序编译连接的时候是静态链接,其相应的文件格式是.lib. 即当程序採用静态链接库的时候..lib文件里的函数被链接到终 ...
- MFC 封装类为静态链接库
mfc自带的基本控件都不怎么美观,所以一般开发者都会自定义类对控件进行重绘.手里也积累了不少控件的重绘,对对话框.静态文本.列表框等. 但是每次都要把这些类重新导入到新的工程里,比较麻烦,而且我也不想 ...
- lib静态链接库,dll动态链接库,h文件
最近在弄摄像头,发现我在调用摄像头自带的函数的时候,库没连接上,于是经过高人指点,学习了一下lib静态链接库,dll动态链接库来补充一下自己的基础知识. 一.首先我们来介绍一下lib静态链接库. li ...
- 动态链接库dll,静态链接库lib, 导入库lib
转载地址:http://www.cnblogs.com/chio/archive/2008/08/05/1261296.html 目前以lib后缀的库有两种,一种为静态链接库(Static Libar ...
- 动态链接库dll,导入库lib,静态链接库lib
目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库” ...
随机推荐
- 在Tomcat中采用基于表单的安全验证
.概述 (1)基于表单的验证 基于From的安全认证可以通过TomcatServer对Form表单中所提供的数据进行验证,基于表单的验证使系统开发者可以自定义用户的登陆页面和报错页面.这种验证方法 ...
- RESTClient
RESTClient是Mozilla Firefox一个用于测试http请求插件. 1.打开火狐扩展搜索RESTClient进行安装并重启浏览器. 2.重启后可以在Mozilla Firefox地址栏 ...
- java去重(1通过迭代器,2直接赋值)
1.List<Integer> list=new ArrayList<Integer>(); //有值 List<Integer> listTemp= new A ...
- PHP错误杂记
Notice: Only variables should be passed by reference in-- 原因:The problem is, that end requires a ref ...
- JQuery 纵向二级菜单与对齐方式
1.效果: 2.代码: style部分: <style type="text/css"> /* ul{margin: 0; padding: 0;}*/ ul{list ...
- c# HTTP技术
种植头发能完全成活吗?头发上出现了掉落,头发变细等情况下是需要去看看是不是皮下的毛囊出了问题,要解决这个头发脱发上,选择植发的方式能有效改善,不过也不要小看这个头发脱发,脱发后如果不加以做好毛发护理, ...
- 浅谈python模块的导入操作
1.什么是模块 在Python中有一个概念叫做模块(module). 所谓模块,就是将代码量较大的程序分割成多个有组织的,彼此独立但双能互相交互的代码片段, 这些自我包含的有组织的代码段就是模块. 2 ...
- ABP官方文档翻译 5.4 SwaggerUI集成
SwaggerUI集成 介绍 ASP.NET Core 安装Nuget包 配置 测试 ASP.NET 5.x 安装Nuget包 配置 测试 介绍 在它的网站上:“...使用Swagger可用的API, ...
- python中的各种转化
1.数之间的转化 #浮点型to整型 >>> int(5.6) 5 #整型to浮点型 >>> float(5) 5.0 2.数与字符串 #字符串to整型 >&g ...
- win7 MySQL Connector/Net 安装卸载问题
问题1:卸载MySQL Connector Net 6.9.9 卸载程序无法卸载 方法:注册表搜索 MySQL Connector Net 6.9.9 全部删除 ******************* ...