每次使用都要去Google的基础知识--.lib与.dll

应该很多人对lib和dll理解的不够深刻,今晚就八一八。

主要参考链接:
https://www.cppfans.org/1394.html
http://www.codeproject.com/Articles/9087/Our-journey-from-EXEs-LIBs-DLLs-COM-to-Assemblies

Lib称为静态链接库(static link library),是在编译的链接期间使用的,他里面其实就是源文件的函数实现。
As software became bigger and more complex, there was a need to use (consume) what we call a ‘third party code’. For example, you would need to use a sockets library to do some network programming inside your own program. Such libraries were available in the form of library (.LIB) files. A LIB file would contain the object code of the library. The linking step had the task to ensure that the object code would be merged with the EXE. This is called as static linking.

Dll成为动态链接库(Dynamic link library),是在程序运行时动态调用的,runtime时使用,它里面包含了源文件的函数实现、DllMain入口函数和.def文件。
The idea of code sharing remained the same. However, the library code was isolated from the executables to form a DLL (dynamic link library) file. These libraries would get linked with the application calling them at runtime. Also, multiple applications calling the library functions would share only one copy of them from the memory. The operating system was responsible to do this linking at runtime. A user just needed to place the DLL in the same folder or in the system folder of the OS, for this scheme to work.

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

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

生产一个.lib文件!
这里假设我们的工具是VS2015
1.建立win32控制台工程
2.在应用程序设置的步骤,选择”静态库 static Library”
3.完成即可 (这里只是针对最简单的Dll,Win32 Application的方式稍有不同)
这样一个静态Lib库的工程就建好了。
4.添加.h文件以及.cpp文件:

// Function.h
void Print();
// Function.cpp
#include "Function.h"

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

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

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

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

Lib文件包含方法:项目属性-> 配置属性 -> 链接器 -> 常规 -> 附加库目录,在这里面填写你Lib文件的路径。项目属性 -> 配置属性 -> 链接器 -> 常规 -> 输入,在这里面填写你Lib文件的名称,例如: Function.lib,接下来就是在代码中使用了:

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

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

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

下面是第二种方法使用.lib文件:
#pragma comment(Lib, “LibPath”)

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。

很多时候,编译的时候使用lib,运行的时候使用dll,这样也方便软件的打包发布,有效减小软件的体积。

你可能感兴趣的:(C++,dll,lib)