核心关键在接口,接口中一定要使用virtual,避免链接出现找不到实现的情况。
/ //一般的DLL都是特定功能的封装,主程序只要调用其功能即可,比如参数设置DLL, //这些DLL不和主程序交互,其只是被动的被调用而已。但有时候程序设计要求在DLL //中能调用主程序的一些功能,比如主程序有一个控制器类CSysCtrl,里面封装了对 //外部设备的访问,在dll中实现的是用户界面,希望能在dll中可以调用这个控制器类, //此时如何把这个class传递到dll中进行处理呢?以下就是专门用来解决这种问题的。 / 要解决这个问题,需要创建一个主程序项目和至少一个DLL项目。 需要如下简单几步即可: 1、创建声明头文件declare.h,放到目录Interface目录下 关键字AFS_FRAMEWORK用来提供给编译器选择,在主程序中设置该预编译项 预编译项AFS_SHARED_IMPORT用于导出或者导入后面的接口 //预编译声明头文件declare.h / #include <QtCore/qglobal.h> #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 / 2、创建提供给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 / 3、创建提供给主程序调用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 / 4、创建C++共享库DLL项目 4.1、在配置文件中加入INCLUDEPATH += ../Interface一行,在编译环境中包含该目录 4.2、然后将canimal.h文件添加到该项目中 4.3、删除项目中*_global.h文件,因为这个声明已经被包含在declare.h文件中了 4.4、修改项目中存在的类,使其继承自canimal类,并实现其中的纯虚函数接口 4.5、创建导出接口函数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、创建主程序项目 5.1、在配置文件中加入INCLUDEPATH += ../Interface一行,在编译环境中包含该目录 5.2、在配置文件中加入DEFINES += AFS_FRAMEWORK一行,声明该项目为主程序 5.3、然后将csysctrl.h文件添加到该项目中 5.4、添加一个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 !"); } / 4、加载动态库,导出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(); } /二、主程序和动态库不在一个文件夹中