/////////////////////////////////////////////////////////////////////////
//通常的DLL都是特定功能的封裝,主程序只要調用其功能便可,好比參數設置DLL,
//這些DLL不和主程序交互,其只是被動的被調用而已。但有時候程序設計要求在DLL
//中能調用主程序的一些功能,好比主程序有一個控制器類CSysCtrl,裏面封裝了對
//外部設備的訪問,在dll中實現的是用戶界面,但願能在dll中能夠調用這個控制器類,
//此時如何把這個class傳遞到dll中進行處理呢?如下就是專門用來解決這種問題的。
/////////////////////////////////////////////////////////////////////////函數
要解決這個問題,須要建立一個主程序項目和至少一個DLL項目。
須要以下簡單幾步便可:
一、建立聲明頭文件declare.h,放到目錄Interface目錄下
關鍵字AFS_FRAMEWORK用來提供給編譯器選擇,在主程序中設置該預編譯項
預編譯項AFS_SHARED_IMPORT用於導出或者導入後面的接口
//預編譯聲明頭文件declare.h
/////////////////////////////////////////////////////////////////////////
#include <QtCore/qglobal.h>ui
#if defined(AFS_FRAMEWORK)
# define AFS_SHARED_IMPORT Q_DECL_EXPORT
# define AFS_SHARED_EXPORT Q_DECL_IMPORT
#else
# define AFS_SHARED_IMPORT Q_DECL_IMPORT
# define AFS_SHARED_EXPORT Q_DECL_EXPORT
#endif
/////////////////////////////////////////////////////////////////////////this
二、建立提供給dll調用的主程序功能接口文件csysctrl.h,也放到Interface目錄下
CSysCtrl是在主程序中實現,提供接口給DLL使用,所以用AFS_SHARED_IMPORT聲明接口
在主程序中使用時導出該接口,在DLL中使用時導入該接口
//CSysCtrl接口聲明頭文件csysctrl.h
/////////////////////////////////////////////////////////////////////////
#ifndef CSYSCTRL_H
#define CSYSCTRL_H設計
#include <QObject>
#include "declare.h"指針
class AFS_SHARED_IMPORT CSysCtrl : public QObject
{
Q_OBJECT
public:對象
signals:繼承
public slots:
virtual void walkWithDog() = 0;
virtual void playWithDog() = 0;
protected:
CSysCtrl(){}
virtual ~CSysCtrl(){}
};接口
#endif // CSYSCTRL_H
/////////////////////////////////////////////////////////////////////////get
三、建立提供給主程序調用dll庫的接口文件canimal.h,也放到Interface目錄下
CAnimal是在DLL中實現,提供接口給主程序使用,所以使用AFS_SHARED_EXPORT聲明接口
在DLL實現該接口並導出接口聲明,在主程序中僅僅聲明該接口的存在
在DLL定義主程序接口指針,並聲明setSysCtrl()函數用來引入該接口指針,
這個函數必須聲明爲virtual類型的純虛函數,不然可能連接不成功,由於編譯器找不到實現代碼
//CAnimal接口聲明頭文件canimal.h
/////////////////////////////////////////////////////////////////////////
#ifndef CANIMAL_H
#define CANIMAL_H編譯器
#include <QObject>
#include "declare.h"
#include "csysctrl.h"
//接口聲明
class AFS_SHARED_EXPORT CAnimal : public QObject
{
Q_OBJECT
public:
signals:
public slots:
virtual bool eat() = 0;
virtual void sleep() = 0;
virtual void setSysCtrl(CSysCtrl* sysctrl) = 0;
protected:
CAnimal(){}
virtual ~CAnimal(){}
protected:
CSysCtrl* m_sysctrl;
};
#endif // CANIMAL_H
/////////////////////////////////////////////////////////////////////////
四、建立C++共享庫DLL項目
4.一、在配置文件中加入INCLUDEPATH += ../Interface一行,在編譯環境中包含該目錄
4.二、而後將canimal.h文件添加到該項目中
4.三、刪除項目中*_global.h文件,由於這個聲明已經被包含在declare.h文件中了
4.四、修改項目中存在的類,使其繼承自canimal類,並實現其中的純虛函數接口
4.五、建立導出接口函數CreateDog()和ReleaseDog(),用來建立和釋放派生對象
示例:這裏派生類採用了單例模式,方便後續使用
//派生類DogTest的頭文件dogtest.h
/////////////////////////////////////////////////////////////////////////
#ifndef DOGTEST_H
#define DOGTEST_H
#include "canimal.h"
#define afs DogTest::instance()->GetSysCtrl()
//建立DogTest對象
extern "C" Q_DECL_EXPORT CAnimal* CreateDog();
extern "C" Q_DECL_EXPORT void ReleaseDog();
class DogTest : public CAnimal
{
public:
bool eat();
void sleep();
void setSysCtrl(CSysCtrl* sysctrl);
static DogTest* instance();
CSysCtrl* GetSysCtrl(){return m_sysctrl;}
protected:
DogTest();
private:
static DogTest* s_instance;
};
#endif // DOGTEST_H
/////////////////////////////////////////////////////////////////////////
//派生類DogTest的實現文件dogtest.cpp
/////////////////////////////////////////////////////////////////////////
#include "dogtest.h"
#include "canimal.h"
#include <QMessageBox>
CAnimal* CreateDog()
{
return DogTest::instance();
}
void ReleaseDog()
{
if (DogTest::instance() != NULL)
delete DogTest::instance();
}
DogTest::DogTest()
: CAnimal()
{
}
DogTest* DogTest::s_instance = NULL;
DogTest* DogTest::instance()
{
if (NULL == s_instance)
s_instance = new DogTest;
return s_instance;
}
void DogTest::setSysCtrl(CSysCtrl *sysctrl)
{
m_sysctrl = sysctrl;
}
bool DogTest::eat()
{
afs->walkWithDog();
QMessageBox::warning(NULL, "warning", "Haha!Dog is eating food !");
return true;
}
void DogTest::sleep()
{
afs->playWithDog();
QMessageBox::warning(NULL, "warning", "Be quit!Dog is sleep now !");
}
/////////////////////////////////////////////////////////////////////////
五、建立主程序項目
5.一、在配置文件中加入INCLUDEPATH += ../Interface一行,在編譯環境中包含該目錄
5.二、在配置文件中加入DEFINES += AFS_FRAMEWORK一行,聲明該項目爲主程序
5.三、而後將csysctrl.h文件添加到該項目中
5.四、添加一個cmyctrl類,派生自csysctrl接口,並實現其中的純虛函數
//派生類CMyCtrl的頭文件cmyctrl.h
/////////////////////////////////////////////////////////////////////////
#ifndef CMYCTRL_H
#define CMYCTRL_H
#include <QObject>
#include "csysctrl.h"
class CMyCtrl : public CSysCtrl
{
Q_OBJECT
public:
CMyCtrl(QObject *parent = 0);
signals:
public slots:
void walkWithDog();
void playWithDog();
};
#endif // CMYCTRL_H
/////////////////////////////////////////////////////////////////////////
//派生類CMyCtrl的實現文件cmyctrl.cpp
/////////////////////////////////////////////////////////////////////////
#include "cmyctrl.h"
#include <QMessageBox>
CSysCtrl::CSysCtrl(QObject *parent)
: QObject(parent)
{
}
CSysCtrl::~CSysCtrl()
{
}
CMyCtrl::CMyCtrl(QObject *parent) :
CSysCtrl(parent)
{
}
void CMyCtrl::walkWithDog()
{
QMessageBox::warning(NULL, "warning", "My dog, let's go walking !");
}
void CMyCtrl::playWithDog()
{
QMessageBox::warning(NULL, "warning", "Yeee! You'd like play football !");
}
/////////////////////////////////////////////////////////////////////////
四、加載動態庫,導出dll中的類
//動態加載DLL
/////////////////////////////////////////////////////////////////////////
#include "widget.h"
#include "canimal.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent), m_dog(NULL),
ui(new Ui::Widget)
{
ui->setupUi(this);
//加載動態庫
m_dll.setFileName("DogTest.dll");
if (m_dll.load())
{
// 解析導出函數
CreateDogFunc createDog = (CreateDogFunc)m_dll.resolve("CreateDog");
if (createDog != NULL)
{
m_dog = createDog();
if (m_dog != NULL)
{
m_dog->setSysCtrl(&m_sysctrl);
//將接口聲明在槽中的好處
connect(ui->pushButton_2, SIGNAL(clicked()), m_dog, SLOT(sleep()));
}
}
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked() { //直接調用接口函數 if (m_dog != NULL) m_dog->eat(); } /////////////////////////////////////////////////////////////////////////