C++編寫DLL的方法

      在寫C++程序時,時常須要將一個class寫成DLL,供客戶端程序調用。這樣的DLL能夠導出整個class,也能夠導出這個class的某個方法。c++

1、導出整個class函數

方法很簡單,只須要在類的頭文件中class和類名之間加上_declspec(dllexport),同時在另一份提供給客戶端調用程序使用的類的頭文件中class和類名之間加上_declspec(dllimport)。爲了能讓客戶端程序和DLL程序公用該類的一份頭文件,一般在類的頭文件中使用宏和預編譯指令來處理。以下DLLTest.h:spa

#ifdef  DLL_TEST_API
#else
#define DLL_TEST_API _declspec(dllimport)
#endif

Class DLL_TEST_API  CDLLTest
{

         Public:
                  CDLLTest();

                  ~CDLLTest();

                  int Add(int a, int b);

};

 DLLTest.cpp以下:指針

#define DLL_TEST_API     _declspec(dllexport)

#include 「DLLTest.h」

………………………………………

 這樣,在DLL編譯時DLL_TEST_API被定義爲_declspec(dllexport),並且客戶端程序編譯時它被定義爲_declspec(dllimport)。code

2、導出這個類的某個或者某幾個方法。對象

這時,須要將_declspec(dllexport)放到成員函數名前,如DLLTest.h:blog

#ifdef DLL_TEST_API
#else
#define DLL_TEST_API _declspec(dllimport)
#endif

Class CDLLTest
{

         Public:
                      CDLLTest();
                      ~CDLLTest();
                      int  DLL_TEST_API  Add(int a, int b);

};

 

可是,若是僅僅是這樣的話,當客戶端程序#include這個頭文件後,定義DLLTest這個類的一個對象後(靜態方式連接DLL),客戶端程序沒法連接經過,會提示構造函數和析構函數沒法解析,此時,須要將構造函數和析構函數前也加上DLL_TEST_API宏便可。繼承

固然這裏還有個問題就是類的函數在導出後,名字會發生變化,咱們能夠在函數名前再加上extern 「C」 ,如 extern 「C」 DLL_TEST_API int Add(int a ,int b);但這隻解決了C與C++調用時名字變動問題,可靠的方法仍是增長一個模塊定義文件def,在該文件中定義導出函數的名稱,咱們將在後面看到樣例。編譯器

DLL編寫完成後,就只剩下客戶端程序如何去調用該DLL了,靜態方式調用DLL和動態方式調用DLL。編譯

1、靜態方式調用DLL

這個方法就簡單了,將DLLTest.h頭文件和DLLTest.lib,DLLTest.dll文件拷貝到客戶端程序的當前目錄下,在客戶端程序中#include<DLLTest.h>,而後經過#pragma comment(lib,」DLLTest.lib」)的方式引入lib庫,或者在客戶端程序的工程屬性裏面增長對該lib文件的引入。

而後就能夠在客戶端程序中如同使用本地的一個class同樣使用該DLL了,如:

CDLLTest dllTest;

dllTest.Add(1,2);

 

2、動態方式調用DLL

動態調用這個DLL,就須要對這個class進行修改了。

首先,在DLLTest.cpp文件中增長一個全局函數,該函數能夠返回這個class的一個實例,這樣,客戶端程序調用這個全局函數後,獲得該class的實例,就能夠調用該class的實例方法了。

extern 「C」  _declspec(dllexport)  CDLLTest*  GetInstance()
{
         return new CDLLTest;
}

 注:extern 「C」 只是解決了c與c++編譯器之間的兼容問題,若是須要和其餘編譯器之間兼容,可靠的辦法仍是增長一個.def文件,文件內容以下:

LIBRARY  「DLLTest」

EXPORTS

GetInstance = GetInstance

 這樣就指定了DLL的函數導出後的名稱仍然不變。

這樣,客戶端程序就能夠經過該函數來獲取class的一個實例了。以下:

先須要定義一個函數指針類型:

typedef  CDllTestBase*  (*pfGetInst)();

//注:CDllTestBase類後面會介紹。

HMOUDLE hMod = LoadLibrary( _T(「DLLTest.DLL」) );

if(hMod)
{
        pfGetInst  pfGetInstance  =  (pfGetInst)GetProcAddress(「GetInstance」);
        if( p )
        {
            //經過基類指針指向派生類對象
              CDllTestBase * pInst = pfGetInstance ();
if( NULL
!= pInst )
{ pInst
->Add( 1,2);
}    if( NULL
!= pInst ) { //釋放對象 delete pInst;
} }
}

      固然,這裏仍是須要include這個DLL的頭文件DLLTestBase.h,若是將以前所寫的頭文件DLLTest.h直接拷貝到客戶端程序的當前目錄下,並include進來的話,在編譯鏈接時,是沒法經過的,咱們須要對這個頭文件進行修改,首先增長一個.h 文件DLLTestBase.h,在這個文件中咱們將須要在客戶端程序中調用的函數都命名成純虛函數,而後讓CDLLTest類繼承自CDLLTestBase類,DLLTestBase.h以下:

Class  CDLLTestBase
{
      Public:
                Virtual  ~CDLLTestBase(){};//虛析構函數,且爲內聯函數
                   Virtual int Add(int a, int b) = 0;

}

 DLLTest.h修改後以下:

#include 「DLLTestBase.h」
Class CDLLTest  :  public  CDLLTestBase
{
     Public:
                CDLLTest();
                ~CDLLTest();
                int  Add(int a, int b);
};

注:這裏的DLLTestBase須要提供一個虛析構函數,這樣在客戶端程序中就能夠經過基類指針來釋放派生類對象了。

      這樣,只須要將DLLTestBase.h拷貝到客戶端程序的當前目錄下,而後在客戶端程序中#include」DLLTestBase.h」,就能夠如上面介紹同樣在客戶端程序中調用DLL裏面的方法了。

相關文章
相關標籤/搜索