Windows平台下动态链接库的总结

1、 动态链接库与静态连接库

       静态连接库与动态链接库都是经过编译器编译之后的,在计算机上可以直接运行的二进制目标文件,就像exe文件一样,但不同于exe文件的是静态链接库和动态链接库不可以独立运行,一般而言,动态链接库和动态链接是在内部实现了一些功能,对外提供了一组接口函数使得外部的程序能够通过这些对外的接口函数来使用其内部的功能。

       静态链接库,是在调用该静态库的可执行文件编译的时候(更确切地说是链接的时候)就将其中的内容整合进目标文件,而动态链接库则是在运行或加载时才将动态库中的内容整合进目标文件。

       静态连接库命名为XXX.lib,它在程序链接时候将被整合进主程序,假如我们要开发一个计算器软件,该软件的主程序为Calc.exe,在开发Calc的时候使用到了两个静态链接库 add.lib和multi.lib ,其中静态库add.lib主要用于对各种类型数据的加、减操作,静态库multi.lib主要用于对各种类型数据的进行高效率的乘法和除法操作。在开发Calc.exe的时候,我们就必须在编译时用到这两个静态链接库 add.lib和multi.lib,最终我们发布的程序只有calc.exe,没有add.lib和multi.lib,因为在生成目标文件calc.exe的时候,编译工具就已经把这两个静态库中的内容整合进了calc.exe中,这时候用户拿到的计算器程序就一个文件:Calc.exe。因此静态库只是提供给开发人员使用,对于最终用户来说是不可见的。

       动态链接库一般命名为XXX.dll,它在程序链接的时候不会被整合进目标文件,因此在发布应用程序的时候,需要一并把所需的动态库文件提供给用户。按照上一个例子,要开发名为Calc.exe的程序,但是在开发的时候用到了两个动态库add.dll和multi.dll,其中add.dll主要用于对各种类型数据进行高效的加减操作,add.dll主要用于对各类数据进行高效的乘除操作,在编译的时候,我们并不需要这两个dll文件,dll文件是在运行的时候需要的,如

果采用显示动态加载dll的方式,则只需要一个头文件就可以了,如果要采用隐式加载时链接dll的方式,则还需要对应的lib文件。在Calc的开发过程中,编译器并没有将dll中实现的内容放进Calc.exe中,因此我们程序在发布的时候,就不能只发布Calc.exe,还需把Calc.exe所依赖的动态库add.dll和multi.dll一起提供给用户,这时候用户拿到的计算器程序包含了三个文件:Calc.exe、add.dll和multi.dll,其中Calc.exe是可执行程序,用户只需双击它就可以使用。

2、创建动态链接库

       动态库的开发基本与正常程序开发一样,只是它不需要main函数,当然dll里面也有个DllMain函数,但是我们一般不使用它。创建动态库的一般步骤如下:

(1)  创建头文件,在该头文件中主要包含待导出的接口函数,该头文件需和动态库一起提供给使用者,因为在使用动态库的时候还需要包含此头文件,这样动态库的使用者才知道动态库的导出函数是怎么定义的。在windows下动态库中的函数如果不特殊说明默认是不导出的,因此如果需要导出一个函数,需要在函数的声明时使用__declspec(dllexport)进行告诉编译器声明的函数将被导出。

(2)  创建源文件,在源文件中来实现dll的所有功能。这些源文件不会提供给使用者。

(3)  编译各源文件,将每个源文件都分别编译成obj文件。


(4)  链接obj文件,连接起将所有的obj文件链接起来形成一个dll文件。

(5)  生成lib文件,当动态库中导出的符号(这里的符号是指导出的函数或变量)超出一个时,还会生成一个lib文件,这里的lib文件不像静态库中生成的lib文件(静态库的lib文件中包含了静态库的全部实现内容),它里面没有实现代码,只是包含导出的符号;动态库的实现代码被放在dll中,lib文件中只是简单的包含导出的符号和其他一些链接信息,以供隐式使用动态库所用。

       下面以VS2008为例来创建一个Calc.exe所需要的动态库add.dll

(1)  File =>New =>Project在弹出的对话框中依次进入:     

Visual C++ => Win32 => Win32 ConsoleApplication

在Name中填写dll工程的名字add,在Location中选择add功所要保存的路径,点击OK,在接下来的一个页码中不作修改直接点击Next,进入Application Settings页面中选择DLL(这里默认的是Console application),然后点击Finish,创建一个空的dll工程。此时可以看到VS已经自动生成了dlmain.cpp,在该文件中就有DllMain的默认定义。这里暂时不做修改。

(2)  添加头文件add.h,

#ifndef _ADD_H

#ifdef __cplusplus

  #defineMY_EXPORT extern “C”__declspec(dllexport)

#else

  # defineMY_EXPORT __declspec(dllexport)

#endif //__cplusplus

 

MY_EXPORT int add(int iVal1, int iVal2);

#endif //_ADD_H

       这里extern “C”是C和C++混合一起编程时需要使用的修饰符号。另外,编译器看到__declspec(dllexport)符号时,会为其所修饰的函数、类、变量等生成一些额外的信息,这些额外的信息在使用动态库的时候会用到。

(3)  添加源文件add.cpp,在源文件中添加导出函数的实现。

#include “add.h”

const int DEFAULT_ADD_VALUE = 100;

int add (int iVal1, int iVal2)

{

       return iVal1 + iVal2 + DEFAULT_ADD_VALUE;

}

(4)  Build 该dll工程,可以在其Debug或Release目录下看到生成了两个文件:add.dll和add.lib

在创建动态链接库的时候需要注意:

(1)  避免导出变量,导出变量将不利于动态库的维护和扩展。

(2)  慎重导出类,因为只有当导出C++类的模块使用的编译器和导入C++类的编译器为同一厂商提供时,才可能保证没有问题。

3、使用动态库连接库

       动态链接库有两种使用方式:“隐式载入时链接”和“显示运行时链接”。“隐式载入时链接”方式在程序启动时就需要把所有依赖的动态库载入,即时在程序中没有执行动态库中的代码也需要将动态库加入,如果所依赖的动态库不存在,则程序无法启动;“显示运行时链接”方式需要在程序中通过函数LoadLibrary来显示的加载动态库,它是运行时才加载所使用的动态库,如果程序中没有执行到加载动态库的操作,则动态库不会被加载进来;例如:

boolbUseLib = false;

if(bUseLib)

{

           HMODULE hdll = LoadLibrary(“add.dll”);

           Typedef int (*TADD)(int,int);

           TADD add = GetProcAddress(hdll, “add”);

           cout<<add(12,13)<<endl;    

}

       此时,在上面的代码中动态库的加载不会被执行,此时即使没有add.dll程序也可以正常执行。

相反如果在隐式动态库中,其实现代码可能为如下形式:

boolbUseLib = false;

if(bUseLib)

{

   cout<<add(12,13)<<endl;    

}

       此时,在程序刚启动时就会加载add.dll,在上述代码中根本就走add这个分支,但是没有了add.dll程序还是启动不了。

在VS2008中,隐式载入时链接的配置与使用方式,需要注意的是Debug和Release模式需要分别配置,配置文件也要对应,例如工程的Debug需要配置Debug模式的动态库,工程的Release模式需要配置Release模式的动态库:

[1]添加动态库的头文件路径:项目属性=>C/C++=〉General=〉Additional Include Directories,在其中填入动态库头文件的路径。

[2]配置lib文件的路径:项目 属性=>Linker=〉General=〉Additional Library Directories,在其中填入lib文件的路径。

[3]配置所依赖的lib文件名:项目 属性=>Linker=〉Input=〉AdditionalDepedencies,在其中填入所依赖的lib文件名称。

[4]将所依赖的dll文件拷贝到Debug或Release目录下;或者将dll的路径添加到环境变量中;或者将dll拷贝到windows下面system32目录下。

[5]在工程中包含dll的头文件,然后在程序中就可以直接使用dll中的函数了。

4、可执行程序搜索动态库的顺序

       在运行时,可执行程序将按照下面的顺序来搜索动态库,搜索到所需要的动态库之后再进行加载,下面以 Calc.exe、add.dll、multi.dll组成的计算器程序在执行时加载动态库为例(假设这三个文件都放在Calc目录下):

(1)  可执行程序所在的目录,如本例中Calc.exe所在的Calc目录。

(2)  Windows的系统目录,该目录可通过函数GetSystemDirectory得到,在xp系统下就是C:\WINDOWS\system32

(3)  Windows的System32子目录

(4)  Windows目录,此目录可以通过函数GetWindowsDirectory得到。

(5)  进程的当前目录,注意进程的当前目录不一定是启动目录,在使用过程中有可能被更改。

(6)  环境变量path中所列出的目录。

5、创建和使用动态库时需要注意的事项

(1)  跨编译器使用dll时要防止导出符号的改名。假如dll的编译与使用都是同一家厂商的编译器,则不存在问题,如果用MS VC++开发的dll要用到其它非MSVC++的编译器的项目上时,需要采取一些预防措施来防止导出符号被改名,可以通过以下两种方式来防止导出符号被改名:

[1]在编译dll的时候增加一个.def文件,并在该文件中添加EXPORT段,然后将导出的符号列在EXPORT之后即可,例如:

EXPORT

        add

[2]在编译dll的时候,在源文件的开始添加类似如下的代码:

#pragmacomment(linker, ”/export:add=_add@8”)

这里_add@8是MS编译器自动为符号修改的名字,其修改规则是:在函数名前加”_”,在函数名后jia”@”和传给函数的参数的字节数构成。

(2)  开发过程中不要在一个动态库中申请内存然后再这个动态库之外释放,这样不仅容易引起内存泄露,而且还有可能由于申请和释放内存所使用的运行库不一样而引起其它的问题。

(3)  尽量避免导出变量。

(4)  谨慎导出类。

(5)  在显示动态链接的时候可以通过LoadLibrary加载一个exe但是并不执行该exe程序,这样我们就可以像动态库一样使用该exe文件中的资源了,不过此时需要指定动态库加载函数LoadLibrary的第二个参数Flags为:LOAD_LIBRARY_AS_DATAFILE

(6)  Dll由系统维护,可以在各进程之间共享代码,因此,显示动态链接时,每调用LoadLibrary加载一次dll,操作系统就会将对应dll的计数器加1,因此一次LoadLibrary就需要对应一次FreeLibrary来将计数器减1,在动态库加载时可以通过GetModuleHandle来获取判断一个动态库是否被加载,例如:

HMODULEhDll = GetModuleHandle(_T(“add.dll”));

If(NULL==hDll)

        hDll = LoadLibrary(_T(“add.dll”));

GetModule还有另外一个作用:返回应用程序的可执行文件的句柄,此时只需传给它一个NULL参数即可。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/352507.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python建模分析实操_城市公交站点设置优化模型-基于Python

城市公交站点设置的优化分析 一、模型应用 进入21世纪以来&#xff0c;我国城市公共交通飞速发展&#xff0c;然而随着经济社会发展&#xff0c;城市不断升级以及人民生活品质越来越好&#xff0c;城市交通拥堵、出行不便等问题日益突出&#xff0c;严重损坏了市民日常的生活体…

【递归:把目录下所有文件的绝对路径给输出在控制台】

package com.bornsoft.test.capitalpool.tyc;import java.io.File;/*** author shusheng* description* Email shushengyiji.com* date 2018/10/16 10:26*/ public class DiGuiDemo2 {/***需求&#xff1a;请大家把目录下所有文件的绝对路径给输出在控制台*分析&#xff1a;*A:…

UDP sendto和recvfrom使用详解

在网络编程中&#xff0c;UDP运用非常广泛。很多网络协议是基于UDP来实现的&#xff0c;如SNMP等。大家常常用到的局域网文件传输软件飞鸽传书也是基于UDP实现的。 本篇文章跟大家分享linux下UDP的使用和实现&#xff0c;主要介绍下sendto()和recvfrom()两个函数的使用&#xf…

SpringOne Platform 2016回顾

我最近结束了在拉斯维加斯参加SpringOne Platform会议的总结。 这是我第一次参加SpringOne。 这是聆听演讲并与软件开发领域的一些顶级专家进行对话的一种体验。 如果您没有参加SpringOne&#xff0c;那么您肯定会想要阅读这篇文章。 我们将介绍这四个主题&#xff0c;以及如何…

欧姆龙cp1hum读保护解密步骤_欧姆龙PLC的NJ系列NJ产品功能介绍

欧姆龙PLC的NJ 系列NJ运动、逻辑和视觉集于一体欧姆龙PLC的NJ 系列NJ特点One Machine Control运动、逻辑和视觉集于一体将组成机械所需的各种控制设备汇集于一体&#xff0c;使用一个软件即可进行控制。 这就是Sysmac自动化平台的努力目标。 我们的新型机器自动化控制器NJ系列通…

关于CUDA和CuDNN配置的小问题

为了方便组员操作&#xff0c;简单写一下CUDA的配置啦。 首先你需要一台电脑&#xff0c;有NVDIA显卡的那种&#xff08;就那个煤气灶&#xff0c;你懂我意思吧&#xff09;&#xff0c;然后就继续往下走吧&#xff0c;如果没有的话可以找一下右上角的红叉了&#xff0c;这篇文…

PyMongo--非关系型数据库mongodb入门(一步一步 版)

PyMongo--非关系型数据库mongodb入门&#xff08;一步一步 版&#xff09; 本文主要内容&#xff1a; 1.简要介绍mongodb 2.Pymongo 3.mongo shell 4.我的mongodb入门之旅 1.简要介绍mongodb MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的…

python画图颜色种类_Python可视化|matplotlib07-python colormap(颜色映射)(三)

本篇详细介绍matplotlib内置的颜色条Colormap使用。 本文将学到什么&#xff1f; 1、colormap名称 2、colormap可视化 3、colormap使用方法 4、参考资料 更好的阅读体验请戳&#xff1a; 1、colormap名称 colormap颜色通过matplotlib的cm模块调用&#xff0c;print(dir(cm))即可…

element table 组件内容换行方案

element table 组件内容换行方案 white-space的值&#xff1a; normal 默认。空白会被浏览器忽略。pre 空白会被浏览器保留。其行为方式类似 HTML 中的<pre> 标签。nowrap 文本不会换行&#xff0c;文本会在在同一行上继续&#xff0c;直到遇到 <br>标签为止。pre-…

UDP Socket编程 C/C++实现 (Windows Platform SDK)

挺无聊一小程序&#xff0c;俩SB一人说一句&#xff0c;据说聊天程序最好用UDP写。 Server&#xff1a; [csharp] view plaincopy #pragma comment (lib,"ws2_32.lib") #include <Winsock2.h> #include <stdio.h> void main() {…

关于Mongodb的全面总结,学习mongodb的人,可以从这里开始!

MongoDB的内部构造《MongoDB The Definitive Guide》 MongoDB的官方文档基本是how to do的介绍&#xff0c;而关于how it worked却少之又少&#xff0c;本人也刚买了《MongoDB TheDefinitive Guide》的影印版&#xff0c;还没来得及看&#xff0c;本文原作者将其书中一些关于M…

Spring Kafka生产者/消费者样本

我的目的是演示Spring Kafka如何为原始Kafka Producer和Consumer API提供一种易于使用且对具有Spring背景的人熟悉的抽象。 示例场景 示例场景是一个简单的场景&#xff0c;我有一个系统&#xff0c;该系统生成一条消息&#xff0c;另一个系统对其进行处理 使用Raw Kafka Pr…

homelede软路由设置方法_斐讯无线路由器怎么设置 斐讯无线路由器设置方法【详解】...

无线路由器的品类在不断增加&#xff0c;人们的生活也逐渐无法离开WIFI&#xff0c;各类无线路由器的设置方法大同小异&#xff0c;为了方便不为人知的小白更方便的设置无线路由器&#xff0c;这里详细介绍一下斐讯(phicomm)无线路由器怎么设置。配置前准备&#xff1a;A、请保…

Angular 个人深究(四)【生命周期钩子】

Angular 个人深究&#xff08;四&#xff09;【生命周期钩子】 定义&#xff1a; 每个组件都有一个被 Angular 管理的生命周期。 Angular 创建它&#xff0c;渲染它&#xff0c;创建并渲染它的子组件&#xff0c;在它被绑定的属性发生变化时检查它&#xff0c;并在它从 DOM 中被…

BITMAPINFO结构

BITMAPINFO结构定义了Windows设备无关位图&#xff08;DIB&#xff09;的度量和颜色信息。 一、BITMAPINFO结构形式&#xff1a; typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO; 二、BITMAPINFO成员 bmi…

多媒体技术基础第四版林福宗pdf_意大利留学|没有绘画基础,还能考研艺术类专业吗?...

想学艺术专业&#xff0c;却没有绘画基础还能学艺术类专业么&#xff1f;意大利研究生的入学会简单一点 是典型的“宽进严出”想学艺术相关的专业&#xff0c;有2种看你是偏理论&#xff0c;还是偏实践。一起看一下&#xff01;偏实践方面纯艺类&#xff1a;油画、版画、雕塑、…

Spring Boot 1:Introduction

Spring Spring 在不断发展的过程中&#xff0c;边界不断扩张&#xff0c;需要的配置文件也越来越多&#xff0c;使用起来也越复杂&#xff0c;项目中也经常因为配置文件配置错误产生很多问题。即&#xff1a;Spring 逐渐变成了一个大而全的框架&#xff0c;背离它简洁开发的理念…

Pinely Round 3 (Div. 1 + Div. 2)

Pinely Round 3 (Div. 1 Div. 2) Pinely Round 3 (Div. 1 Div. 2) A. Distinct Buttons 题意&#xff1a;当前处于(0, 0)原点&#xff0c;给出若干个平面坐标轴上的点&#xff0c;是否可以仅选择三个方向便可以到达所有给出的点。 思路&#xff1a;到达单一坐标点最多需要…

在MFC,Win32程序中向控制台(Console)窗口输出调试信息

在MFC程序中输出调试信息的方法有两种&#xff0c;一种是使用TRACE宏&#xff0c;可以向Output窗口输出调试信息&#xff1b;另一种是用MessageBox&#xff0c;弹出消息框来输出调试信息&#xff0c;但会影响程序的运行。其实有一种方法可以更为方便的输出调试信息&#xff0c;…

C++程序内存泄漏都与哪些方面有关,该如何处理和避免

动态内存分配有几种: 一个是用了malloc/free, new/delete 第二个使用了第三方的库&#xff0c;库里面的API使用了第一种方法&#xff0c;他们需要你来释放空间&#xff0c;这个会在库的说明文档里有说明。 第三是程序递归&#xff0c;大量的程序递归和互相调用而不推出导致栈空…