深度解析 Qt 中動態連結庫
本文介紹的是Qt 中動態連結庫,現在有些軟體有自動升級功能,有些就是下載新的DLL檔案,替換原來的動態連結庫,MFC好象也有類似機制,Qt還有一種方式,就是把一個QWidget子類,編譯成動態連結庫.然後根據動態連結庫建立一個物件,返回QWidget子針,如果你的類不是QWidget的子類,就不能用這種方法了
QLibrary也是用來載入動態連結庫,但它創建出來返回的是某個"方法的指標"(不需要標頭檔案),而QPluginLoader建立返回的是"物件的指標"(物件中有哪些方法可呼叫,就要標頭檔案說了,所以上面的例子中,需要提供一個介面標頭檔案)
昨天一個同事說要把一個類做成DLL的形式,但這個類不是從QWidget繼承。 研究了一下,發現Qt
寫一個例子說明吧:
工程A中,使用了這樣一個類,專門用來讓算所得稅;
- Tax.h
- class Tax
- {
- float incomeTax(int income);
- };
- Tax.cpp
- Tax::incomeTax(int income)
- {
- float res=(income-1600)*0.5;
- return res;
- }
- main.cpp
- Int main()
- {
- Tax taxobject;
- taxObject. incomeTax(2200);
- ............
- }
編譯後部署到機器上.
如果計稅方式變了,則incomeTax()必須改寫了;
- Tax::incomeTax(int income)
- {
- float res=(income-2000)*0.3;
- return res;
- }
標頭檔案沒有變,只是修改了計稅方式,工程A必須重新編譯,然後重新部署; 如果工程A很大或部署的機器很多,代價就大了.
如果讓Tax類,單獨編譯成動態連結庫,再把Tax中的要被使用的介面寫在另一個頭檔案中,供工程A使用;每次計稅方式變了,只需修改Tax類,然後重新編譯生成動態連結庫,然後替換工程A中的相應的動態連結庫.而工程A不用重新編譯即可使用新的計稅方式.
新建一工程B,專門用來把Tax類做成動態連結庫的形式:
- //首先定義介面(只要一個頭檔案即可)
- Tax.h
- class Tax
- {
- virtual float incomeTax(int income);
- };
- QT_BEGIN_NAMESPACE
- Q_DECLARE_INTERFACE(Tax,"TaxDLL/1.0"); //這個巨集用宣告介面
- QT_END_NAMESPACE
然後從寫一個具體業務類,繼承上面的介面和QObject,實現介面中定義的方法
- TaxPlugin.h
- class TaxPlugin:public QObject,Tax
- {
- Q_OBJECT
- Q_INTERFACES(Tax)
- public:
- float incomeTax(int income);
- };
- TaxPlugin.cpp
- TaxPlugin::incomeTax(int income)
- {
- float res=(income-1600)*0.5;
- return res;
- }
- Q_EXPORT_PLUGIN2(Tax, TaxPlugin); //這個巨集用來匯出動態連結庫
編譯工程B,生成Tax.dll.
把工程Tax.dll和標頭檔案Tax.h,拷到工程A中,供工程A使用.
工程A中
- int main()
- {
- Tax *taxObject;
- QPluginLoader pluginLoader("Tax.dll");
- QObject *plugin = pluginLoader.instance();
- taxObject= qobject_cast<Tax *>(plugin);
- taxObject->incomeTax(2100);
- }
如果稅率變了,只要修改工程B中的類,然後重新編譯生成Tax.dll,替換工程A中原來的庫.而工程A不必重新編譯即可使用新的計稅方式.
現在有些軟體有自動升級功能,有些就是下載新的DLL檔案,替換原來的動態連結庫。
MFC好象也有類似機制
Qt還有一種方式,就是把一個QWidget子類,編譯成動態連結庫.然後根據動態連結庫建立一個物件,返回QWidget子針,如果你的類不是QWidget的子類,就不能用這種方法了
QLibrary也是用來載入動態連結庫,但它創建出來返回的是某個"方法的指標"(不需要標頭檔案),而QPluginLoader建立返回的是"物件的指標"(物件中有哪些方法可呼叫,就要標頭檔案說了,所以上面的例子中,需要提供一個介面標頭檔案)動態
記錄一下QT5 動態連結庫的建立和使用
在文章的最後有完成的程式碼供下載
1.建立動態連結庫
先新建一個庫專案
選擇chose進入下一下頁面,型別選擇共享庫,輸入一個名稱:我輸入的是sld
再點選下一步到
如果這裡我們需要QtGui所以也勾選上了
再點選下一步直到完成
我們在.pro檔案里加上
DESTDIR =..\MyDebug
它的意思是我們把生成的內容放到這個資料夾裡,如果沒有它會自動生成
點選專案把Shadow build 去掉勾選
設定完成後我們來實現方法測試
把sld.h修改成這樣
#ifndef SLD_H #define SLD_H #include "sld_global.h" #include <QString> class SLDSHARED_EXPORT Sld { public : Sld(); QString GetStr(); }; #endif
它的.cpp
#include "sld.h" #include <QDebug> Sld::Sld() { qDebug()<<""; } QString Sld::GetStr() { return "aa"; }
我們那了一個測試方法返回aa
我們再新新增一個窗體供測試用
我給命名為MyDialog
把.h檔案修改成
#ifndef MYDIALOG_H #define MYDIALOG_H #include <QDialog> #include "sld_global.h" namespace Ui { class MyDialog; } class SLDSHARED_EXPORT MyDialog : public QDialog { Q_OBJECT public: explicit MyDialog(QWidget *parent = 0); ~MyDialog(); private: Ui::MyDialog *ui; }; #endif // MYDIALOG_H
,cpp
#include "mydialog.h" #include "ui_mydialog.h" MyDialog::MyDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MyDialog) { ui->setupUi(this); } MyDialog::~MyDialog() { delete ui; }
這裡什麼也沒做只是把SLDSHARED_EXPORT加上了
好了就這樣,我們構建一下就會在MyDebug資料夾裡生成我們的dll
2.怎麼使用動態連結庫
新建一個Qt WidgetsApplication專案
在.pro檔案裡修改和上邊一樣加上
DESTDIR =..\MyDebug 再點選專案把shadow build 去掉
再加上
INCLUDEPATH +=../sld
LIBS +=$$DESTDIR/sld.lib
第一句是把sld專案的資料夾包含到這個專案裡來,這樣我們就能直接用它裡面的標頭檔案了
第二句是告訴編譯器lib在哪(我用的是vs的編譯器如果 用mingw就是改成sld.dll)
我樣在窗體上加一個按鈕,並新增槽
.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_pushButton_clicked(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "sld.h" #include <QMessageBox> #include "mydialog.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_clicked() { MyDialog * my =new MyDialog(this); my->show(); QMessageBox * msg=new QMessageBox(this); Sld s; msg->setText(s.GetStr()); msg->show(); }
好了,構建,執行看效果吧
作者:李鵬 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。 本文主要講解在QT開發環境中如何使用VC生成的DLL及QT自身生成的DLL。至於其它情況本文不作討論。連線庫分為2種
(1)動態連線庫,通常有.h .lib .dll三個檔案,功能實現在dll中 (2)靜態連線庫,通常有.h .lib二個檔案,功能實現在lib中 由上可以看出動態庫的lib和靜態庫的lib檔案是不同的。 如果使用生成連線庫的開發環境與使用連線庫的開發環境相同,一般不會出什麼問題,如VC寫的連線庫 (包括動態庫和靜態庫)還在VC中用一般不會有什麼問題的。有時候我們需要DLL跨開發環境,如以前VC下的 DLL想在QT中用。有網友說QT不支援VC生成的靜態庫,所以只測試QT使用動態庫的情況。 【先看VC生成的DLL】 使用VC6建立Win32 dynamic-link library工程,工程中只有兩個檔案,編譯即可生成DLL和LIB [html] view plaincopyprint?- ////////////////////////////////// MFCDLL.h /////////////////////////////////////
- #ifndef _MFCDLL_H
- #define _MFCDLL_H
- #ifdef __cplusplus
- extern "C" {
- #endif
- #ifdef DLL
- // do nothing
- #else
- #define DLL __declspec(dllimport)
- #endif
- DLL void hello();
- DLL int add(int a, int b);
- #ifdef __cplusplus
- }
- #endif
- #endif
////////////////////////////////// MFCDLL.h /////////////////////////////////////
#ifndef _MFCDLL_H
#define _MFCDLL_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DLL
// do nothing
#else
#define DLL __declspec(dllimport)
#endif
DLL void hello();
DLL int add(int a, int b);
#ifdef __cplusplus
}
#endif
#endif
[cpp]
view plaincopyprint?
- ////////////////////////////////// MFCDLL.cpp /////////////////////////////////////
- #define DLL __declspec(dllexport)
- #include "MFCDLL.h"
- #include <windows.h>
- void hello()
- {
- ::MessageBox(NULL, "hello world!",
- "greeting", MB_OK);
- }
- int add(int a, int b)
- {
- return a + b;
- }
////////////////////////////////// MFCDLL.cpp /////////////////////////////////////
#define DLL __declspec(dllexport)
#include "MFCDLL.h"
#include <windows.h>
void hello()
{
::MessageBox(NULL, "hello world!",
"greeting", MB_OK);
}
int add(int a, int b)
{
return a + b;
}
【使用QT生成DLL】使用QT建立動態庫工程,編譯即可得到DLL(無LIB檔案)
[cpp]
view plaincopyprint?
- ////////////////////////////////// qtdll_global.h //////////////////////////////
- #ifndef QTDLL_GLOBAL_H
- #define QTDLL_GLOBAL_H
- #include
- #if defined(QTDLL_LIBRARY)
- # define QTDLLSHARED_EXPORT Q_DECL_EXPORT
- #else
- # define QTDLLSHARED_EXPORT Q_DECL_IMPORT
- #endif
- #endif // QTDLL_GLOBAL_H
////////////////////////////////// qtdll_global.h //////////////////////////////
#ifndef QTDLL_GLOBAL_H
#define QTDLL_GLOBAL_H
#include
#if defined(QTDLL_LIBRARY)
# define QTDLLSHARED_EXPORT Q_DECL_EXPORT
#else
# define QTDLLSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // QTDLL_GLOBAL_H
[cpp]
view plaincopyprint?
- ////////////////////////////////// qtdll.h /////////////////////////////////////
- #ifndef QTDLL_H
- #define QTDLL_H
- #include "qtdll_global.h"
- class QTDLLSHARED_EXPORT QTDLL
- {
- public:
- QTDLL();
- public:
- int add(int a, int b);
- };
- #endif // QTDLL_H
////////////////////////////////// qtdll.h /////////////////////////////////////
#ifndef QTDLL_H
#define QTDLL_H
#include "qtdll_global.h"
class QTDLLSHARED_EXPORT QTDLL
{
public:
QTDLL();
public:
int add(int a, int b);
};
#endif // QTDLL_H
[cpp]
view plaincopyprint?
- ////////////////////////////////// qtdll.cpp ///////////////////////////////////
- #include "qtdll.h"
- QTDLL::QTDLL()
- {
- }
- int QTDLL::add(int a, int b)
- {
- return a + b;
- }
////////////////////////////////// qtdll.cpp ///////////////////////////////////
#include "qtdll.h"
QTDLL::QTDLL()
{
}
int QTDLL::add(int a, int b)
{
return a + b;
}
【QT顯式載入VC生成的DLL】
[cpp]
view plaincopyprint?
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include <QLibrary>
- #include <QDebug>
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- // DLL顯式載入,只需要DLL檔案即可,不需要.H和.LIB檔案
- // 需要將DLL放到可執行目錄中
- typedefvoid(*FUN1)();
- typedefint(*FUN2)(int, int);
- QLibrary lib("MFCDLL.dll");
- if (lib.load()) {
- qDebug() << "load ok!";
- FUN1 hello = (FUN1)lib.resolve("hello");
- FUN2 add = (FUN2)lib.resolve("add");
- if (hello) {
- qDebug() << "load hello ok!";
- hello();
- }
- if (add) {
- qDebug() << "load add ok!";
- qDebug() << add(3, 5);
- }
- } else {
- qDebug() << "load error!";
- }
- }
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QLibrary>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// DLL顯式載入,只需要DLL檔案即可,不需要.H和.LIB檔案
// 需要將DLL放到可執行目錄中
typedef void(*FUN1)();
typedef int(*FUN2)(int, int);
QLibrary lib("MFCDLL.dll");
if (lib.load()) {
qDebug() << "load ok!";
FUN1 hello = (FUN1)lib.resolve("hello");
FUN2 add = (FUN2)lib.resolve("add");
if (hello) {
qDebug() << "load hello ok!";
hello();
}
if (add) {
qDebug() << "load add ok!";
qDebug() << add(3, 5);
}
} else {
qDebug() << "load error!";
}
}
【QT隱式載入VC生成的DLL】
[cpp]
view plaincopyprint?
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include "lib/MFCDLL.h"
- #include <QDebug>
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- // DLL隱式載入,只需要.DLL .H和.LIB檔案
- // 1需要將DLL放到可執行目錄中
- // 2將LIB路徑設定到專案PRO檔案中
- // 3將標頭檔案包含進來,如果不包含需要自已宣告函式原型及來源(本質與包含標頭檔案相同)
- hello();
- qDebug() << add(5, 6);
- qDebug() << "ok";
- }
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "lib/MFCDLL.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// DLL隱式載入,只需要.DLL .H和.LIB檔案
// 1需要將DLL放到可執行目錄中
// 2將LIB路徑設定到專案PRO檔案中
// 3將標頭檔案包含進來,如果不包含需要自已宣告函式原型及來源(本質與包含標頭檔案相同)
hello();
qDebug() << add(5, 6);
qDebug() << "ok";
}
pro工程檔案中要設定LIB檔案路徑
[cpp]
view plaincopyprint?
- # lib檔案路徑
- LIBS += "F:/lib/MFC_DLL_TEST_WITH_QT_2/lib/MFCDLL.lib"
# lib檔案路徑
LIBS += "F:/lib/MFC_DLL_TEST_WITH_QT_2/lib/MFCDLL.lib"
【QT使用QT生成的動態庫,隱式】
[cpp]
view plaincopyprint?
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include <QDebug>
- #include "lib/qtdll.h"
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- // QT使用QT生成的DLL
- // 1. 包含標頭檔案
- // 2. 在工程檔案中指定lib路徑
- // 3. 將動態庫拷貝到可執行檔案目錄
- QTDLL dll;
- qDebug() << dll.add(3, 5);
- }
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include "lib/qtdll.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// QT使用QT生成的DLL
// 1. 包含標頭檔案
// 2. 在工程檔案中指定lib路徑
// 3. 將動態庫拷貝到可執行檔案目錄
QTDLL dll;
qDebug() << dll.add(3, 5);
}
pro工程檔案中的設定
[cpp]
view plaincopyprint?
- LIBS += "F:/lib/QT_DLL_TEST_WITH_DLL/lib/QTDLL.dll"