首先使用Wizard創建一個Win32 Dynamic-Link Library工程,然後定義一個簡單的C++類CInDLL.由於該類會被工程之外的文件所引用,所以需要對這個類進行引出。因為只有引出後所生成的DLL中才帶有供足夠的信息以在連接和運行時被正確引入到進程空間中。有兩種方法可以引出類,使用__declspec(dllexport)定義和使用定義文件。
下面先講使用__declspec(dllexport)的方法:將類定義改為:class __declspec(dllexport) CInDLL 就可以了。 這樣產生的工程在編譯時是正確的但是在使用時會產生錯誤,因為你包含的頭文件中也是使用__declspec(dllexport),而使用這個DLL的工程中並沒有引出這個類,而是需要引入這個類)在使用時需要將類定義改為class __declspec(dllimport) CInDLL就可以了。
使用定義文件可以有效的避免這個問題, 這種方法是利用宏定義在不同的地方產生不同的編譯代碼:
在頭文件中加入如下的代碼:
#ifndef ClassInDLL_H
#define ClassInDLL_H
#ifdef _CLASSINDLL
#define CLASSINDLL_CLASS_DECL __declspec(dllexport)
#else
#define CLASSINDLL_CLASS_DECL __declspec(dllimport)
#endif
#endif // ClassInDLL_H
//將class __declspec(dllexport) CInDLL改為
class CLASSINDLL_CLASS_DECL CInDLL
{
CInDLL()
……
}
在實現這個類的CPP文件的頂部加入#define _CLASSINDLL語句,然後include頭文件。
#define _CLASSINDLL
#include "CInDLL.h"
CInDLL::CInDLL(){}……
這樣一來在使用這個類時就可以不做任何改動了。
在使用這個類的工程中添加這個類的頭文件,並設置或者動態鏈接.lib文件。
1.共享庫的對外接口函數的聲明必須加上extern “C”。
2.使用共享庫對話接口函數生成的對象指針時在該對象未被釋放之前不能關閉共享庫句柄,否則會出現segmentation fault錯誤。
以下是一個插件式設計的示例:
1、主執行程序:main.cpp
#include
#include
#include "SMSGamePlugin.h"
int main(int argc, char** argv)
{
void *GameLib = dlopen("./Flower.so", RTLD_LAZY);
const char *dlError = dlerror();
if (dlError)
{
< "dlopen error!" << dlError <<
return(-1);
}
CSMSGamePlugin *(*pGetGameObject)(void);
pGetGameObject = (CSMSGamePlugin *(*)(void))dlsym(GameLib, "GetGameObject");
dlError = dlerror();
if (dlError)
{
< "dlsym error!" << dlError <<
return(-1);
}
CSMSGamePlugin *pGame = (*pGetGameObject)();
pGame->Initialize();
pGame->Load();
pGame->Handle();
delete *pGame;
dlclose(GameLib);
}
2、公用基類部分:SMSGamePlugin.h
#ifndef __SMSGamePlugin_h__
#define __SMSGamePlugin_h
class CSMSGamePlugin
{
public:
virtual int Initialize(void) = 0;
virtual int Load(void) = 0;
virtual int Handle(void) = 0;
};
#endif
編譯:g++ -rdynamic -ldl -s -o Test main.cpp
3、共享庫部分:
共享庫頭文件:Flower.h
#ifndef __Flower_h__
#define __Flower_h__
#include "SMSGamePlugin.h"
extern "C" CSMSGamePlugin *GetGameObject(void);
class CFlower: public CSMSGamePlugin
{
public:
virtual int Initialize(void);
virtual int Load(void);
virtual int Handle(void);
};
#endif
4、共享庫實現文件:Flower.cpp
#include
#include "Flower.h"
CSMSGamePlugin *GetGameObject(void)
{
return(new CFlower());
}
int CFlower::Initialize(void)
{
< "Initialize()" <<
return(0);
}
int CFlower::Load(void)
{
< "Load()" <<
return(0);
}
int CFlower::Handle(void)
{
< "Handle()" <<
return(0);
}
你不是說,要將這些函數封裝到類裡,方便調用嗎?那麼就應該封裝進去呀。你開始說得和你最後的提問怎麼自相矛盾的?
要調用這些函數,當然是提供一系列的類函數,將這些函數封裝到類函數裡,這個和你的LoadClientSdk是一個思路的!
調用client_sdk_get_fd_udata的封裝:
int? CLS_MidServer::GetFdUData(int? i1, int? i2)
{
//something else;
int? RetVal = (*client_sdk_get_fd_udata)(i1, i2);
//something else;
return RetVal;
}
另外要做的就是需要導出類,否則在客戶程序中無法使用類函數了,如果不想導出類,在DLL封裝類,並沒簡化函數調用,這個和你一開始的說法又存在矛盾的。當然,不導出類也是可以的,模型可以這樣搭建:
class A{
void LoadClientSDK() {}
void clsFunctionA() {}
void clsFunctionB() {}
};
A a;
void /*EXPORTABLE*/ LoadClientSDK()
{
a.LoadClientSDK();
}
void /*EXPORTABLE*/ globalFunctionA()
{
a.clsFunctionA();
}
void /*EXPORTABLE*/ globalFunctionB()
{
a.clsFunctionB();
}
如果夠專業,程序模型的搭建是非常重要的,不是說要實現一個功能,碼幾行代碼,只要能實現功能就OK了,實現程序的思路有很多,必須在若干方案之間選擇最優方案來實現。
寫程序和寫文章是一樣的,一般來講,大人寫出來的文章,肯定比小孩來得好,但業余的一般比不上作家!如果一個作家寫出來的文章水平只有業余人士的水平,小孩是不會嘲笑的,但作協的人一看都會笑掉大牙的!因此,程序架構能反映一個人的水平高低!
另外指出,像client_sdk_get_fd_udata這些變量為何不封裝到類中呢?既然說是類了,就應該封裝起來,不要裸露在全局中!裸露在全局中的變量,還不如干脆導出這些變量,這樣在客戶程序中就可以直接使用這些函數指針變量來調用所需要的函數,傳參數啦,判斷返回值啦都有了!
最後一點,既然有LoadClientSdk,就應該再提供FreeClientSdk,使客戶程序能主動釋放被包裝的SDK庫:
void CLS_MidServer::FreeClientSdk()
{
FreeLibrary(m_ClientSdk);
m_ClientSdk = NULL;
client_sdk_get_fd_udata = NULL;
//something else
}...余下全文>>
把person做成導出類,就可以用person對象,並調用xPrint。定義導出類的方法
class __declspec(dllexport) person{...}或是class __declspec(dllimport) person{...} .新建win32 Dynamic-link library工程,在裡面定義類,就像上面寫的那樣。調用時將生成的*.dll、*.lib和頭文件復制到要用的文件夾裡,再新程序裡包含頭文件,連接剛才的*.lib文件。就可以像普通類一樣的使用:person a;a.xPrinf();