Qt建立和主程序交互的動態庫dll

/////////////////////////////////////////////////////////////////////////
//通常的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(); } /////////////////////////////////////////////////////////////////////////

相關文章
相關標籤/搜索