博客地址已更改,文章數量較多不便批量修改,若想訪問源文請到 coologic博客 查閱,網址:www.coologic.cn
如本文記錄地址為 techieliang.com/A/B/C/ 請改為 www.coologic.cn/A/B/C/ 即可查閱
版權聲明:若無來源注明, Techie亮博客文章均為原創。 轉載請以鏈接形式標明本文標題和地址:
本文標題:Qt動態連接庫/靜態連接庫創建與使用,QLibrary動態加載庫 本文地址: https://www.techieliang.com/2017/12/680/
1. 動態連接庫創建與使用
1.1. 項目創建
注意選擇成shared library
此時新建的項目pro文件:
- QT -= gui
- TARGET = library
- TEMPLATE = lib
- DEFINES += LIBRARY_LIBRARY
- DEFINES += QT_DEPRECATED_WARNINGS
- SOURCES += \
- library.cpp
- HEADERS += \
- library.h
- unix {
- target.path = /usr/lib
- INSTALLS += target
- }
注意其Template為lib,且聲明了一個LIBRARY_LIBRARY
對於global.h文件建議直接拷貝到library.h,這樣發布的時候只需要給別人一個.h文件
library.h
- #ifndef LIBRARY_H
- #define LIBRARY_H
- #include <QtCore/qglobal.h>
- #if defined(LIBRARY_LIBRARY)
- # define LIBRARYSHARED_EXPORT Q_DECL_EXPORT
- #else
- # define LIBRARYSHARED_EXPORT Q_DECL_IMPORT
- #endif
- class LIBRARYSHARED_EXPORT Library {
- public:
- Library();
- int sum(int a, int b);
- };
- int LIBRARYSHARED_EXPORT sum(int a, int b);
- #endif // LIBRARY_H
pro聲明的宏在這里用上了,做了一個判斷,如果有定義則LIBRARYSHARED_EXPORT=Q_DECL_EXPORT,否則等於Q_DECL_IMPORT,也就是說在這個lib項目里是導出的意思,在其他項目因為給別人的只有.h文件並沒有LIBRARY_LIBRARY的定義,所以是導入。從而實現不做任何修改即可發布.h文件。
將global.h拷貝到library.h也是為了只提供一個文件,否則若忘記了提供global.h調用方會提示缺少文件。
library.cpp
- Library::Library() {
- }
- int Library::sum(int a, int b) {
- return a+b;
- }
- int sum(int a, int b) {
- return a+b;
- }
此時直接Ctrl+B構建即可liblibrary.a、library.dll、library.o三個文件(MinGW版,VS的會有lib文件),提供給調用方*.h和*.dll文件即可(windows,linux共享庫是*.so)
注意生成庫也區分debug和release,debug的庫內帶有調試代碼,一般debug的庫文件名最后是d也就是
1.2. 調用-使用.h文件
建立一個Qt Console Application項目,將library.dll和library.h文件拷貝到項目目錄下(和新項目的main.cpp在一起即可)
默認pro文件:
- QT -= gui
- CONFIG += c++11 console
- CONFIG -= app_bundle
- DEFINES += QT_DEPRECATED_WARNINGS
- SOURCES += main.cpp
在打開的pro項目右鍵,選擇添加庫(Add library),可以把dll文件包含到項目里,如果不包含此處選擇外部庫(External Library)
在pro文件最后添加
LIBS += library.dll
簡單的寫法是上面的樣子,建議使用完整的寫法:
LIBS += -LD:/my_program_design/dll_test/test_library_by_header/ -llibrary
+=前后允許有空格? -L和路徑名不可有空格?? -l前面有空格后面不可有空格要緊跟文件名?? 文件名不需要后綴,系統會自動識別是dll還是lib
文件名是library,前面加了個-l變成了-llibrary,別忘了-l
main.cpp
- #include <QCoreApplication>
- #include <library.h>
- #include <QDebug>
- int main(int argc, char *argv[]) {
- QCoreApplication a(argc, argv);
- qDebug()<<sum(1,2);//測試c函數
- Library t;
- qDebug()<<t.sum(1,2);//測試類函數
- return 0;
- }
此時運行,可以生成成功,但是會報錯,因為dll文件還需要拷貝到生成的exe文件目錄,拷貝后再運行即可
2. 靜態庫創建及使用
2.1. 創建
見動態庫第二圖,創建時不要選擇shared,選擇靜態連接庫Statically Linked Library。
創建項目以后沒有什么特色,不會有global.h文件也不會有一個export、import的定義,因為靜態庫不需要導入導出,生成庫提供給使用者,使用者在編譯時會將代碼需要的代碼編譯到自己的項目中,不需要附帶dll/lib等文件。
先看pro文件:
- QT -= gui
- TARGET = static_library
- TEMPLATE = lib
- CONFIG += staticlib
- SOURCES += \
- static_library.cpp
- HEADERS += \
- static_library.h
- unix {
- target.path = /usr/lib
- INSTALLS += target
- }
相比於動態鏈接庫差異是增加了CONFIG += staticlib 刪掉了DEFINES
.h和.cpp文件和動態庫一樣,只不過沒有了LIBRARYSHARED_EXPORT
然后運行就會生成文件libstatic_library.a和static_library.o文件(MinGW,VS是lib文件)
2.2. 使用
和動態庫一樣新建個項目,把libstatic_library.a或者lib文件以及static_library.h考到項目
在pro文件增加:
LIBS += -LD:/my_program_design/dll_test/test_static_library/ -llibstatic_library
main.cpp文件:
- #include <QCoreApplication>
- #include <static_library.h>
- #include <QDebug>
- int main(int argc, char *argv[]) {
- QCoreApplication a(argc, argv);
- qDebug()<<sum(1,2);
- Static_library l;
- qDebug()<<l.sum(1,2);
- return 0;
- }
運行即可,這里就不需要吧.a文件復制到程序目錄了,因為靜態庫編譯的時候已經把需要的內容編譯到exe程序了。
3. QLibrary動態加載動態庫
3.1. 介紹
在動態庫使用那里,直接編譯運行會無法打開,把dll文件拷貝到運行目錄才能打開程序,否則會提示缺少文件,然后就沒有然后了。。。
不知道是否注意到有些程序是主動提示缺少文件錯誤的?會在程序運行到需要庫文件時提示缺少此庫,並且可以自定義提示內容,而不是用系統默認的錯誤提示,若想實現此功能需要動態加載。當然在庫供應方只給了dll文件沒給.h的時候也能使用,換句話說動態加載方式在編寫項目時不需要.h文件,也不需要在pro文件增加“libs+=”
先看幫助文檔:http://doc.qt.io/qt-5/qlibrary.html
接口很簡單:
- QLibrary(QObject *parent = Q_NULLPTR)
- QLibrary(const QString &fileName, QObject *parent = Q_NULLPTR)
- QLibrary(const QString &fileName, int verNum, QObject *parent = Q_NULLPTR)
- QLibrary(const QString &fileName, const QString &version, QObject *parent = Q_NULLPTR)
- ~QLibrary()
- QString errorString() const
- QString fileName() const
- bool isLoaded() const
- bool load()
- LoadHints loadHints() const
- QFunctionPointer resolve(const char *symbol)
- void setFileName(const QString &fileName)
- void setFileNameAndVersion(const QString &fileName, int versionNumber)
- void setFileNameAndVersion(const QString &fileName, const QString &version)
- void setLoadHints(LoadHints hints)
- bool unload()
注意load用完了記着unload,還可以做version版本判斷,其他的不說了,直接看簡單范例。
3.2. 范例
不需要吧dll文件放到工程目錄就行,因為編譯的時候不會訪問它,只需要放到運行目錄即可
- #include <QCoreApplication>
- #include <QString>
- #include <QDebug>
- #include <QLibrary>
- typedef int (*myfun)(int, int);//定義函數格式
- int main(int argc, char *argv[]) {
- QCoreApplication a(argc, argv);
- QLibrary test_dll("library.dll");//加載dll
- if(test_dll.load()) {//判斷是否加載成功
- myfun fun1 = (myfun)test_dll.resolve("sum");//獲取dll的函數
- if (fun1) {//判斷是否獲取到此函數
- double result;
- result=fun1(1,2);//和正常調用函數一樣了
- qDebug()<<QString(QStringLiteral("load ok, result:"))+
- QString::number(result);
- }
- else {
- //函數解析失敗
- qDebug()<<QStringLiteral("dll function load error");
- }
- }
- else {
- qDebug()<<QStringLiteral("dll load error");//dll文件加載失敗
- }
- return 0;
- }
范例很簡單,然后運行就會發現給出的結果是:dll function load error
此時不要盲目的去找檢查typedef 的錯誤,其實這個程序沒錯,錯的是之前的dll項目。
因為c++為了適應函數重載,對於函數名稱在編譯過程中會做一定的修改,所以此時找sum肯定找不到,應該修改動態庫的項目文件,把頭文件改成:
- #ifndef LIBRARY_H
- #define LIBRARY_H
- #include <QtCore/qglobal.h>
- #if defined(LIBRARY_LIBRARY)
- # define LIBRARYSHARED_EXPORT Q_DECL_EXPORT
- #else
- # define LIBRARYSHARED_EXPORT Q_DECL_IMPORT
- #endif
- class LIBRARYSHARED_EXPORT Library {
- public:
- Library();
- int sum(int a, int b);
- };
- extern "C" int LIBRARYSHARED_EXPORT sum(int a, int b);
- #endif // LIBRARY_H
extern “C” 包含雙重含義,從字面上即可得到:首先,被它修飾的目標是“extern”的;其次,被它修飾的目標是“C”的。讓我們來詳細解讀這兩重含義。
被extern “C”限定的函數或變量是extern類型的:extern是C/C++語言中表明函數和全局變量作用范圍(可見性)的關鍵字,該關鍵字告訴編譯器,其聲明的函數和變量可以在本模塊或其它模塊中使用。同時”C”又告訴編譯器以C語言方式編譯和連接。
把新生成的文件拷貝到運行目錄即可得到”load ok, result:3″
3.3. 比較extern”C”的dll與原始dll的差別
用depends工具查看dll文件,我把新的命名為library.dll舊的命名為library2.dll看圖對比即可:
轉載請以鏈接形式標明本文標題和地址:Techie亮博客 » Qt動態連接庫/靜態連接庫創建與使用,QLibrary動態加載庫