您的位置:首页 > 其它

dll和lib的相关问题总结

2015-09-07 12:50 274 查看
之前一直在纠结关于dll和lib的问题,花点时间总结一下,以后肯定会有用的。

一.简介

在编程的时候,最简单的是将源码给我们,在编程的时候,包含头文件和实现文件,但是这样做比较麻烦,而且源码也暴露了。所以,一种更加人性化的方式就是将代码编译成库函数,供我们调用。库里面是木有代码的,换句话说,库里面的代码是已经编译好了的东东,我们肉眼是看不懂的。只能通过头文件,知道库中有神马函数,然后根据头文件调用库中的函数,再直白一点,库就是封装好的黑盒子,我们只管用就好。库函数分为两种,一种是lib库,一种是dll库。
下面分别看一下两种库,并学习一下怎么编译出两种库,并且使用。

二.lib库

lib库,一般称之为静态链接库,其实lib是我们编译过程产生的obj的合集,加上了一些辅助信息,便于我们定位到函数。不过lib也分为两种,一种为纯静态链接的库(静态lib),里面有完整的函数实现(声明和实现),我们包含lib之后,编译链接的过程会将函数的代码加入目标模块,所以链接好了这种lib就没有用了。而还有一种lib是配合着dll(动态链接库)一起使用的(动态lib),这种lib里面木有代码,只是有dll相关定位信息(对实现部分的部分声明),配合链接dll的,链接完也就木有用了,而如果动态载入dll的话,则不需要这种lib配合了。

1.编译lib库

下面看一下怎么编译粗来一个lib库,一个比较简单的例子,只包含了一个类和一个全局的函数。
.h文件:
/*!
 * \file LibTest.h
 *
 * \author puppet_master
 * \date 八月 2015
 *
 * 测试Lib
 */
#pragma once

//写一个全局函数测试
void FuncTest();

//写一个类测试
class LibTest
{
public:
	LibTest(void);
	~LibTest(void);

	void Show();
};


.cpp文件:
#include "stdafx.h"
#include "LibTest.h"
#include <iostream>
using namespace std;

LibTest::LibTest(void)
{
}

LibTest::~LibTest(void)
{
}

void LibTest::Show()
{
	cout<<"LibTest Obj show!"<<endl;
}

void FuncTest()
{
	cout<<"Func Test Show!"<<endl;
}


编译的之前我们需要改一下编译的选项,使之生成lib,而不是一贯的exe。



然后,就可以放心大胆的编译啦!在debug目录会生出来一个LibTest.lib文件。



2.使用lib库

下面使用一下刚才我们编译好的lib文件。有时候lib库或者dll库和最后我们要编译出来的exe是属于同一个解决方案的不同项目的,这种比较简单,但是有时候我们需要不同项目使用lib库或者dll,所以这次,新建一个解决方案,使用我们上面编译好的lib库。
我们新建一个解决方案,在其中新建两个文件夹,一个叫Include(包含头文件目录),一个叫Lib(库文件目录),将刚才的那个项目中的LibTest.h拷贝到Include目录中,将生成的LibTest.lib拷贝到Lib目录中。
然后设置一下这个新项目的包含目录和库目录,包含目录设定为刚才的Include目录,库目录设定为刚才的Lib目录:



注:1.为了能够整体移植,将项目拷贝到其他地方也不需要再配置环境,还是将包含的头文件和Lib文件放到项目目录下比较靠谱(前提是比较小的情况)。当然,放到外面也是可以的。VC的自带的库也就不在项目中,系统也可以找到这些系统库的位置。再比如之前的DX配置环境的时候,就是将包含目录设置到其他位置的。
2.如果直接将Lib和.h文件放到项目中也是可以的,这样就不用设置上面那步,不过不太推荐这样做,尤其是在使用第三方库的时候,这些东西还是单独放到一个目录中比较好。

下面就是使用我们的库文件啦。使用库文件需要有两样东东,第一个是库的.h文件,第二个才是lib库本身。
包含库有两种方式:
第一种是通过 项目->属性->配置属性->连接器->附加依赖项来添加。



第二种是直接通过一句话,在项目中就可以添加相应的库,如:
#pragma comment(lib, "LibTest.lib")


好了,添加完相应的内容之后,就可以使用之前库中的类和函数啦,看一个例子:

// Test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "LibTest.h"
#include <windows.h>
//如果没有直接通过设置添加,可以通过这句添加库
//#pragma comment(lib, "LibTest.lib")

int _tmain(int argc, _TCHAR* argv[])
{
	//测试全局函数
	FuncTest();
	//测试LibTest类
	LibTest obj;
	obj.Show();

	system("pause");
	return 0;
}
结果如下:
Func Test Show!

LibTest Obj show!

请按任意键继续. . .

三.dll库

其实dll库才是比较麻烦的东东,记得小时候,从网上好不容易下载了个大型游戏,一打开,蹦出一个缺少XXX.dll的提示,然后又费了半天劲,下了个dll,塞到目录下,本以为能玩了,一打开,又蹦个框,缺另一个dll,于是一怒之下就删了游戏。

dll库,全称为dynamic link library,即动态链接库,又称为应用程序扩展,是一种更高级,更先进的库,优点很多。dll的作用其实与lib的基本作用是一样的,都是共享代码,lib比较死板,只要包含了lib,那么这个lib就被包含在最终生成的exe应用程序中了。而使用dll的话,这些东东不必被包含在exe中,而是exe可以动态的加载这些dll。这样,我们生成的exe会比较小,而且可以修改dll而不必修改整个exe。
动态链接库可以被各个程序所共享,如果有多个程序都使用这个dll的话,内存中只会有一份dll的拷贝。而且dll与编程语言和编译器无关,只需要遵循dll的接口规范和调用方式就可以相互调用。当然,更爽的是,dll本身还可以包含dll或者lib,而lib却没有这个特性。
下面看一下dll的编译和使用

1.编译dll

上面提到lib有两种,一种是静态的lib即我们上面所说的那种,而另一种lib(动态lib)则是我们在编译dll产生的附加产物。编译dll时,会有两种情况,一种情况是定义导出dll的情况,这种情况会生成lib和dll两个生成文件,而另一种是不定义这种,则只会生成一个dll文件。区别的话,在于使用,如果我们编译dll时还有lib的话,将lib也包含在要用的项目中,就可以隐式加载dll了,不需要自己写LoadLibaray来显示加载,这种情况可以像使用lib一样简单的使用dll。而另一种情况,只有dll时,我们在调用时就比较麻烦了,由于没有lib文件,就需要写一些东东,来显示加载和调用dll了。
还是上面的那个例子:

.h文件
/*!
 * \file LibTest.h
 *
 * \author puppet_master
 * \date 八月 2015
 *
 * 测试dll
 */
#pragma once

//通过一个编译宏控制编译流,如果定义了DLLANDLIB就按照导出dll的方式编译(生成dll和lib),否则只生成dll
#ifdef DLLANDLIB
#define DLLEXPORT _declspec(dllexport)  //生成dll和lib
#else
#define DLLEXPORT //不定义这项,则默认只生成dll
#endif // 

//写一个C风格的函数
extern "C" DLLEXPORT void CFuncTest();

//写一个全局函数测试
DLLEXPORT void FuncTest();

//写一个类测试
class DLLEXPORT LibTest
{
public:
	LibTest(void);
	~LibTest(void);

	void Show();
};
.cpp文件
#include "stdafx.h"
#include "LibTest.h"
#include <iostream>
using namespace std;

LibTest::LibTest(void)
{
}

LibTest::~LibTest(void)
{
}

void LibTest::Show()
{
	cout<<"LibTest Obj show!"<<endl;
}

void FuncTest()
{
	cout<<"Func Test Show!"<<endl;
}

void CFuncTest()
{
cout<<"C Func Test Show"<<endl;
}
这次,我们修改一下编译属性,设置生成dll,并且暂时不添加编译宏。



然后编译,就会在debug目录下生成一个dll



而如果我们设置编译宏为DLLANDLIB的话,就会生成dll和lib两个文件:



看一下:



两种方法都可以,不过建议使用dllexport的方式。据说如果不用这个,可能会导致类中static变量找不到的情况。

2.使用dll

学会了编译dll,下面看一下dll怎么用。还是刚才那个Test的项目,使用dll的话有两种情况,第一种是有.h,.dll,.lib三个文件的情况,这种使用起来比较简单,除了需要将dll放到程序可以找到的目录下,其余与使用lib没有太大区别。而另一种就比较麻烦了,在没有lib的情况下,直接动态导入dll进入程序。

有lib,.h,.dll三个文件时:

首先,和上面的方法一样,将.h文件添加到包含目录,将.lib文件添加到库目录,然后将刚才编译好的dll文件放在debug目录下(项目生成exe文件的地方即可),然后按照和上面一样的方法添加头文件和lib引入。
// Test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "LibTest.h"
#include <windows.h>
//如果没有直接通过设置添加,可以通过这句添加库
//#pragma comment(lib, "LibTest.lib")

int _tmain(int argc, _TCHAR* argv[])
{
	//测试C风格函数
	CFuncTest();
	//测试全局函数
	FuncTest();
	//测试LibTest类
	LibTest obj;
	obj.Show();

	system("pause");
	return 0;
}
编译,运行,结果如下:
C Func Test Show

Func Test Show!

LibTest Obj show!

请按任意键继续. . .

如果我们不把dll添加到exe文件所在的目录,就会出现我们下载完游戏经常出现的那种情况了:



直接使用dll:

另一种方式是动态导入dll,这种情况不需要lib和.h文件,有dll就可以。不过我们需要知道dll里面有什么函数,通过函数名找到函数。上例子:
dll文件:
/*!
 * \file LibTest.h
 *
 * \author puppet_master
 * \date 九月 2015
 *
 * 测试dll
 */
#pragma once

//通过一个编译宏控制编译流,如果定义了DLLANDLIB就按照导出dll的方式编译(生成dll和lib),否则只生成dll
#ifdef DLLANDLIB
#define DLLEXPORT _declspec(dllexport)  //生成dll和lib
#else
#define DLLEXPORT //不定义这项,则默认只生成dll
#endif // 

//写一个C风格的函数
extern "C" DLLEXPORT void CFuncTest();
cpp:
#include "stdafx.h"
#include "LibTest.h"
#include <iostream>
using namespace std;

void CFuncTest()
{
	cout<<"C Func Test Show"<<endl;
}


Test文件:

// Test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <windows.h>
using namespace std;

//定义函数指针
typedef void (*Func)(void);

int _tmain(int argc, _TCHAR* argv[])
{
	//导入DLL
	HINSTANCE hDll;
	hDll = LoadLibrary("LibTest.dll");
	if (hDll)
		cout<<"Dll import successfully!"<<endl;
	else
		cout<<"Dll import failed!"<<endl;
	
	//定位函数
	Func cFunc = (Func)GetProcAddress(hDll, "CFuncTest");

	//测试函数
	if (cFunc)
		cFunc();
	else
		cout<<"C func load failed"<<endl;

	system("pause");
	return 0;
}


结果:
Dll import successfully!

C Func Test Show

请按任意键继续. . .

还有一个问题,应用程序如何找到DLL文件?

使用LoadLibrary显式链接,那么在函数的参数中可以指定DLL文件的完整路径;如果不指定路径,或者进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:

(1)包含EXE文件的目录

(2)工程目录

(3)Windows系统目录

(4)Windows目录

(5)列在Path环境变量中的一系列目录

关于怎么动态加载一个含有类的dll,暂时还是不会。不过看到网上有文章说把类封装到函数中,提供各种接口。感觉好麻烦,目前打算还是使用.h文件静态加载dll吧。

参考链接:
http://blog.csdn.net/heyabo/article/details/8721611

https://msdn.microsoft.com/zh-cn/library/dtba4t8b(VS.80).aspx

http://www.cnblogs.com/chio/archive/2007/11/03/948480.html

/article/1478250.html

http://blog.csdn.net/qianchenglenger/article/details/21599235

http://blog.csdn.net/shg104/article/details/875592
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: