dll和lib(包括静态链接库和与dll同时生成的lib)

2023-05-16

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

MyTestFun@1

MyTestFun2@2

就可以了。其中,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      // 应用程序内代码,无需做任何处理。

{

       ....

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

dll和lib(包括静态链接库和与dll同时生成的lib) 的相关文章

  • Gym 101028J 100541D

    Gym 100499I 这题当理解题意的时候就出现一个难题 xff0c 循环小数怎么转化为分数 xff0c 果断百度下 普及知识 xff1a 1 纯循环小数 小数点后有几位数 分母就有几个9 分子为一个循环节 如 0 345 345循环 6
  • Wayland与Weston简介

    xfeff xfeff 简单地说 xff0c Wayland是一套display server Wayland compositor 与client间的通信协议 xff0c 而Weston是Wayland compositor的参考实现 其
  • 什么是IaaS,PaaS和SaaS及其区别

    云计算的三种服务模式 xff1a IaaS xff0c PaaS和SaaS Infrastructure xff08 基础设施 xff09 as a Service xff0c Platform xff08 平台 xff09 as a Se
  • Backup for tire

  • 用QXDM获取log的方法说明

    用 QXDM 获取 log 的方法说明 对于获取 LOG 可能我们会有一些误解 测试人员害怕得到的 LOG 信息不全 而将所有的选项都选上 希望能够得到最全的 LOG 其实不然 受到手机及手机与 PC 的通讯限制 如果选择保存所有的 LOG
  • Python环境变量PYTHONPATH设置和easy_install简单使用

    1 把自己编写的 python模块添加到 PYTHONPATH上 要想让 python解释器找到自己编写的模块 xff0c 则该模块必须 PYTHONPATH上 xff0c 否则在导入该模块时会出现找不到该模块的错误 xff0c 因此必须把
  • 超级有用的git reset --hard和git revert命令

    很多时候 xff0c git新手容易误操作 xff0c 比如 xff0c 在levelIISZ 1 4 dev分支下 xff0c 运行了git pull idc cpp 1 0的结果 xff0c 这样做麻烦很大 xff0c 经常导致mave
  • android 为什么需要签名

    所有的Android应用程序都要求开发人员用一个证书进行数字签名 xff0c anroid系统不会安装没有进行签名的由于程序 平时我们的程序可以在模拟器上安装并运行 xff0c 是因为在应用程序开发期间 xff0c 由于是以Debug面试进
  • 高通平台工具使用

    OverView QPST 综合工具 传输文件 查看 device 的 EFS 文件系统 代码烧录 QRCT 测试RF QXDM 看log JTAG trace32调试 QPST QXDM的使用说明 xff0c 具体的可以看我上传到csdn
  • git创建与管理远程分支

    1 远程分支就是本地分支push到服务器上的时候产生的 比如master就是一个最典型的远程分支 xff08 默认 xff09 1 git push origin master 除了master之外 xff0c 我们还可以随便创建分支 xf
  • pthread_key_t和pthread_key_create()详解

    下面说一下线程中特有的线程存储 xff0c Thread Specific Data 线程存储有什么用了 xff1f 他是什么意思了 xff1f 大家都知道 xff0c 在多线程程序中 xff0c 所有线程共享程序中的变量 现在有一全局变量
  • 2016 Personal Training #11 Div.2 B G J

    UVALive 5963 题意 xff1a 给你n个数 xff0c 如果这n个数满足 xff1a 例如n 61 4第一个数前面有0个数后面有三个数那么这第一个位置数可以为0或者3 xff0c 第二个位置可以为1或2等等 给出的n个数满足则输
  • Ubuntu22.04安装CUDA11.8和CUDNN

    下载CUDA11 8 下载CUDA11 8 选择对应的系统 架构 OS 版本 逐步执行上图命令 编辑环境变量文件 sudo gedit bashrc 配置环境变量 export PATH 61 usr local cuda 11 8 bin
  • ACME.SH 申请SSL证书(免费、自动更新)

    1 获取DNS密钥 xff08 1 xff09 获取域名服务商AccessKey ID及AccessKey Secret 我使用的域名是阿里云 xff0c 故需要去阿里云RAM管理平台获取 xff1a 其他服务商 xff0c 可以去指定的服
  • C语言fscanf函数读取结构化数据

    函数原型 xff1a int fscanf FILE restrict stream const char restrict format span class hljs keyword span fscanf 分隔符是 空格 tab 回车
  • 选择法排序

    选择法排序 xff1a 假设有N个数要按照从大到小的顺序排序 xff0c 选择法就是先设第一个数是最大的 xff08 进行第一次大循环 xff09 xff0c 然后将这个数与数组中剩下的数依次比较 xff0c 如果剩下的数中有比这个数大的
  • debian 10的安装DVD

    准备 下载debian 链接 xff1a https pan baidu com s 1BfyVmF3UgiEyKWzgQO90LA 提取码 xff1a evk9 复制这段内容后打开百度网盘手机App xff0c 操作更方便哦 来自百度网盘
  • Linux 最常用命令汇总

    常用命令 一 文件操作进入文件夹查看文件夹下文件创建文件夹复制文件移动文件删除文件查看文件内容实时查看文件内容创建文件编辑文件追加文件内容添加文件内容替换文件内容清空文件压缩解压文件分割文件文件合并文件对比显示文件树软链接一次执行多个she
  • CSP官网题目——炉石传说

    问题描述 玩家会控制一些角色 xff0c 每个角色有自己的生命值和攻击力 当生命值小于等于 0 时 xff0c 该角色死亡 角色分为英雄和随从 玩家各控制一个英雄 xff0c 游戏开始时 xff0c 英雄的生命值为 30 xff0c 攻击力
  • 【C51自学笔记】定时器

    CPU时序 xff1a v 振荡周期 xff1a 为单片机提供定时信号的振荡源的周期 xff08 晶振周期或外加振荡周期 xff09 v 状态周期 xff1a 2个振荡周期为1个状态周期 xff0c 用S表示 振荡周期又称S周期或时钟周期

随机推荐

  • Codeforces Round #706 (Div. 2)

    代码 xff1a span class token macro property span class token directive keyword include span span class token string lt iost
  • Codeforces Round #366 (Div. 2) A和B

    昨晚打了一个小时CF感悟最大的就是英文真是菜的抠脚 xff0c 第二题看了半天再结合样例解释才知道是什么意思 xff0c 第一题第一次提交代码输出漏写个单词真是醉了 xff0c 两题都掉分果真CF A Hulk 题意 xff1a 如果是1就
  • Matlab进行多项式的因式分解

    clear all span class token punctuation span clc syms x span class token punctuation span f1 span class token operator 61
  • 【linux】详解linux 下安装软件tar.gz, rpm,deb的方法

    在Linux系统中 xff0c 软件安装程序比较纷繁复杂 xff0c 不过最常见的有两种 xff1a 1 xff09 一种是软件的源代码 xff0c 您需要自己动手编译它 这种软件安装包通常是用gzip压缩过的tar包 xff08 后缀为
  • 有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第 几号的那位。

    问题 xff1a 有n个人围成一圈 xff0c 顺序排号 从第一个人开始报数 xff08 从1到3报数 xff09 xff0c 凡报到3的人退出圈子 xff0c 问最后留下的是原来第 几号的那位 解决思路 我的解决思路是先给这n个人排序生成
  • 【AtCoder】【模拟】【模型转化】Camel and Oases(AGC012)

    题意 xff1a 有一个骆驼 xff0c n个绿洲遍布在数轴上 xff0c 第i个绿洲的坐标为x i xff0c 保证x i 单增 骆驼的驼峰有体积初始值V 当驼峰的体积变为v的时候 xff0c 驼峰中至多只能够存储v L的水 骆驼希望走完
  • windows10远程桌面登录ubuntu20.04

    一 ubuntu系统共享设置 设置远程共享密码 xff0c 密码要记住 xff0c 一会在windows远程登陆的时候 xff0c 第二步要输入密码 二 安装xrdp sudo apt get install xrdp 三 安装dconf
  • 配置ArchLinux系统

    配置ArchLinux系统环境 文章目录 配置ArchLinux系统环境配置 96 AUR 96 中国源桌面环境安装登录管理器安装字体与其他附加组件安装驱动安装中文输入法安装 xff1a 优化配置 96 TRM 96 磁盘优化 96 tlp
  • Linux字体推荐

    Linux字体推荐 以下是Linux推荐的字体在不同Linux发行版的不同包装名称 xff0c 每款都很精致 xff0c 请欣赏 xff01 ArchLinux字体软件包名称Debian字体软件包名称Fedora字体软件包名称Gentoo字
  • 安装配置fcitx输入法

    安装配置fcitx输入法 文章目录 安装配置 96 fcitx 96 输入法安装输入法引擎可用的 96 fcitx 96 输入法包括 xff1a 中文日文其语言 可用的 96 fcitx5 96 输入法包括 xff1a 中文日文其他语言 输
  • 使用adb安装或卸载卸载手机系统应用

    使用adb安装 卸载手机系统应用 准备工具 在浏览器下载并打开带有adb的工具 xff1b 手机打开 开发者选项 xff0c 在 开发者选项 中开启usb调试并连接电脑 xff0c 手机端选择 传输文件 xff1b 在手机端点击 一律允许计
  • OpenWRT好用的插件

    PPP xff08 连接Internet共享 xff09 xff1a 用于让路由器到外网的连接 xff1b 资源占用小 xff0c 官网有相应版本可供下载 xff0c 软件包名称为 ppp mod pppoe Adblock xff08 广
  • Gentoo Linux查看已安装软件包

    Gentoo Linux查看已安装软件包 对于Gentoo Linux xff0c 需要一个名为Equery的软件来查看已安装软件包的信息 Equery是一个用于简化常用 Portage 操作的工具 此外 xff0c 可以显示包依赖项 元数
  • 2016 Personal Training #4 Div.2 A B C G H

    xff21 URAL 2091 题意 xff1a 有四个类别 xff11 xff0e xff21 和 xff22 都包含 xff0c xff12 xff0e 只包含 xff21 不包含 xff22 xff0c xff13 xff0e 只包含
  • Gentoo Linux系统清理

    Gentoo Linux系统清理 一 清除源码包文件 清除已经下载的软件包 xff0c 步骤如下 xff1a 在这一步中 xff0c 所使用的工具是rm命令 xff0c 将会强制删除 使用前 xff0c 请仔细检查所要删除的目录是否正确 x
  • Gentoo Linux卸载无用内核

    Gentoo Linux卸载无用内核 移除内核源代码 在安装新的内核并正常工作之后 xff0c 旧的内核就可以移除了 emerge的 depclean 选项 xff08 缩写为 c xff09 可以用来移除所有旧的或者不用的软件版本 xff
  • flink学习(一)---Maven配置简单易学

    一 下载Maven 进入官网http maven apache org 点击Download 进入官网之后 xff1a 我演示的是windows的配置步骤 xff1a 1 将安装包放入C盘 xff0c 进行解压 xff0c 将maven文件
  • eclipse括号风格改为独占一行风格

    一 原Eclipse生成的花括号 情形 public class test public static void main String args System out println 34 Eclipse 的花括号 xff08 brace
  • SecureCRT按Ctrl+S后假死问题

    SecureCRT作为著名的SSH客户端 xff0c 经常用于登陆远程服务器 在上面编辑文本 xff0c 特别是用vi打开两个文本 xff0c 并且需要切换时 xff0c 很容易出现卡死的现象 xff0c 不能接受任何的键盘输入 很是郁闷
  • dll和lib(包括静态链接库和与dll同时生成的lib)

    1 xff1a 神马是Dll和Lib xff0c 神马是静态链接和动态链接 大家都懂的 xff0c DLL就是动态链接库 xff0c LIB是静态链接库 DLL其实就是EXE xff0c 只不过没main 动态链接是相对于静态链接而言的 所