C++連接庫

  靜態連接庫在程序編譯連接過程當中就導入lib文件而且包含在生成的exe文件裏,而動態連接庫DLL是在程序運行中由程序加載和卸載的,也就是說它是動態的,固然動態連接庫DLL也能夠靜態加載當作靜態來用;windows

  靜態連接庫使用方便直接,但程序內存佔用大、使用不靈活,而動態連接庫使用靈活但加載須要時間。ide

靜態連接庫函數

  本文使VC++6.0集成工具,新建一個Win32 Static Library工程,再新建一個以下cpp文件,完成構建以後能夠獲得一個lib文件,這就是以後的應用程序要用到的文件;工具

//--********* libTest.cpp **********--//
//-----------
聲 明 一 個 函 數,並 按 C 方 式 編 譯 連 接 ---------
extern "C" int getNumber();
int
getNumber() { return 23; }

  構建以後,靜態鏈接庫就算建立完成了,在lib文件中能夠找到"_getNumber",這就是"C"編譯方式的函數名;若是聲明的函數名以前加上__stdcall",spa

即聲明:int __stdcall getNumber();那麼lib文件中能夠見到"?getNumber@@YGHHH@Z",這就是C++編譯方式的函數名(方便函數重載)。另外的以__cdecl(缺省)和__fastcall編譯以後的命名方式也相似。設計

而後新建一個工程,把頭文件libTest.h和庫文件libTest.lib複製到新工程文件夾下,主程序代碼以下:3d

#include <stdio.h>
#include "libTest.h"
//--------- 把 要 用 到 的 外 部 鏈 接 文 件 和 本 地 obj 文 件 一 同 構 建 ------
//--------- 如 果 不 復 制 ,則 這 裏 使 用 全 路 徑 指 向 庫 工 程 的 lib ---
#pragma comment(lib,"libTest.lib") int main(int argc, char* argv[]) { printf("Number: %d\n", getNumber()); return 0; }

程序運行結果以下:指針

 

動態連接庫code

/*** 新建 一 個 dynamic~link Libbrary 工 程 dllTest *******
**** 添 加 一 個 dllTest.cpp 文 件 *****
*/
//-------------- 聲 明 並 導 出 函 數 -------
extern "C" __declspec(dllexport) int add(int x, int y);
//__declspec(dllexport)int __stdcall add(int,int);

int
add(int x, int y) { return x + y; }

建立一個動態連接庫時,須要把其中的方法導出,並生成dll文件,上述的__declspec(dllexport)是一種導出方式;對象

或者能夠用def文件導出,新建一def文件,並添加文件到工程就好了,其所有的代碼以下:

LIBRARY dllTest
EXPORTS
add @ 1

其中add是函數名,1是序號;這樣構建以後也能夠獲得一個dll文件。

新建一個應用工程,把生成的dll文件複製到工程文件夾下,主函數以下:

#include <stdio.h>
#include <windows.h> //------------- 根 據 之 前 導 出 的 形 式 定 義 一 個 函 數 指 針 類 型 -----------
typedef int (*lpAddFun)(int,int);
//typedef int (__stdcall *lpAddFun)(int,int);
int main(int argc, char* argv[]) {
//-------------- 用 定 義 的 函 數 指 針 類 型 創 建 一 個 函 數 ------ lpAddFun add;
//--------------- 加 載 動 態 鏈 接 庫 --------- HINSTANCE hDll
= LoadLibrary("dllTest.dll"); if(hDll) {
  //----------------- 可 以 用 函 數 名 導 入,也 可 以 用 序 號 來 導 入--------
add
= (lpAddFun)GetProcAddress(hDll, "add");
   //add = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));
if(add) printf("5 + 7 = %d\n", add(5, 7));   //----------------- 卸 載 鏈 接 庫 ----------- FreeLibrary(hDll); } return 0; }

程序運行結果以下: 

 

 dll的靜態調用方式

   動態連接庫的建立方式相同,可是使用時,須要複製dll文件、lib文件兩個文件到應用工程文件夾下,主函數代碼以下:

#include <stdio.h>
#pragma
comment(lib, "dllTest.lib")
//-------------- 以 "C" 方 式 導 出 的 函 數 ,這 裏 就 要 以 "C" 的 方 式 導 入 ---------- //-------------- 或 者 導 出 時 不 加 extern "C" ,這 裏 也 不 加( 缺 省 __cdecl ) ------------- //-------------- 也 就 是 說 導 入 和 導 出 的 方 式 必 須 一 致(注意!) ------------------ extern "C" __declspec(dllimport) int add(int, int);
//__declspec(dllimport)int __stdcall add(int,int);
int main() { printf("12 + 25 = %d\n", add(12, 25)); return 0; }

 兩種調用方式的對比:

  (1) 靜態調用的add(),是經過dllTest.lib文件索引到dllTest.dll文件中的add();

  (2) 動態調用方式是在程序運行時,直接加載dll文件,導出其中的add()方法並給函數指針賦值,經過函數指針調用函數。

 

函數DllMain

  Windows加載dll時須要一個入口函數DllMain(),當dll文件中沒有DllMain()函數時,會調用一個缺省的無任何操做的DllMain(),它是由系統調用的,不能手動調用。

#include "dllTest.h"
#include <stdio.h>
#include <windows.h>

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch(ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        printf("dll process attach\n");
        break;
    case DLL_THREAD_ATTACH:
        printf("dll thread attach\n");
        break;
    case DLL_PROCESS_DETACH:
        printf("dll process detach\n");
        break;
    case DLL_THREAD_DETACH:
        printf("dll process detach\n");
        break;
    }
    return TRUE;
}

int add(int x, int y)
{
    return x + y;
}

  這是一個DllMain()使用實例,其中ul_reason_for_call表明dll的四種調用方式,包括兩種加載和兩種卸載;

用前面的應用程序實驗以後,結果以下:

 

導出類class

class CPoint
{
public:
    float x;
    float y;

    virtual void    add(float, float) = 0;
    virtual float   getX() = 0;
    virtual float   getY() = 0;
};

class CMyPoint : public CPoint
{
public:
    CMyPoint(float, float);
    virtual void    add(float, float);
    virtual float   getX();
    virtual float   getY();
};

class __declspec(dllexport)CMyPoint;

 

  設計一對父類——子類,父類中的方法設計成純虛函數,而它的子類就是咱們要用的類了,在dll中工程中導出的也是子類CMyPoint,上面最後一句代碼導出這個子類。

  這是子類的實現代碼:

CMyPoint::CMyPoint(float x0, float y0)
{
    var = 12;
    x = x0;
    y = y0;
}

void CMyPoint::add(float dx, float dy)
{
    x = x + dx;
    y = y + dy;
}

float CMyPoint::getX()
{
    return x;
}

float CMyPoint::getY()
{
    return y;
}
CMyPoint.cpp

  類CMyPoint的實現還包含一個Create函數,是用來建立類對象的。這個方法也應該用C方式導出,不然使用起來有些麻煩。

extern "C" __declspec(dllexport)CMyPoint* CreatePoint(float,float);
CMyPoint* CreatePoint(float x0, float y0)
{
    return (new CMyPoint(x0, y0));
}

 

 

動態加載方式

  新建應用工程,須要複製.h頭文件、dll文件到新的工程文件夾下,程序先獲取CreatePoint()方法,再經過它建立類CMyPoint並返回一個對象指針,接下來即可以操做這個類對象;

  注意:應用工程文件夾下的dllTest.h中不能有多餘的導出或者導入語句,能夠用宏屏蔽,也能夠手動刪除。

#include "dllTest.h"

typedef CMyPoint*(*lpCreatePoint)(float,float); int main(int argc, char* argv[]) { lpCreatePoint CreatePoint; HINSTANCE hDll = LoadLibrary("dllTest.dll"); if(hDll) { CreatePoint = (lpCreatePoint)GetProcAddress(hDll, "CreatePoint"); if(CreatePoint) { CMyPoint* p0 = CreatePoint(12, 14); printf("x = %.2f, y = %.2f\n", p0->getX(), p0->getY()); } FreeLibrary(hDll); } return 0; }

 

 

 

靜態加載方式

  靜態使用方式很簡單,一樣應用建立函數CreatePoint(),並且是導出後直接使用,固然類界面仍是必須inlclude;

#include "dllTest.h"
#pragma
comment(lib, "E:\\VC6\\dllTest\\Debug\\dllTest.lib")

extern "C" __declspec(dllimport)CreatePoint(float,float); int main() { CMyPoint* p0 = CreatePoint(3, 4); p0->add(2, 3); printf("x = %.2f, y = %.2f\n", p0->getX(), p0->getY()); return 0; }

 

  靜態導入方式構建生成的EXE文件可移植,lib文件和dll文件在構建時都固化在其中,因此這裏生成的EXE不管到哪兒,只要系統還兼容,程序就能夠運行;

  而以前的動態導入方式,dll文件須要隨着EXE文件移動,否則程序運行時會找不到dll文件而出錯,因此咱們通常的見到的軟件安裝目錄下都有一大堆dll文件。

 

導出變量

  在dll工程中,導出:int __declspec(dllexport)varName;

  在應用工程中,導入:int __declspec(dllimport)varName;

  其中,int是變量類型,varName是變量名,

相關文章
相關標籤/搜索