-
2018-08-16 15:46:11
在Windows下编程的同学,可能都知道可以使用Depends这个工具查看dll依赖项和导出符号,
却很少知道在命令行下,有两个更好用的命令,分别是dumpbin和lib,这是VS安装目录下的两个程序。dumpbin
用法: DUMPBIN [选项] [文件] 选项: /ALL /ARCHIVEMEMBERS /CLRHEADER /DEPENDENTS /DIRECTIVES /DISASM[:{BYTES|NOBYTES}] /ERRORREPORT:{NONE|PROMPT|QUEUE|SEND} /EXPORTS /FPO /HEADERS /IMPORTS[:文件名] /LINENUMBERS /LINKERMEMBER[:{1|2}] /LOADCONFIG /NOLOGO /OUT:filename /PDATA /PDBPATH[:VERBOSE] /RANGE:vaMin[,vaMax] /RAWDATA[:{NONE|1|2|4|8}[,#]] /RELOCATIONS /SECTION:名称 /SUMMARY /SYMBOLS /TLS
/HEADERS可以查看dll的位数X86/X64,有哪些SECTION;
/DEPENDENTS可以查看依赖项,这和可视化工具Depends功能一样;
/EXPORTS xxx.dll查看导出符号,即dll中包含哪些函数;
/IMPORTS xxx.dll查看从依赖项中具体需要导入的函数;
/LINKERMEMBER xxx.lib则可以查看静态导入库中导入了哪些函数;这里我们要制作dll对应的lib静态导入文件,就需要先产生def文件
由/EXPORTS选项即可查看,但显示的信息不是按照def文件格式来的,需要我们手动调整;Dump of file libReadFace.dll File Type: DLL Section contains the following exports for libReadFace.dll 00000000 characteristics 5B740FAD time date stamp Wed Aug 15 12:34:05 2018 0.00 version 1 ordinal base 45 number of functions 45 number of names ordinal hint RVA name 1 0 00002780 releaseFaceDetectResult 2 1 000028F0 releaseFaceTrackResult 3 2 00007820 rsGetCroppedFaceByTrackId 4 3 00002930 rsGetFaceQualityScore 5 4 000079E0 rsGetRSFaceSDKVersion 6 5 00001DF0 rsGetSDKLicenseDeviceKey 7 6 000042B0 rsInitAttributeDetect 8 7 000079B0 rsInitDepthLivenessDetect 9 8 00007540 rsInitFaceCrop 10 9 00002710 rsInitFaceDetect 11 A 00002910 rsInitFaceQuality 12 B 00004FF0 rsInitFaceRecognition 13 C 000027E0 rsInitFaceTrack 14 D 00001E00 rsInitLicenseManager 15 E 00006460 rsInitLivenessDetect 16 F 00002850 rsInitSingleFaceTrack
pexport
这里我们介绍另一款工具,pexport
下载地址http://sourceforge.net/projects/mingw/files/MinGW/Extension/pexports
这是mingw项目下的一款工具使用命令
pexports -o xxx.dll > xxx.def
即可直接导出标准的def文件
lib
接下来就是使用lib命令,制作对应的lib文件了
用法: LIB [选项] [文件] 选项: /DEF[:文件名] /ERRORREPORT:{NONE|PROMPT|QUEUE|SEND} /EXPORT:符号 /EXTRACT:成员名 /INCLUDE:符号 /LIBPATH:目录 /LIST[:文件名] /LTCG /MACHINE:{ARM|ARM64|EBC|X64|X86} /NAME:文件名 /NODEFAULTLIB[:库] /NOLOGO /OUT:文件名 /REMOVE:成员名 /SUBSYSTEM:{BOOT_APPLICATION|CONSOLE|EFI_APPLICATION| EFI_BOOT_SERVICE_DRIVER|EFI_ROM|EFI_RUNTIME_DRIVER| NATIVE|POSIX|WINDOWS|WINDOWSCE}[,#[.##]] /VERBOSE /WX[:NO]
lib /DEF:xxx.def /MACHINE:X64 /OUT:xxx.lib
至此,我们就可以在MSVC编译器中,使用
#pragma comment(lib, "xxx.lib")
即可直接调用头文件中声明的函数了。
更多相关内容 -
GDI+开发需要的LIB DLL和头文件
2011-11-23 13:30:41需要GDI+开发的可以用,包含GDI+的dll和lib,以及一些头文件,是C++的 -
DLL导出lib文件和.h头文件
2012-06-18 14:16:50还有不少dll导出的都是类,直接显式调用不太方便,最后综合网上的办法,自己再写两个工具,终于实现了将dll导出lib文件,并同时生成.h头文件。 还是自己动手才能丰衣足食啊。 ===========================...最近工作中遇到需要调用别人的动态链接库,但是只有个dll文件,别的一概没有,这可怎么办呢。还有不少dll导出的都是类,直接显式调用不太方便,最后综合网上的办法,自己再写两个工具,终于实现了将dll导出lib文件,并同时生成.h头文件。
还是自己动手才能丰衣足食啊。=========================================================================
问题:如果手上只有一个DLL文件,没有lib文件.h头文件,程序里怎么调用它
方法当然很简单,
先用VC的工具Depends.exe查看导出函数的函数名啊
用IDAPro或者其他反汇编工具分析函数原型
再用API函数:LoadLibrary,GetProcAddress
完成!但是……如果Dll导出的是一个个的类呢,这就稍微有点麻烦
========================================================================
方法1:还是用显式调用,LoadLibrary,GetProcAddress,
这时候就要在头文件里声明一个个的“类成员函数的函数指针”了,
把GetProcAddress返回值赋给这些指针,使用时调用指针即可。如何知道类的成员函数原型是长什么样的呢?
还是Depends工具,如果直接看,会发现导出函数名中有好多"?","@"等符号,Depends的上面有个按钮"C++"(如果没有这个按钮,说明你的Depends太老了,下载个新的吧),点了这个按钮,再看看函数名有啥变化,是不是变得比较好理解了
不过Depends解析得还不够完全,看不出来成员函数是public、protected还是private,也不知道调用约定是__cdecl、__stdcall还是__fastcall,是虚函数还是静态成员函数。
如果要知道这些,还是用VC的undname.exe看得比较全面。=======================================================================
方法2:用lib文件进行隐式链接,这样无需LoadLibrary,个人感觉比较方便的
如何生成lib文件呢,假设动态链接库名为example.dll,步骤如下1、执行dumpbin.exe/EXPORTS example.dll>example.def
生成了一个def文件,里面的内容大概是下面这样:
ordinal hint RVA name
1 0 00004570?function1@@
2 1 00004540?function2@@2、编辑这个def文件,删掉没用的信息,将它整理成这样的格式 :
LIBRARY "example"
EXPORTS
?function1@@ @1
?function2@@ @2
上面的@1和@2是根据第1个步骤中的ordinal序号来的这个步骤我写了个程序HandleDef.exe来自动修改,否则太多函数的话手动改就累死了
3、运行lib.exe/def:example.def
生成了example.lib和example.exp文件。这个lib文件就可以在VC里用了,
比如这样 #pragma comment(lib,"example.lib")4、新建一个文件example.tmp,里面保存函数名
?function1@@
?function2@@
然后运行undname.exeexample.tmp>example.txt
这样函数名就解析到example.txt文件里了5、自己写了个HandleTxt.exe程序,把example.txt转换为example.h
转换后变成类似下面这样:1. class __declspec(dllimport) CExample
2. {
3. public: int function1(void);
4. public: void function2(char);
5. };
这个样子的基本就能直接拷到VC里用了。
不过,如果自己的程序要用到构造函数来新建一个对象的话,对导入类进行声明的时候要事先分析好类对象的内存分布(这可不是个简单的事啊),上面这个CExample类应该这样声明1. class __declspec(dllimport) CExample
2. {
3. public: int function1(void);
4. public: void function2(char);
5. public: BYTE m_data[256];
6. };
//上面m_data具体多少个字节,要靠自己反汇编分析,没法自动生成,具体方法比较麻烦,有兴趣的可以去网上查一些C++类的反汇编资料。
==========================================
总结:
为了方便,把以下内容写到一个批处理文件中
set name=exampledumpbin.exe/EXPORTS %name%.dll>%name%.def
HandleDef.exe %name%.def
lib.exe /def:%name%.def /MACHINE:IX86
undname.exe %name%.tmp>%name%.txt
HandleTxt.exe %name%.txt执行完之后生成lib文件和h文件。
=========================================上面提到的HandleDef.exe和HandleTxt.exe是我自己用C#写的小程序。
写了这么多,也不知道有没有人会感兴趣。
注:我这个还只是适用于VC编写的dll导出的类
如果用extern"c"导出的函数,除了自己反汇编分析原型,貌似没有别的办法
如何调用非VC编译器编译出来的类,暂时未研究过。
下载地址:http://pan.baidu.com/netdisk/singlepublic?fid=795627_2574562052
-
Visual Studio 开发笔记——头文件、lib 和 dll
2021-06-02 18:45:20头文件、lib 和 dll 三者的作用和联系 ① 头文件的作用:声明函数接口 ② lib 库有两种, 静态链接库(Static Library),索引和实现都在其中 动态链接库的导入库,此时 lib 只是一些索引信息,记录了 dll 中函数的...头文件、lib 和 dll
三者的作用和联系
① 头文件的作用:声明函数接口
② lib 库有两种,
- 静态链接库(Static Library),索引和实现都在其中
- 动态链接库的导入库,此时 lib 只是一些索引信息,记录了 dll 中函数的入口和位置,dll 中是函数的具体内容
【Note】链接器怎么知道该调用哪个 DLL 文件呢?
这就是导入库文件的作用:告诉链接器调用的函数在哪个 DLL 中,函数执行代码在 DLL中 的什么位置
这也就是为什么需要在工程属性的『附加依赖项』中需要填入 lib 文件,它起到桥梁的作用。如果生成静态链接库文件,则没有 dll,只有 lib,这时函数可执行代码部分也放在 lib 文件中
③ dll 动态链接库的作用:含有函数的可执行代码。有两种加载方式,隐式链接(需要导入库)和显示连接(不需要导入库)
dll 一般会有对应的导入库,方便程序进行隐式链接加载,否则就需要 自己
LoadLibary
调入 dll 文件,然后再调用GetProcAddress
获得对应函数(划线内容即显示链接加载 dll 方式)。有了导入库,我们只需要链接导入库后按照头文件函数接口的声明调用函数就可以了④ 总结,
头文件是编译时必须的,lib 库是链接时需要的,dll 动态链接库是运行时需要的
如果要完成源代码的编译,只需要 lib;如果要使动态链接的程序运行起来,只需要 dll
三者的引入
① 添加工程的头文件目录
项目 → 属性 → 配置属性 → C/C++ → 常规 → 附加包含目录:加上头文件的存放目录
② 添加文件引用的 lib 静态库路径
项目 → 属性 → 配置属性 → 链接器 → 常规 → 附加库目录:加上lib文件的存放目录
③ 添加工程引用的 lib 文件名
项目 → 属性 → 配置属性 → 链接器 → 输入 → 附加依赖项:加上lib文件名
【Problem】LINK 2019
出现 LINK 2019 的错误,多半都是 lib 文件引入不正确上面的问题,检查链接器附加库目录和附加以依赖项是否正确配置
④ 添加对应的 dll
这个我建议用 everthing 搜一下当前工程下的 dll 文件都放在哪里,再把我们需要的 dll 文件拷进去就行
生成和使用 dll 文件
我们以一个简单加法例子来说明如何生成和使用 dll 文件,我们先创建一个 DLL 项目,取名作 Add,在该项目下面编写如下两个文件,
add.h
文件和add.cpp
// 下列 ifdef 块是创建使从 DLL 导出更简单的宏的标准方法 // 此 DLL 中的所有文件都是用命令行上定义的 ADD_EXPORTS 符号编译的 // 在使用此 DLL 的任何其他项目上不应定义此符号 // 这样,源文件中包含此文件的任何其他项目都会将 // ADD_API 函数视为是从 DLL 导入的, // 而此 DLL 则将用此宏定义的符号视为是被导出的 #ifdef ADD_EXPORTS #define ADD_API __declspec(dllexport) #else #define ADD_API __declspec(dllimport) #endif extern "C" ADD_API int add(int, int); // 注意这里加上extern "C",不然后面调用会出错 // ADD_API int add(int,int);
注意上面代码中的
extern "C"
,虽然在源代码add.cpp
中定义的函数名是 add,但在 dll 的二进制文件中,经过编译器的加工,它实际上变成另外一个名称。这个另外的名称在原有函数名 add 的基础上加上了一些函数输入输出参数的信息,这样做主要是为函数重载服务的,所以如果直接这样调用 dll,程序就会一直提示找不到 dll,因为函数名不匹配,解决方法有两种,- 一是运用
extern "C"
修饰 add,就像我们上面那样 - 二是运用模块定义文件
.def
【Note】为什么要加
extern "C"
详细可以参考这篇博客 C++调用C函数,为什么要加extern “C”?
我们的
add.cpp
文件为// add.cpp : 定义 DLL 应用程序的导出函数 #define ADD_EXPORTS #include "add.h" //#include <stdafx.h> // 这是导出函数的一个示例 ADD_API int add(int x, int y) { int sum = x + y; return sum; }
然后不着急点调试项目,在资源管理器中右键我们的 Add 项目,点击生成,如果成功,Add.lib 和 Add.dll 文件就创建好了
【Problem】提示 dll 不是有效的win32应用程序
调试 dll 应该附加到一个 exe 进程中,我们现在还没有 exe 程序,自然会报错
【Problem】在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加 #include “pch.h”
解决方法 参考这里
隐式调用
接着,我们在同一个解决方案下面新建一个项目 UseAddDll_Implicit,创建的是控制台引用程序,然后在
UseAddDll_Implicit.cpp
文件里编写如下代码#include <iostream> #include "add.h" using namespace std; int main() { int x, y, sum; cin >> x >> y; sum = add(x, y); cout << sum << endl; }
这时候会发现找不到
add.h
文件,这是正常的,因为我们还没有调用 dll将
add.h
文件和Add.lib
、Add.dll
复制到 UseAddDll_Implicit 对应的工程文件夹里面,然后再在工程的属性中添加 lib 文件,不会添加 lib 文件的可以参照下面关于错误的解决方法
【Problem】文件无效或损坏无法在 0x379 处读取
问题发生原因:链接器→输入→附加依赖项,这里面应该是 lib 文件,但错误填写了 dll 文件
显示调用
我们还是在同一个解决方案下面新建一个项目 UseAddDll_Explicit,创建的是控制台引用程序,然后把 Add.dll 文件拷到新项目的文件下,再在
UseAddDll_Explicit.cpp
文件里编写如下代码#include <Windows.h> #include <iostream> // Define a function pointer typedef int(*FUNA)(int, int); using namespace std; int main() { const char* dllName = "Add.dll"; const char* funName = "add"; HMODULE hDLL = LoadLibrary(dllName); if (hDLL != NULL) { FUNA fp1 = FUNA(GetProcAddress(hDLL, funName)); if (fp1 != NULL) { int x, y, sum; cin >> x >> y; sum = fp1(x, y); cout << sum << endl; } else { cout << "Func add cannot be found." << endl; } FreeLibrary(hDLL); } else { cout << "Add.dll cannot be loaded." << endl; } }
说明两点,
- LoadLibrary、FreeLibrary、GetProcAddress 等都是 Win32 API 函数,包含在头文件 Windows.h 中
- 当程序不再使用 dll 时,应及时调用 FreeLibrary 释放占用的内存空间
【Problem】const char* 类型的实参与 LTCWSTR 类型的形参不兼容
解决方法 参考这里
dll 导出函数名称规范化
我们可以使用 VS 自带的 dumpbin 工具查看 dll
在 VS 界面里面去找工具,然后在命令行里面找到开发者 PowerShell,打开后输入下面命令
dumpbin /exports ./Debug/Add.dll
,接着我们可以下面的内容当然在前面部分我们已经使用过
extern "C"
来修饰函数,如果不的话,我们将会看到下面这种的函数名
不使用extern "C"
时add.h
和add.cpp
文件编写如下#ifdef ADD_EXPORTS #define ADD_API __declspec(dllexport) #else #define ADD_API __declspec(dllimport) #endif ADD_API int add(int, int); // 注意这里没有使用 extern "C"
// add.cpp : 定义 DLL 应用程序的导出函数 #define ADD_EXPORTS #include "add.h" //#include <stdafx.h> // 这是导出函数的一个示例 ADD_API int add(int x, int y) { int sum = x + y; return sum; }
这种输出相当不友好,会为 dll 的使用带来很多不便,例如在 LINK2019 的错误出现时,就会出现这种情况。所以,我们希望采用一定的方式使导出的函数修饰名称规范一些
当然
extern "C"
就是一种方法,还有一种是运用模块定义文件 def,这种方法的效果更好我们在 Add 工程中添加一个
Source.def
文件,文件中编写如下的内容LIBRARY Add EXPORTS add = ?add@@YAHHH@Z
然后再用 dumpbin 查看,结果发现函数名成功的被我们修改了
但是,我们还发现那个奇怪的函数名还是存在,这时可以修改Add.h
,将#define ADD_API __declspec(dllexport)
修改为#define ADD_API
,这时的结果就比较满意了生成和使用 lib 文件
我们新建一个静态库项目,取名为 AddLib,静态库项目没有 main 函数,也没有像动态链接库中的
dllmain.cpp
文件,创建完项目后自己添加addlib.h
和addlib.cpp
文件#ifndef _MYLIB_H_ #define _MYLIB_H_ extern "C" int add(int, int); #endif
#include "addlib.h" #include <iostream> int add(int x, int y) { int sum = x + y; return sum; }
调用 lib
调用静态库需要使用头文件和 lib 文件
同样我们还是新建一个工程,然后 将头文件和 lib 文件拷贝到新建的工程目录下面,然后再配置链接器的附加依赖项(或者直接在源代码中加入
#pragma comment(lib, "addlib.lib")
),在源文件中添加addlib.h
,然后就可以在UseAddLib.cpp
文件中调用 lib 了#include "addlib.h" #include <iostream> using namespace std; int main() { int x, y, sum; cin >> x >> y; sum = add(x, y); cout << sum; }
【Note】将现有的项目文件添加到另外一个项目中
我们可以将现有的项目文件拷贝到新的项目文件夹中,然后在 VS 的资源管理器中点击显示所有文件,之后右键点击拷贝过来的文件,选择包含到项目中,这样就好了
我们可以通过次方法将
addlib.h
文件引入到 UseAddLib 工程里面,当然也可以新建一个头文件,然后将代码复制过来
【By the way】哪里去找生成的 lib 文件
生成和使用 exe 文件
如果当前项目调试通过,那么 VS 本身就会在当前工程目录的 Debug 文件夹下生成 exe可执行文件,非常方便
为什么要使用 dll
代码复用是提高软件开发 效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用
『白盒复用』:像是 ATL、MFC等许多应用程序框架,它们都以源代码的形式发布。由于这种复用是源码级别的,源代码完全暴露给了程序员,因而称之为白盒复用
白盒复用的缺点 比较多,总结起来有4点,
- 暴露了源代码
- 容易与程序员的普通代码发生命名冲突
- 多份拷贝,造成存储浪费
- 更新功能模块比较困难
实际上,以上四点概括起来就是,暴露的源代码造成代码严重耦合。为了弥补这些不足,就提出了 『二进制级别』 的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为 『黑盒复用』
在 Windows 系统中有两种可执行文件,其后缀名分别为
.exe
和.dll
它们的区别在于,
.exe
文件可被独立的装载于内存中运行;而.dll
文件却不能,它只能被其它进程调用。然而无论什么格式,它们都是二进制文件。上面说到的二进制级别的代码复用,就可以使用.dll
来实现与白盒复用相 比,dll 很大程度上弥补了上的几个缺陷,
- dll 是二进制文件,因此隐藏了源代码
- 如果采用显式调用,一般不会发生命名冲突
- 由于 dll 是动态链接到应用程序中去的,它并不会在链接生成程序时原原本本拷贝进去
- 最后,dll 文件相对独立的存在,因此更新功能模块是可行的
当然,实现黑盒复用的途径不只 dll 一种,静态链接库甚至更高级的 COM 组件都是
动态链接库虽然一定程度上实现了黑盒复用,但仍存在着诸多不足,例如
- dll 节省了编译期的时间,但相应延长了运行期的时间,因为在使用 dll 的导出函数时,不但要加载 dll,而且程序将会在模块间跳转,降低了 cache 的命中率
- 若采用隐式调用,仍然需要头文件、lib 文件和 dll 文件三件套,并不能支持完全高效模块的更新
- 显式调用虽然很好地支持模块的更新,但却不能导出类和变量。
- dll 不支持 template
二进制级别的代码复用相比源码级别的复用已经有了很大的进步,但在二进制级别的代码复用中,dll 显得太古老。想真正完美实现跨平台、跨语言的黑盒复用,采用 COM 才是真正正确的选择
-
NTDLL导出API的头文件及LIB文件
2010-02-08 12:42:42资源为NTDLL的导出API的头文件及LIB文件 -
.h(头文件) .lib(库文件) .dll(动态链接库文件) 之间的关系和作用的区分
2021-07-31 11:12:36.h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果也使动态连接的程序....h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。
附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果也使动态连接的程序运行起来,有dll就够了(放在Debug文件夹里)。在开发和调试阶段,当然最好都有。
.h .lib .dll三者的关系是:
H文件作用是:声明函数接口
DLL文件作用是: 函数可执行代码
当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道该调用哪个DLL文件呢?这就是LIB文件的作用: 告诉链接器 调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置,这也就是为什么需要附加依赖项 .LIB文件,它起到桥梁的作用。如果生成静态库文件,则没有DLL ,只有lib,这时函数可执行代码部分也在lib文件中
目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。在动态库的情况下,有两个文件,而一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。
-------------------------------------------------------------------------------------
静态链接库(Lib)与动态链接库(DLL)的区别
静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
“每一个lib文件就是若干函数(假设只有函数)的定义”
lib库有两种,一种是包含了函数所在DLL文件和文件中函数位置的信息,称为导出库;一种是包含函数代码本身,一般现有的DLL,用的是前一种库;以前在DOS下的TC/BC等,是后一种库。包含函数原型声明的,是头文件(.h)。
“通过#include包含这些函数声明的头文件后,我们的应用程序就可以使用lib文件中的函数”
还要指定编译器链接相应的库文件。在IDE环境下,一般是一次指定所有用到的库文件,编译器自己寻找每个模块需要的库;在命令行编译环境下,需要指定每个模块调用的库。
“那他和直接给出那个函数定义的文件,比如.cpp文件,和头文件有什么区别,静态链接库有什么用”
cpp文件是源代码,库文件是编译后的二进制代码,比如你可以调用Windows的API,但是不能看到其源代码一样。
“还有不明白的是,静态链接库中的lib文件只要用到,则整个lib文件的内容都放进了exe文件中,那它是被编译进去还是链接的时候连接进去的呢?”
是在链接的时候将lib链接到目标代码中。
静态链接库(Lib)
在VC++6.0中new一个名称为libTest的static library工程,
并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下:
//文件:lib.h
#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y); //声明为C编译、连接方式的外部函数
#endif
//文件:lib.cpp
#include "lib.h"
int add(int x,int y)
{
return x + y;
}
编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add函数了。
标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。
下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall工程。libCall工程仅包含一个main.cpp文件,它演示了静态链接库的调用方法,其源代码如下:
#include <stdio.h>
#include "..\lib.h"//不可丢失
#pragma comment( lib, "..\\debug\\libTest.lib" ) //指定与静态库一起连接
int main(int argc, char* argv[])
{
printf( "2 + 3 = %d", add( 2, 3 ) );
}
静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。代码中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本文件生成的.obj文件应与libTest.lib一起连接
-------------------------------------------------------------------------------------------
用VC++生成静态库文件
今天闲着没事做,自己写了一点小笔记,不知道对于新手有没用,高手就不用看了,作为新手的我斗胆来发表一个笔记,就是静态库文件的封装过程,使用VC++6.0编写,下面是正文,也许我的用语并不专业
以前我们写C/C++源文件的时候,都是先将各个写好的源文件编译,编译生成的是目标文件机器码,即.obj文件.(目标文件的扩展名不一定是.obj文件).
我们调用的标准C/C++函数机器码实际被封装于标准C/C++静态库文件中的.即那些扩展名为.lib的文件中.
最后链接器将我们编译的各个目标文件里的机器码和静态库(标准C/C++库)中的函数机器码链接到一起形成一个扩展名为.exe的可执行文件模块.
在这里我们叙述将C/C++源文件编译链接成一个静态库文件,但它不是可执行模块,它体内含有可执行机器码
静态库文件就像一个仓库或者容器,里面封装了一些可执行机器码.这些机器码是我们用程序设计语言,比如C/C++源文件编译后生成的机器码.
一.下面将讨论将C/C++源文件编译并链接成一个静态库文件的过程,
在VC++6.0中选择File-New-Win32 Static Library,写好工程名创建好工作空间后再选择菜单中New-File来为工程添加C或者C++ 源文件.
假如我们为该工程添加了一个名为lib_c.c和一个名为lib_cpp.cpp的源文件
//lib_c.c中的内容
extern int Add(int x,int y) //该函数是一个外部函数,任何文件都可以访问它
{
return x+y;
}
extern int data_c
//这是一个外部全局变量,任何文件可以访问它
//lib_cpp.cpp中的内容
extern “C” int
reduce(int x,int y)//这里加了个”C”表示允许C源文件访问这个C++函数代码
{
return x-y;
}
extern “C” int data_cpp=2;
注意以下几点
(1)当“extern”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量任何文件可以访问,“extern”关键字可以省略不写,缺省下就是”extern”
当“extern”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“extern”关键字修饰定义的函数或全局变量.
(2)当”static”关键字修饰在函数或全局变量的定义中时,表示该函数或全局变量只能由本文件中加了”static”关键字修饰的函数声明或全局变量声明来引用.
当”static”关键字修饰在函数声明或全局变量声明中时,表示限定当前文件只能引用用“static”关键字修饰定义的函数或全局变量.
(3)在CPP源文件的函数和全局变量定义中加了个”C”表示允许C源文件访问该函数和全局变量.如果是C++源文件访它们的话则可加可不加.注意这”C”要大写.
接下来就要将写好的C/C++源文件进行编译和链接,最后会生成一个扩展名为.lib的文件.该文件就是静态库文件了,该静态库文件是不能直接运行的,我们所编译的C/C++源文件的机器码就已经被封装进这个用VC++6.0创建的静态库文件里面去了.
二.如何将编写好的静态库文件像使用C/C++标准库那样使用,下面将继续讨论
1.用VC++6.0新建一个工程名为TEST,添加一个名为TEST.c的源文件到该工程,因为我们将测试一下,将我们编写的库文件里的函数或者全局变量的机器码链接到我们这个TEST.c源文件中去,假设我们生成的库文件名为TEST.lib,先拷贝如下范例代码到TEST.c中
//TEST.c
#include <stdio.h>
extern int
Add(int x,int y); //当前文件只能访问“extern”关键字修饰定义的Add函数
extern int
reduce(int x,int y);// //当前文件只能访问“extern”关键字修饰定义的reduce函数
#pragma comment(lib,"TEST.lib") //指示链接器到字符串所表示的文件路径中去找库文件
int main()
{
printf("%d\n",Add(2,3));
printf("%d\n",reduce(3,2));
return 0;
}
这里我们要声明静态库中已知的函数或全局变量的声明
#pragma comment(lib,"TEST.lib")这条指令告诉链接器到字符串所表示的路径下去找库文件,这里我将库文件放到了当前工程目录下.也可以不写这句.
还有一种方法,可以直接在VC++6.0中设置依次选择tools、options、directories、library files菜单或选项,填入库文件路径(只键入库文件所在目录路径而不能输入库文件名),这只是告诉链接器库文件所在目录的路径,还没告诉链接器库文件名,方法是VC++6.0中设置依次选择project-settings-link 在object/library modules: 这栏输入库文件名字然后就OK了
2.当用C++源文件的目标文件和库文件的代码链接时有一点小改变,这里就不浪费口舌了,假设我们新建了一个工程并添加了一个名为TEST.CPP的源文件,拷贝如下范例代码到TEST.CPP中
//TEST.cpp
#include <stdio.h>
extern “C” int
Add(int x,int y); //表示引用的是C函数代码
extern int
reduce(int x,int y);
#pragma comment(lib,"TEST.lib")
int main()
{
printf("%d\n",Add(2,3));
printf("%d\n",reduce(3,2));
return 0;
}
在这个C++源文件里引用C函数代码同样要加个”C”,但是在C源文件引用C++函数代码不能加”C++”,编译会报错,只能在C++文件函数定义中加”C”.
只有C++才支持这种引用方式,也许因为只有C++兼容C而没有C兼容C++这一原则..h用于编译阶段的审核,如在math.h中有函数声明:
int abs(int);
但是在使用中写为
#include <math.h>
...abs(3,5);
编译器阶段就会检测出错误。
.dll用于运行阶段,如调用SetWindowText()函数等,需要在user32.dll中找到该函数。DLL可以简单认为是一种包含供别人调用的函数和资源的可执行文件。
.lib用于链接阶段,在链接各部分目标文件(通常为.obj)到可执行文件(通常为.exe)过程中,需要在.lib文件中查找动态调用函数(一般为DLL中的函数)的地址信息,此时需要在lib文件中查找,如查找SetWindowText()函数的地址偏移就需要查找user32.lib文件。(.lib也可用于静态链接的内嵌代码)
lib和dll文件的区别和联系
.dll是在你的程序运行的时候才连接的文件,因此它是一种比较小的可执行文件格式,.dll还有其他的文件格式如.ocx等,所有的.dll文件都是可执行。
.lib是在你的程序编译连接的时候就连接的文件,因此你必须告知编译器连接的lib文件在那里。一般来说,与动态连接文件相对比,lib文件也被称为是静态连接库。当你把代码编译成这几种格式的文件时,在以后他们就不可能再被更改。如果你想使用lib文件,就必须:
1? 包含一个对应的头文件告知编译器lib文件里面的具体内容
2? 设置lib文件允许编译器去查找已经编译好的二进制代码
如果你想从你的代码分离一个dll文件出来代替静态连接库,仍然需要一个lib文件。这个lib文件将被连接到程序告诉操作系统在运行的时候你想用到什么dll文件,一般情况下,lib文件里有相应的dll文件的名字和一个指明dll输出函数入口的顺序表。如果不想用lib文件或者是没有lib文件,可以用WIN32 API函数LoadLibrary、GetProcAddress。事实上,我们可以在Visual C++ IDE中以二进制形式打开lib文件,大多情况下会看到ASCII码格式的C++函数或一些重载操作的函数名字。
一般我们最主要的关于lib文件的麻烦就是出现unresolved symble 这类错误,这就是lib文件连接错误或者没有包含.c、.cpp文件到工程里,关键是如果在C++工程里用了C语言写的lib文件,就必需要这样包含:
extern "C"
{
#include "myheader.h"
}
这是因为C语言写的lib文件没有C++所必须的名字破坏,C函数不能被重载,因此连接器会出错。
C语言中有一些函数不需要进行编译,有一些函数也可以在多个文件中使用。一般来说,这些函数都会执行一些标准任务,如数据库输入/输出操作或屏幕控制等。可以事先对这些函数进行编译,然后将它们放置在一些特殊的目标代码文件中,这些目标代码文件就称为库。库文件中的函数可以通过连接程序与应用程序进行连接。这样就不必在每次开发程序时都对这些通用的函数进行编译了。
不同类型的应用程序将会使用不同的函数库。例如:libdbm库中组包含了对数据库文件进行访问的dbm函数,需要对数据库进行操作的程序就会与该库进行连接。数学应用程序将使用数学库libm,X-Windows应用程序将使用Xlib库,libX11。另外,所有的程序都将使用标准的C函数库。libc,该库中包含了诸好内存管理或输入输出操作的基本函数,这些库都存放在/usr/lib这些系统公用的目录中,系统中的任何用户都可以利用这些库。当然用户也可以建立自己专用的库函数,供自己或其它指定的人员使用。
库可以有三种使用的形式:静态、共享和动态。静态库的代码在编译时就已连接到开发人员开发的应用程序中,而共享库只是在程序开始运行时才载入,在编译时,只是简单地指定需要使用的库函数。动态库则是共享库的另一种变化形式。动态库也是在程序运行时载入,但与共享库不同的是,使用的库函数不是在程序运行开始,而是在程序中的语句需要使用该函数时才载入。动态库可以在程序运行期间释放动态库所占用的内存,腾出空间供其它程序使用。由于共享库和动态库并没有在程序中包括库函数的内容,只是包含了对库函数的引用,因此代码的规模比较小。
lib是静态库,dll一般是动态链接库(也有可能是别的)
比如要编译个exe,lib在编译的时候就会被编译到exe里,作为程序的一部分
而dll是不被编译进去,是运行的时候才调入的(可能是exe刚运行就调入,也可能运行了一半才调入)
用法,lib需要个.lib文件和一个.h文件,程序正常使用.h的函数,在链接选项里加入.lib文件就ok
dll用法有2种,一是 .h + .lib + .dll的,用法和前面一样,中间的lib是个中转,运行的时候会调用dll
二是:直接用dll,需要知道dll的函数定义,用LoadLibrary和GetProcAddress把函数指针取出来,看msdn的例子吧 -
cmake使用-生成头文件
2021-12-19 23:29:38目录结构如下: lqd@ubuntu:~/lqd/cmake/test$ ...├── lib │├── CMakeLists.txt │├── sum.c │└── sum.h └── src ├── CMakeLists.txt └── main.c 5 directories, 7 files 目标: ... -
VS2010中lib与dll文件的生成与使用方法
2020-12-25 18:51:001)、静态lib文件:将导出的文件的声明和实现都放在lib文件中,此时lib文件主要包含函数的实现部分(cpp文件),例如类的函数定义。使用时只需配合相关的头文件,编译后程序将lib文件中的代码嵌入到宿主程序中,也... -
vc中dll导出导入简单教程
2015-03-18 17:47:48dll和lib的区别未完待续如何导出dll查阅msdn官方文档发现,导出dll有三种方式,一种是使用.def文件导出,另一种是在代码中使用宏__declspec(dllexport)导出,最后一种是配置vc工程的属性,使用LINK 命令中的 /EXPORT... -
.h(头文件)、.lib(库文件)和.ddl(动态链接库文件)三者的作用与联系
2021-11-19 10:41:39.h(头文件)是编译时必须的,lib(库文件)是链接时需要的,dll(动态链接库文件)是运行时需要的。 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件。如果要完成源代码的编译和链接,有头文件和lib... -
h头文件、lib库文件及dll动态库文件之间的关系
2017-02-03 19:39:14h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。 附加依赖项的是.lib不是.dll,若生成了dll,则肯定也生成lib文件。如果要完成源代码的编译和链接,有头文件和lib就够了。如果要使需要动态连接... -
.dll、.lib、.h的生成导出和调用其他第三方库的相关这个
2019-03-28 19:50:24动态库相关的使用 目录 动态库相关的使用 1.选择生成.dll项目...3.将生成的*.dll和*.lib文件都直接导出到自定义的指定目录 4.VS 生成*.exe文件到指定目录 5.包含指定的.h和.lib文件夹,方便使用和调用。 ... -
静态库导出函数、导出类
2021-05-17 11:34:16静态库包含类和函数动态库中dll类和全局方法是全局的 类中的方法非静态的要生成对象才能访问静态库1、函数导出//文件:lib.h#ifndef LIB_H#define LIB_Hextern "C" int add(int x,int y); //声明为C编译、连接方式... -
llinux系统头文件、C标准库头文件简介
2021-05-18 10:09:24头文件概述:在进行有关系统软件的安装的时候(编译一个新的驱动,或者安装一个系统级别的测试工具,例如systemtap),经常需要重新编译内核,相应的问题往往与内核头文件有关。那么,什么是内核头文件,为什么需要... -
c++ 头文件和库
2021-07-28 11:25:231 c++ 头文件和库的位置与编译器类型有关。 2 两种编译器 gcc是一个编译器集合,不是单一编译器。使用autotools,make作为build工具。使用gdb作为debugger。使用ld作为linker。 clang/llvm是一个编译器工具链。使用... -
Windows下查看library(即.lib文件)导出函数或32、64位编译等信息的方法
2017-03-31 11:17:13开发人员都知道,查看DLL或exe文件导出函数、依赖文件等信息,使用Depends即可,Depends.exe随VC6.0平台发布。 但是,Depends却不能想查看静态库.lib文件的相关信息,那如果想 1)查看.lib文件信息; 2)没有安装VC... -
由dll导出lib文件
2017-01-16 11:48:30只有dll文件(my.test.dll)和头文件,没有lib文件,需要导出lib,静态编译进代码中,用VS自带的dumpbin.exe和lib.exe来完成。 dumpbin.exe和lib.exe的路径不赘述,路径见下图: 先用如下命令行导出dll的... -
linux下的头文件和库文件搜索路径 (转)
2021-05-09 06:39:40GCC 找头文件有三种策略:1. 会在默认情况下指定到 /usr/include 文件夹 ( 更深层次的是一个相对路径, GCC 可执行程序的路径是 /usr/bin ,那么它在实际工作时指定头文件头径是一种相对路径方法,换算成绝对路径... -
【Qt 导出生成并使用第三方库】01:导出C++内容的DLL 并使用
2022-01-24 10:54:07第三方库/程序是别人提供的库/程序(例如开源库/程序, 或者我们从别的公司买过来的基础库/程序) 如ffmpeg库 opencv库等 在第二篇我会讲解 如何在QT中调用opencv第三方库 为什么要用第三方库 优点避免重复造轮子,... -
C/C++混编,导出dll时,只有dll,没有lib
2020-07-21 17:37:411 C/C++混编,导出dll时,只有dll,没有lib 下面代码copy自网上,乍一览,没啥问题,直接用了 然而…就是导不出lib文件,只有dll怎么看都看不出问题… 求助于火眼金睛的同事后,发现,多了分号 2 #define的定义是直接将后面... -
理解头文件(.h)、库文件(.lib)、和动态链接库文件(.dll),Fortran中的预处理及Fortran中function的简单...
2020-09-01 18:15:17文章目录问题来源我的问题头文件、库文件和动态链接库头文件.h库文件.lib动态链接库.dll三者的关系静态链接动态链接初识 Fortran 预处理包含文件 includeFortran中function简单使用声明interface调用function声明... -
Clucene库以及自己封装的方法库(补充)——对应的头文件和.lib索引文件
2015-05-12 14:11:32上次那个库对应的.h和.lib忘了共享了。在这里补上 -
Qt如何导出DLL并使用
2020-11-18 15:49:06Qt如何导出DLL并使用 ** 1、建立DLL文件 1.1 打开Qt,按照下图的操作完成DLL文件创建向导。 1.2 在myclockdll.h文件中添加如下代码 #ifndef MYCLOCKDLL_H #define MYCLOCKDLL_H #include "myclockdll_global.h" ... -
unity-cpp-lib:对Unity插件使用c ++库的简单演示
2021-05-12 00:23:18此库描述了如何以动态链接库(DLL)的形式构建C ++库,封装C ++类和导出函数。 另一方面,此存储库还显示了如何通过c#脚本在Unity中导入和使用库(DLL)。 如何使用回购 此仓库包含两个文件夹: SampleCppDll-... -
MFC扩展DLL中导出类和对话框的实现方法
2020-09-04 02:06:21主要介绍了MFC扩展DLL中导出类和对话框的实现方法,详细讲述了实现扩展DLL中导出类和对话框的具体步骤与方法,具有不错的实用价值,需要的朋友可以参考下 -
vs2010创建DLL工程并导出dll和lib文件
2021-04-07 22:41:50生成dll、lib文件完成! 1. 创建DLL类型Win32项目 下一步: 应用程序类型选择DLL 2. 编写自己的代码 添加"dll_create.h"头文件,定义接口 #define DLL_EXPORTS #ifdef DLL_EXPORTS #define DLL_API __... -
解析VC中创建DLL,导出全局变量,函数和类的深入分析
2020-09-05 08:50:50本篇文章是对VC中创建DLL,导出全局变量,函数和类进行了详细的分析介绍,需要的朋友参考下 -
用于查找给定的项目在给定文本文件中的存在性的C函数(含头文件、静态库、动态库和说明文档)
2022-01-19 10:52:53- 本函数以C语言写就,并以标准的C格式导出;所用IDE为Microsoft Visual Studio 2019,编译器遵循C17标准; - 请注意,目标文本文件的一行被视为一个项目,即以`'\n'`分隔项目,若您的目标文本文件非此格式,烦请稍...