1. 程式人生 > >Lib和Dll的那點事

Lib和Dll的那點事

搞程式開發的朋友應該對Lib和Dll很熟悉,對於這兩個東西,可謂是幾家歡喜幾家憂,喜歡的人覺得它可以封裝程式碼,避免別人剽竊,不喜歡的人覺得它很麻煩,幹嘛不直接用原始檔。而特別是新手對於Lib和Dll的關係和使用完全搞不清楚。

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++中有函式過載,所以函式名編譯後可能會出現[email protected]的形式;而且這樣也可以讓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是各有千秋,使用的情況也是各不相同,不過最終還是需要大家在專案中實踐到底哪種方法好,到底採用哪種型別的庫,總之,一切都要按需求最優。