您的位置:首页 > 其它

动态链接库,静态链接库和可执行文件的区别是什么?

2015-03-26 18:44 465 查看
动态链接库(Dynamic Link Library,缩写为DLL)是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。动态链接库文件的扩展名一般是dll,也有可能是drv、sys和fon,它和可执行文件(exe)非常类似,区别在于DLL中虽然包含了可执行代码却不能单独执行,而应由Windows应用程序直接或间接调用。

   动态链接是相对于静态链接而言的。所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的exe文件中,该文件包含了运行时所需的全部代码。当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。

   一般情况下,如果一个应用程序使用了动态链接库,Win32系统保证内存中只有DLL的一份复制品,这是通过内存映射文件实现的。DLL首先被调入Win32系统的全局堆栈,然后映射到调用这个DLL的进程地址空间。在Win32系统中,每个进程拥有自己的32位线性地址空间,如果一个DLL被多个进程调用,每个进程都会收到该DLL的一份映像。

搞程序开发的朋友应该对LibDll很熟悉,对于这两个东西,可谓是几家欢喜几家忧,喜欢的人觉得它可以封装代码,避免别人剽窃,不喜欢的人觉得它很麻烦,干嘛不直接用源文件。而特别是新手对于LibDll的关系和使用完全搞不清楚。

Lib称为静态链接库(static
link library),是在编译的链接期间使用的,他里面其实就是源文件的函数实现。

Dll成为动态链接库(Dynamic link library),是在程序运行时动态调用的,runtime时使用,它里面包含了源文件的函数实现、DllMain入口函数和.def文件。

先说说Lib库吧,相对来说大家对它比dll熟悉一些。

Lib库


Lib库有两种,一种就是常见的普通Lib(static Lib),还有一种大家经常下载的开源代码编译后,会产生Lib和dll,其中Lib只是Dll的附带品,是DLL导出的函数列表文件而已,暂且称之为Dynamic
Lib。

两者都是二进制文件,两者都是在链接是调用的,使用static lib的exe可直接运行,使用dynamic lib的exe需要对应的dll才能运行。下来我们来看如何产生并使用一个static lib文件。

这里假设我们的工具是VS2005(包含)以上的版本,其他的工具都是大同小异的,就不做介绍了。

1.建立win32控制台工程

2.在应用程序设置的步骤,选择”静态库 static Library”

3.完成即可 (这里只是针对最简单的Dll,Win32 Application的方式稍有不同)

这样一个静态Lib库的工程就建好了。代码如下:
//////////////////////////////////////////////////////////////////////////
// Function.h
//////////////////////////////////////////////////////////////////////////

void Print();

//////////////////////////////////////////////////////////////////////////
// Function.cpp
//////////////////////////////////////////////////////////////////////////
#include "Function.h"

void Print()
{
std::cout << "Hello world!" << std::endl;
}


编译会生成一个以工程名作为名称的Lib文件。

在你的项目工程属性中包含这个Lib文件的头文件目录和Lib文件目录。

头目录包含方法:项目属性(Alt + F7) -> 配置属性 -> C/C++ -> 常规 -> 附加包含目录,里面包含你的Lib库的头文件,你可以使用绝对路径,也可以使用VS中宏表示的相对路径,建议使用相对路径。

Lib文件包含方法:项目属性(Alt + F7) -> 配置属性 -> 链接器 -> 常规 -> 附加库目录,在这里面填写你Lib文件的路径。项目属性(Alt + F7) -> 配置属性 -> 链接器 -> 常规 -> 输入,在这里面填写你Lib文件的名称,例如: Function.lib

这样你在你的代码里就可以这样使用了:
#include <stdio.h>
#include <stdlib.h>
#include "Function.h"

int _tmain(int argc, _TCHAR* argv[])
{
Print();
system("pause");
return 0;
}


这样就是一个完整生成并使用Lib库的例子。

当然了,你还可以使用#pragma comment(Lib, “LibPath”)的方法来调用Lib文件。

==============================================================

Dynamic Lib的调用方法与Static lib完全一致,唯一的区别就是使用Dynamic
Lib编译出来的程序,运行时需要其对应的Dll文件。前面我们已经说过了。

DLL


下来我们好好谈谈Dll的问题,相对于Lib来说,Dll使用的频率应该是非常高的了,因为你的程序运行,系统运行等等都靠它,MS也是因为这个才导致操作系统封装的越来越好了。

Dll其实和Exe是几乎完全一样的,唯一的不一样就是Exe的入口函数式WinMain函数(console程序是main函数),而Dll是DllMain函数,其他完全是一样的。所以有人也戏称Dll是不能自己运行的Exe。

Dll创建的过程也比较简单,唯一麻烦的就是需要定义导出函数接口。

创建Dll工程过程很简单,建立win32控制台工程,在应用程序设置的步骤,选择”动态库 Dynamic Library”,完成即可。(这里只是针对最简单的Dll,Win32 Application的方式稍有不同)

定义导出函数接口有两种方式:

1.使用__declspec宏
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

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;
}

//////////////////////////////////////////////////////////////////////////
// Fucntion.h
//////////////////////////////////////////////////////////////////////////

extern "C" __declspec(dllexport) void Print(void);

//////////////////////////////////////////////////////////////////////////
// Function.cpp
//////////////////////////////////////////////////////////////////////////
#include "Function.h"

void Print(void)
{
std::cout << "[Dll] Hello world!" << std::endl;
}


这里假设我们的工程名叫Function,那么 编译后会生成一个Function.dll和一个Function.lib(Dynamic lib),Dynamic lib前面已经说过,此处不再赘述了。

Function.h头文件中的
extern "C" __declspec(dllexport) void Print(void);


我们只需要清楚其中函数的名称,返回值,参数就可以了。

extern “C”表示我们要按照C语言的方式编译该函数,防止在C++工程中编译出现函数名错误,因为C++中有函数重载,所以函数名编译后可能会出现Print@1的形式;而且这样也可以让C调用C++的动态链接库;__declspec(dllexport)表示下来的函数是dll的导出函数接口。

2.使用def文件,类似于声明导出接口的方式,不过却不需要声明了,因为它专门定义了一个def文件来说明
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

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;
}

//////////////////////////////////////////////////////////////////////////
// Fucntion.h
//////////////////////////////////////////////////////////////////////////

void Print(void);

//////////////////////////////////////////////////////////////////////////
// Function.cpp
//////////////////////////////////////////////////////////////////////////
#include "Function.h"

void Print(void)
{
std::cout << "[Dll] Hello world!" << std::endl;
}

//////////////////////////////////////////////////////////////////////////
// Fucntion.def
//////////////////////////////////////////////////////////////////////////

EXPORTS
Print


同样,类似于Static Lib的显式调用方法,dll也可以显式调用,前提是我们很清楚函数名、返回值、参数列表。

 
#include <stdlib.h>
#include "Function.h"
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    HINSTANCE hInstance  = LoadLibrary("Function.dll");
    typedef void(*_Print)(void);
    _Print printFunction;
    if (hInstance != NULL)
    {
        printFunction = (_Print)GetProcAddress(hInstance, "Print");
    }

    printFunction();
    FreeLibrary(hInstance);
    system("pause");
    return 0;
}


当然了,有显式自然还有隐式调用了,这个时候Function.dll的伴生产物Function.lib就可以派上用场了,其使用方法和静态lib完全相同。

DLL调用的两种方法各有利弊:采用寻找DLL中函数地址的方法,优点是只要函数形参没变化,那么修改了函数实现也没关系,不需要重新编译Exe,只需要将新的DLL文件拷贝过来即可,大型项目上使用比较灵活;缺点是比较麻烦,需要定义实例,函数指针,加载DLL,释放DLL等过程。而采用Dynamic Lib的方法,优点是容易理解和接受(因为他跟静态库的调用方法类似);缺点是修改了DLL工程的任何东西都需要使用最新的Dynamic Lib重新能编译Exe。

 

总结:


DLL和Lib是各有千秋,使用的情况也是各不相同,不过最终还是需要大家在项目中实践到底哪种方法好,到底采用哪种类型的库,总之,一切都要按需求最优。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dll lib exe win32 windows
相关文章推荐