C++ Qt 呼叫動態庫 外掛
1.隱式呼叫
1>新建一個C++庫,工程名為Cal,自動生成三個檔案cal.h, cal_global.h, cal.cpp,編譯後生成我們需要的Cal.lib, Cal.dll
#ifndef CAL_H
#define CAL_H
#include "cal_global.h"
class CALSHARED_EXPORT Cal
{
public:
Cal();
int add(int a, int b);
int substract(int a, int b);
};
#endif // CAL_H
2>新建一個Qt應用程式,工程名為CalTest#include "cal.h" Cal::Cal() { } int Cal::add(int a, int b) { return a + b; } int Cal::substract(int a, int b) { return a - b; }
在CalTest.pro檔案所在目錄下新建一個lib->cal目錄,將cal相關檔案拷貝到這個目錄下:cal.h, cal_global.h, Cal.lib;
在CalTest.pro檔案後面增加兩行程式碼:
INCLUDEPATH += $${PWD}/lib/cal
LIBS += $${PWD}/lib/cal/Cal.lib
我們在建構函式裡面測試,程式碼如下:
#include "dialog.h" #include "ui_dialog.h" #include "cal.h" #include <QDebug> Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); Cal cal; int a = 100, b = 200; int addResult = cal.add(a, b); int substractResult = cal.substract(a, b); qDebug() << "add:" << addResult << ",substract:" << substractResult; } Dialog::~Dialog() { delete ui; }
編譯完成之後,將Cal.dll檔案拷貝到生成的CalTest.exe目錄下執行即可。
隱式呼叫用起來比較方便,但是不太靈活,如果缺少標頭檔案,庫檔案那麼這個程式就編譯,執行不了了。
2.顯示呼叫
Cal工程程式碼如下:
#ifndef CAL_H #define CAL_H #include "cal_global.h" class CALSHARED_EXPORT Cal { public: Cal(); int add(int a, int b); int substract(int a, int b); }; extern "C" CALSHARED_EXPORT Cal* createCal(); extern "C" CALSHARED_EXPORT void deleteCal(Cal *cal); #endif // CAL_H
#include "cal.h"
Cal::Cal()
{
}
int Cal::add(int a, int b)
{
return a + b;
}
int Cal::substract(int a, int b)
{
return a - b;
}
Cal* createCal()
{
return new Cal();
}
void deleteCal(Cal *cal)
{
delete cal;
cal = NULL;
}
CalTest呼叫的時候,CalTest.pro裡就不需要
INCLUDEPATH += $${PWD}/lib/cal
LIBS += $${PWD}/lib/cal/Cal.lib
這兩行程式碼了,直接看cpp檔案
#include "dialog.h"
#include "ui_dialog.h"
#include <QDebug>
#include <QLibrary>
#include "../Cal/cal.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
typedef Cal* (*CreateCal)();
typedef void (*DeleteCal)(Cal *cal);
QLibrary lib("Cal.dll");
if (lib.load()) {
CreateCal createCal = (CreateCal)lib.resolve("createCal");
DeleteCal deleteCal = (DeleteCal)lib.resolve("deleteCal");
if (createCal && deleteCal) {
Cal *pcal = createCal();
int a = 100, b = 200;
int addResult = pcal->add(a, b);
int substractResult = pcal->substract(a, b);
qDebug() << "add:" << addResult << ",substract:" << substractResult;
deleteCal(pcal);
} else {
qDebug() << "resolve failed";
}
lib.unload();
} else {
qDebug() << "load failed";
}
}
Dialog::~Dialog()
{
delete ui;
}
#include "../Cal/cal.h"標頭檔案還是需要的,因為我們不知道Cal類是怎麼宣告的。
這樣當這個dll不存在的時候我們的程式還是可以通過編譯執行的。
但是這裡還存在問題,Cal類我們還是匯出了,因為我們演示的是一個比較簡單的類,如果這個類比較複雜那麼匯出就有麻煩了,因為匯出類的父類和子類也要匯出,所以我們希望這個類不匯出也可以使用。
3.純虛基類,不匯出類,通過虛表來定位具體的函式
Cal工程程式碼
#ifndef CALINTERFACE_H
#define CALINTERFACE_H
#include "cal_global.h"
class CalInterface
{
public:
virtual ~CalInterface() {}
virtual int add(int a, int b) = 0;
virtual int substract(int a, int b) = 0;
};
extern "C" CALSHARED_EXPORT CalInterface* createCal();
extern "C" CALSHARED_EXPORT void deleteCal(CalInterface *cal);
#endif // CALINTERFACE_H
#include "CalInterface.h"
#include "cal.h"
CalInterface* createCal()
{
return new Cal();
}
void deleteCal(CalInterface *cal)
{
delete cal;
cal = NULL;
}
#ifndef CAL_H
#define CAL_H
#include "cal_global.h"
#include "CalInterface.h"
class Cal : public CalInterface
{
public:
Cal();
virtual ~Cal();
virtual int add(int a, int b);
virtual int substract(int a, int b);
};
#endif // CAL_H
Cal.cpp內容和上面一樣CalTest工程呼叫:
#include "dialog.h"
#include "ui_dialog.h"
#include <QDebug>
#include <QLibrary>
#include "../Cal/CalInterface.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
typedef CalInterface* (*CreateCal)();
typedef void (*DeleteCal)(CalInterface *cal);
QLibrary lib("Cal.dll");
if (lib.load()) {
CreateCal createCal = (CreateCal)lib.resolve("createCal");
DeleteCal deleteCal = (DeleteCal)lib.resolve("deleteCal");
if (createCal && deleteCal) {
CalInterface *pcal = createCal();
int a = 100, b = 200;
int addResult = pcal->add(a, b);
int substractResult = pcal->substract(a, b);
qDebug() << "add:" << addResult << ",substract:" << substractResult;
deleteCal(pcal);
} else {
qDebug() << "resolve failed";
}
lib.unload();
} else {
qDebug() << "load failed";
}
}
Dialog::~Dialog()
{
delete ui;
}
這樣Cal類就不必要匯出了,而且CalInterface.h標頭檔案是比較獨立的,不依賴於其他的一些東西。
C++呼叫方法跟這類似,只是將QLibrary換成了LoadLibrary,GetProcAddress。
4.外掛編寫(lower-level api)
1>建立C++庫,型別選擇Qt Plugin,名稱CalPlugin,刪掉自動生成的genericplugin.h和genericplugin.cpp兩個檔案
2>建立介面類標頭檔案calinterface.h
#ifndef CALINTERFACE_H
#define CALINTERFACE_H
#include <QtPlugin>
class CalInterface
{
public:
virtual ~CalInterface() {}
virtual int add(int a, int b) = 0;
virtual int substract(int a, int b) = 0;
};
Q_DECLARE_INTERFACE(CalInterface, "org.qt-project.Qt.Examples.CalPlugin.CalInterface")
#endif // CALINTERFACE_H
3>建立實現類Cal
#ifndef CAL_H
#define CAL_H
#include <QObject>
#include "calinterface.h"
class Cal : public QObject, public CalInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.CalPlugin.Cal" FILE "CalPlugin.json")
Q_INTERFACES(CalInterface)
public:
explicit Cal(QObject *parent = 0);
int add(int a, int b);
int substract(int a, int b);
};
#endif // CAL_H
#include "cal.h"
Cal::Cal(QObject *parent)
: QObject(parent)
{
}
int Cal::add(int a, int b)
{
return a + b;
}
int Cal::substract(int a, int b)
{
return a - b;
}
編譯預設生成的dll檔案在安裝目錄的plugins子目錄下面,等會我們需要將它拷貝出來。
5.呼叫外掛
1>建立CalPluginTest對話方塊型別的應用程式,編譯執行;
2>在CalPluginTest.exe所在目錄下新建一個plugins子目錄,將上面生成的CalPlugin.dll外掛拷貝到plugins目錄下;
3>直接在建構函式寫測試程式碼如下:
#include "dialog.h"
#include "ui_dialog.h"
#include <QtWidgets>
#include "../CalPlugin/calinterface.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
QDir dir(QCoreApplication::applicationDirPath() + "/plugins");
foreach (QString filename, dir.entryList(QDir::Files)) {
qDebug() << "path:" << dir.absoluteFilePath(filename);
QPluginLoader loader(dir.absoluteFilePath(filename));
CalInterface *pcal = qobject_cast<CalInterface*>(loader.instance());
if (pcal) {
int a = 12, b = 34;
int addResult = pcal->add(a, b);
int substractResult = pcal->substract(a, b);
qDebug() << "add:" << addResult << ", substract:" << substractResult;
} else {
qDebug() << "error";
}
}
}
Dialog::~Dialog()
{
delete ui;
}