1 DLL LibStatic Lib的区别
一种是LIB包含了函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供,称为动态链接库dynamic link library。动态链接使用动态链接库,允许可执行模块(.dll文件或.exe文件)仅包含在运行时定位DLL函数的可执行代码所需的信息。应用程序发布的时候必须包含动态链接库。
一种是LIB包含函数代码本身,在编译时直接将代码加入程序当中,称为静态链接库static link library。静态链接使用静态链接库,链接器从静态链接库LIB获取所有被引用函数,并将库同代码一起放到可执行文件中。应用程序发布的时候无需包含静态链接库。
Opencv为什么使用静态链接库又使用动态链接库?下面是解释:当lib和dll都有的时候,lib中一般是索引信息,记录dll的函数入口和位置,dll中是函数的具体内容,因此所有使用opencv编写的程序都必须将其dll拷贝到没有安装opencv的机器上运行。如果只有lib,那么lib是静态编译出来的,索引和实现都在其中。
关于lib和dll的区别如下:
(1)lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。
(2)如果有dll文件,那么lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体内容;如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。使用静态编译的lib文件,在运行程序时不需要再挂动态库,缺点是导致应用程序比较大,而且失去了动态库的灵活性,发布新版本时要发布新的应用程序才行。
(3)动态链接的情况下,有两个文件:一个是LIB文件,一个是DLL文件。LIB包含被DLL导出的函数名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到DLL文件。在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中相应函数代码的地址,从而节省了内存资源。DLL和LIB文件必须随应用程序一起发行,否则应用程序会产生错误。如果不想用lib文件或者没有lib文件,可以用WIN32 API函数LoadLibrary、GetProcAddress装载。
使用lib需注意两个文件:
(1).h头文件,包含lib中说明输出的类或符号原型或数据结构。应用程序调用lib时,需要将该文件包含入应用程序的源文件中。
(2).LIB文件,略。
使用dll需注意三个文件:
(1).h头文件,包含dll中说明输出的类或符号原型或数据结构的.h文件。应用程序调用dll时,需要将该文件包含入应用程序的源文件中。
(2).LIB文件,是dll在编译、链接成功之后生成的文件,作用是当其他应用程序调用dll时,需要将该文件引入应用程序,否则产生错误。如果不想用lib文件或者没有lib文件,可以用WIN32 API函数LoadLibrary、GetProcAddress装载。?
(3).dll文件,真正的可执行文件,开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,并不需要.lib文件和.h头文件。
2 lib文件的生成和使用
静态lib中,一个lib文件实际上是任意个obj文件的集合,obj文件是cpp文件编译生成的。在编译这种静态库工程时,根本不会遇到链接错误;即使有错,也只会在使用这个lib的EXT文件或者DLL工程里暴露出来。
使用动态链接中的lib,不是obj文件的集合,即里面不会有实际的实现,它只是提供动态链接到DLL所需要的信息,这种lib可以在编译一个DLL工程时由编译器生成。
1 生成lib的方法:
1 创建专门的工程
在VC中新建一个Win32 staticlibrary类型的工程Lib,加入test.cpp文件和test.h文件(头文件内包括函数声明),然后编译,就生成了Lib.lib文件。
2 不创建单独的工程
选择Static Lib然后将测试main函数删除,编译即可。
2 使用lib有两种方式:
(1)在project->link->Object/LibraryModule中加入Lib.lib文件(先查询工程目录,再查询系统Lib目录);或者在源代码中加入指令#pragmacomment(lib, “Lib.lib”)。
(2)将Lib.lib拷入工程所在目录,或者执行文件生成的目录,或者系统Lib目录中。
(3)加入相应的头文件test.h。
注意:Debug和Release下的lib是不同的,因此必须添加两个lib
问题:如何在程序中设置debug和release生成的lib的名字?
3 dll文件的生成和使用
1 生成dll文件
1 Win32下创建Dll
1.在VS中创建名为选择模版”Win32Project” ,创建一个名为”Math”的项目:
2.在向导中,选择程序类型为”Dll”,附加选项为”Emptyproject”:
3.由于选择的是空项目,所以解决方案管理器中现在还没有任何代码文件。
4.手动添加两个文件:Math.h和Math.cpp,其中包含了一个一个类Math,一个全局函数Multiply()和一个全局变量GlobalVariable:
1: //Math.h
2: #pragma once
3: class Math
4: {
5: public:
6: Math(void);
7: ~Math(void);
8:
9: public:
10: int Add(int a,int b);
11: };
12:
13: int Multiply(int a,int b);
14:
15: extern int GlobalVariable;
1: //Math.cpp
2: #include "Math.h"
3:
4: Math::Math(void){}
5: Math::~Math(void){}
6: int Math::Add(int a, int b)
7: {
8: return a+b;
9: }
10:
11: int Multiply(int a,int b)
12: {
13: return a*b;
14: }
15:
16: int GlobalVariable = 101;
5.打开Math项目的属性对话框,注意,在ConfigurationProperties—>C/C++—> Preprocessor—> Preprocessor Definitions中已经定义了一个预处理符号:MATH_EXPORTS
6. 编译此项目,将在解决方案的Debug目录下生成一个Math.dll文件,但这个动态链接库是无法直接使用的。如果其他项目想要使用此dll,还需要导出一个lib文件。
7.在Math.h中添加几行预处理指令(用到了项目属性中已定义好的MATH_EXPORTS),并在每个需要导出的类,函数和全局变量前面加上导出标记MATH_API。修改后的Math.h为:
1: //Math.h
2: #pragma once
3:
4: #ifdef MATH_EXPORTS
5: #define MATH_API __declspec(dllexport)
6: #else
7: #define MATH_API __declspec(dllimport)
8: #endif
9:
10: class MATH_API Math
11: {
12: public:
13: Math(void);
14: ~Math(void);
15:
16: public:
17: int Add(int a,int b);
18: };
19:
20: int MATH_API Multiply(int a,int b);
21:
22: extern MATH_API int GlobalVariable;
8.重新编译Math项目,会在解决方案的Debug目录下生成一个Math.lib和一个Math.dll,以及其他几个调试辅助文件。其他项目想要调用Math.dll,只需要添加Math.h头文件,然后链接Math.lib即可。
2 MFC下创建Dll
(1)新建MFCAppWizard(dll)工程,工程名为MFCDll,选择
RegularDLL using shared MFC DLL类型// 该类型没有将MFC中的DLL编译到其中
RegularDLL with MFC statically linked类型// 该类型将MFC作为静态支持
MFCextension DLL类型//
(2)在生成的MFCDll.cpp文件后面增加下面几行:
intsum(int a, int b)
{
returna+b;
}
(3)在生成的MFCDll.def文件后面增加如下:
sum @1;//表示第一个函数是sum
(4)编译后会产生两个文件MFCDll.lib,MFCDll.dll
2 使用dll的两种方法
(1)隐式链接
第一种方法是:通过project->link->Object/LibraryModule中加入.lib文件(或者在源代码中加入指令#pragmacomment(lib, “Lib.lib”)),并将.dll文件置入工程所在目录,然后添加对应的.h头文件。
#include "stdafx.h"
#include "DLLSample.h"
#pragma comment(lib, "DLLSample.lib") //你也可以在项目属性中设置库的链接
int main()
{
TestDLL(123); //dll中的函数,在DllSample.h中声明
return(1);
}
当没有.h文件的时候,调用者必须自己写函数声明,因此最好有头文件!
(2)显式链接
需要函数指针和WIN32 API函数LoadLibrary、GetProcAddress装载,使用这种载入方法,不需要.lib文件和.h头文件,只需要.dll文件即可(将.dll文件置入工程目录中)。
#include <iostream>
#include <windows.h> //使用上述函数
typedef void (*DLLFunc)(int);
int main()
{
DLLFunc dllFunc;
HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll");
if (hInstLibrary == NULL)
{
FreeLibrary(hInstLibrary);
}
// 获取Dll中的函数TestDLL指针
dllFunc = (DLLFunc)GetProcAddress(hInstLibrary, "TestDLL");
if (dllFunc == NULL)
{
FreeLibrary(hInstLibrary);
}
dllFunc(123);
std::cin.get();
FreeLibrary(hInstLibrary);
return(1);
}
LoadLibrary函数利用一个名称作为参数,获得DLL的实例(HINSTANCE类型是实例的句柄),通常调用该函数后需要查看一下函数返回是否成功,如果不成功则返回NULL(句柄无效),此时调用函数FreeLibrary释放DLL获得的内存。
GetProcAddress函数利用DLL的句柄和函数的名称作为参数,返回相应的函数指针,同时必须使用强转;判断函数指针是否为NULL,如果是则调用函数FreeLibrary释放DLL获得的内存。此后,可以使用函数指针来调用实际的函数。
最后要记得使用FreeLibrary函数释放内存。
注意:应用程序如何找到DLL文件?
使用LoadLibrary显式链接,那么在函数的参数中可以指定DLL文件的完整路径;如果不指定路径,或者进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:
(1)包含EXE文件的目录
(2)工程目录
(3)Windows系统目录
(4)Windows目录
(5)列在Path环境变量中的一系列目录
4 生成Lib或dll时遇到的问题:
1 warning 4251
class“std::vector<_Ty>”需要有 dll 接口由 class“XF_CONVNET”的客户端使用
1:情况一
如果类的定义里面仅含有 编译器内置的类型变量, int,float 等等. 或者成员函数仅使用了这些变量作为参数, 那么很简单.
直接
class __declspec(dllexport) YourClass
{
}
就行了.
2:情况二
如果类内部使用了别的类, 那么别的类最好也导出, 不然, 首先编译的时候会出现编译警告:
warning C4251: needs to have dll-interface
意思是,你使用另外的一些类型/接口, 但是这些类型或接口没有导出. 当你的client使用这些类型/接口的时候, 会出错!
class __declspec(dllexport) YourClass
{
YourAnatherClassm_data; // 这里会 出现 warning 4251. 如果YourAnatherClass 没有导出的话.
}
解决办法就是: 在YourAnatherClass定义的地方加上
class __declspec(dllexport) YourAnatherClass
{
}
如上, 当你的YourAnatherClass没有导出的时候, dll的使用方会出现链接错误
3:情况三
当类的内部使用了STL模板的时候, 也会出现C4251警告, 情况会有所不同
class __declspec(dllexport) YourClass
{
vector m_data; // 这里会 出现 warning 4251. 因为vector类型没有被导出
}
上面的使用模板(无论是stl模板,还是自定义模板)的代码,编译dll或者lib时会出现C4251警告, 但是dll或lib的使用方, 却不会出现链接错误!!!
这个因为dll的使用方那里也有一套模板的定义, 当他们使用那个vector的时候, 虽没有导出, 但是用户自己也有一套STL模板(或者是自定义的模板),用户会利用自己的模板实例化这个dll中没有导出的东西!
所以, 对于因为使用STL(或模板)出现的c4251警告, 关闭之即可
#pragma warning(push)
#pragma warning(disable:4251)
//your declarations that cause 4251
#pragma warning(pop)
若想不使用通过关闭警告的方式关闭警告, 那么就这样
1)对于用户自定义的模板
template classDLLImportExportMacro SomeTemplate;
SomeTemplate y;
2)对于STL的模板
templateclass DLLImportExportMacro std::allocator
templateclass DLLImportExportMacro std::vector
std::allocator>;
vectorm_data;
还有一个解释为:
在头文件中,定义宏
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
现在,在变量m_structs前,添加:
template class MYDLL_API std::allocator;
template class MYDLL_API std::vector >;
2 LNK4075
1>ImageScanning1.0.lib(ImageScanning.obj): MSIL .netmodule or module compiled with /GL found; restarting link with/LTCG; add /LTCG to the link command line to improve linker performance
1>LINK : warning LNK4075: ignoring'/INCREMENTAL' due to '/LTCG' specification
3 LNK4098
1>LINK: warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use/NODEFAULTLIB:library
以前经常遇到这个警告信息,因为运行并没有什么问题,所以也没深究。但是耿耿于怀那个“0 个错误,0 个警告”的成功提示,在网上搜了一下。原来问题出在默认库的引用选择上。
VS2008,项目——属性——配置属性——C/C++——代码生成:他有/MT,/MTd,/Md,/MDd四个选项,你必须让所有使用的库都使用相同的配置,否则就会有相应的提示,甚至可能会出现无法解析的函数。有时我们使用的库不是自己可以控制的,那么就只能把工程属性设置成河你使用的库相同的选项。
错误 1 error LNK2005: _free 已经在libcmtd.lib(dbgheap.obj) 中定义 MSVCRT.lib
错误 2 error LNK2005:_malloc 已经在libcmtd.lib(dbgheap.obj) 中定义 MSVCRT.lib
.....
如果有一堆的重定义错误发生在同一个lib中,而且跟它冲突的也是同一个lib,那么这个两个lib的功能应该是一样的,可以2选一,只要在“忽略特定的库”内填入需要忽略的库。
项目属性-配置属性-链接器-输入-忽略特定的库:libcmtd.lib
项目属性-配置属性-常规-MFC的使用:在共享 DLL 中使用 MFC
MSVCRT.lib 和libcmt.lib的冲突还是比较常见的。
从错误信息可以看出是msvcrt.lib和libcmt.lib库中重复定义了__isctype等符号。为什么会出现这样的问题呢?这就要从这两个库的作用说起了。
msvcrt.lib是VC中的MultithreadedDLL 版本的C运行时库,而libcmt.lib是Multithreaded的运行时库。在同一个项目中,所有的源文件必须链接相同的C运行时库。如果某一文件用了MultithreadedDLL版本,而其他文件用了Single-Threaded或者Multithreaded版本的库,也就是说用了不同的库,就会导致这个警告的出现。
告警信息的意思我们明白之后,就要找造成这个问题的原因了。在项目设置中我们可以看到当前项目使用的是Multithreaded非DLL版本的运行时库,这说明项目中还有其他文件用到了不是这个版本的运行时库。很显然,就是openssl的静态库。查看openssl中ms下的nt.mak,我们可以发现静态库版本中openssl使用编译开关/MD进行编译的,也就是说openssl静态库是默认用的MultithreadedDLL 版本的C运行时库。
原因找到了。那么解决方法,很明显有两个。总之就是将两个项目的运行时库统一。
简单的方式就是将项目的动态库修改为使用MultithreadedDLL 版本的C运行时库即可。
某些情况下你的项目可能不能改变当前的运行时库,你可以将openssl的nt.mak中的/MD开关修改为/MT然后重新编译openssl静态库就可以了。
默认库“library”与其他库的使用冲突;请使用/NODEFAULTLIB:library LNK4098 的解决办法
您试图与不兼容的库链接。
注意
运行时库现在包含可防止混合不同类型的指令。如果试图在同一个程序中使用不同类型的运行时库或使用调试和非调试版本的运行时库,则将收到此警告。例如,如果编译一个文件以使用一种运行时库,而编译另一个文件以使用另一种运行时库(例如单线程运行时库对多线程运行时库),并试图链接它们,则将得到此警告。应将所有源文件编译为使用同一个运行时库。有关更多信息,请参见使用运行时库(/MD、/MT 和 /LD)编译器选项。
可以使用链接器的/VERBOSE:LIB 开关来确定链接器搜索的库。如果收到LNK4098,并想创建使用如单线程、非调试运行时库的可执行文件,请使用/VERBOSE:LIB 选项确定链接器搜索的库。链接器作为搜索的库输出的应是LIBC.lib,而非LIBCMT.lib、MSVCRT.lib、LIBCD.lib、LIBCMTD.lib和MSVCRTD.lib。对每个要忽略的库可以使用/NODEFAULTLIB,以通知链接器忽略错误的运行时库。
下表显示根据要使用的运行时库应忽略的库。
若要使用第一行运行时库 请忽略第2行的这些库
单线程(libc.lib)
libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib
多线程(libcmt.lib)
libc.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib
使用 DLL 的多线程(msvcrt.lib)
libc.lib、libcmt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib
调试单线程(libcd.lib)
libc.lib、libcmt.lib、msvcrt.lib、libcmtd.lib、msvcrtd.lib
调试多线程(libcmtd.lib)
libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、msvcrtd.lib
使用 DLL 的调试多线程(msvcrtd.lib)
libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib
例如,如果收到此警告,并希望创建使用非调试、单线程版本的运行时库的可执行文件,可以将下列选项与链接器一起使用:
/NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib/NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib
defaultlib"library" conflicts with use of other libs; use /NODEFAULTLIB:library
你试图链接相互不兼容的库。
重要提示 目前运行时库包含预编译指令来阻止混合不同类型库的链接。如果你试图在同一个程序中使用不同类型的或者调试/非调试版本的运行时库,你将看到这个警告。举个例子,如果你编译一个文件使用某个类型的运行时库,而别的文件使用另外一个类型(例,单线程版本和多线程版本),那么在你试图链接它们的时候,你将看到这个警告。你应该使用相同的运行时库来编译所有的源文件。参阅MSDN中的Use Run-Time Library 章节了解关于(MD,/ML, /MT, /LD)编译开关的更多信息。
你可以使用链接开关/VERBOSE:LIB来了解链接器在搜索哪个库。如果你试图创建一个使用单线程非调试版本运行库的执行文件时收到LNK4098警告,那么使用/VERBOSE:LIB参数来找出链接器在搜索哪个库。链接器应该列出搜索的库LIBC.LIB,而不是LIBCMT.LIB,MSVCRT.LIB, LIBCD.LIB, LIBCMTD.LIB, 或者MSVCRTD.LIB等库。你可以通过在VC开发环境项目 Project设置Settings 链接Link页面中的IgnoreLibraries编辑框中输入不正确的运行时库名告诉链接器忽略这些运行时库,或者可以使用/NODEFAULTLIB:library 的链接开关来忽略那些不正确的库。参阅MSDN中 IgnoreLibraries 章节了解(/NODEFAULTLIB)链接开关的更多信息。
下面的表中列出了在你选择不同运行时库时应该被忽略的库。
使用的运行时库 | 被忽略的库 |
单线程 (libc.lib) | libcmt.lib, msvcrt.lib, libcd.lib, libcmtd.lib, msvcrtd.lib |
多线程 (libcmt.lib) | libc.lib, msvcrt.lib, libcd.lib, libcmtd.lib, msvcrtd.lib |
多线程DLL (msvcrt.lib) | libc.lib, libcmt.lib, libcd.lib, libcmtd.lib, msvcrtd.lib |
调试版单线程 (libcd.lib) | libc.lib, libcmt.lib, msvcrt.lib, libcmtd.lib, msvcrtd.lib |
调试版多线程 (libcmtd.lib) | libc.lib, libcmt.lib, msvcrt.lib, libcd.lib, msvcrtd.lib |
调试版多线程DLL (msvcrtd.lib) | libc.lib, libcmt.lib, msvcrt.lib, libcd.lib, libcmtd.lib |
举例,如果你试图创建一个使用单线程非调试版运行时库的执行文件时,收到这个警告信息,你可以使用下面参数来链接:
/NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB