1. 程式人生 > >Qt動態連接庫/靜態連接庫創建與使用,QLibrary動態加載庫

Qt動態連接庫/靜態連接庫創建與使用,QLibrary動態加載庫

成功 方式 ++ 版本 應該 blog 不包含 對比 body

版權聲明:若無來源註明,Techie亮博客文章均為原創。 轉載請以鏈接形式標明本文標題和地址:
本文標題:Qt動態連接庫/靜態連接庫創建與使用,QLibrary動態加載庫 本文地址:http://techieliang.com/2017/12/680/ 文章目錄
  • 1. 動態連接庫創建與使用
  •  1.1. 項目創建
  •  1.2. 調用-使用.h文件
  • 2. 靜態庫創建及使用
  •  2.1. 創建
  •  2.2. 使用
  • 3. QLibrary動態加載動態庫
  •  3.1. 介紹
  •  3.2. 範例
  •  3.3. 比較extern”C”的dll與原始dll的差別

1. 動態連接庫創建與使用

1.1. 項目創建

技術分享圖片

註意選擇成shared library

技術分享圖片

此時新建的項目pro文件:

  1. QT -= gui
  2. TARGET = library
  3. TEMPLATE = lib
  4. DEFINES += LIBRARY_LIBRARY
  5. DEFINES += QT_DEPRECATED_WARNINGS
  6. SOURCES += \
  7. library.cpp
  8. HEADERS += \
  9. library.h
  10. unix {
  11. target.path = /usr/lib
  12. INSTALLS += target
  13. }

註意其Template為lib,且聲明了一個LIBRARY_LIBRARY

對於global.h文件建議直接拷貝到library.h,這樣發布的時候只需要給別人一個.h文件

library.h

  1. #ifndef LIBRARY_H
  2. #define LIBRARY_H
  3. #include <QtCore/qglobal.h>
  4. #if defined(LIBRARY_LIBRARY)
  5. # define LIBRARYSHARED_EXPORT Q_DECL_EXPORT
  6. #else
  7. # define LIBRARYSHARED_EXPORT Q_DECL_IMPORT
  8. #endif
  9. class LIBRARYSHARED_EXPORT Library {
  10. public:
  11. Library();
  12. int sum(int a, int b);
  13. };
  14. int LIBRARYSHARED_EXPORT sum(int a, int b);
  15. #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

  1. Library::Library() {
  2. }
  3. int Library::sum(int a, int b) {
  4. return a+b;
  5. }
  6. int sum(int a, int b) {
  7. return a+b;
  8. }

此時直接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文件:

  1. QT -= gui
  2. CONFIG += c++11 console
  3. CONFIG -= app_bundle
  4. DEFINES += QT_DEPRECATED_WARNINGS
  5. 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

  1. #include <QCoreApplication>
  2. #include <library.h>
  3. #include <QDebug>
  4. int main(int argc, char *argv[]) {
  5. QCoreApplication a(argc, argv);
  6. qDebug()<<sum(1,2);//測試c函數
  7. Library t;
  8. qDebug()<<t.sum(1,2);//測試類函數
  9. return 0;
  10. }

此時運行,可以生成成功,但是會報錯,因為dll文件還需要拷貝到生成的exe文件目錄,拷貝後再運行即可

2. 靜態庫創建及使用

2.1. 創建

見動態庫第二圖,創建時不要選擇shared,選擇靜態連接庫Statically Linked Library。

創建項目以後沒有什麽特色,不會有global.h文件也不會有一個export、import的定義,因為靜態庫不需要導入導出,生成庫提供給使用者,使用者在編譯時會將代碼需要的代碼編譯到自己的項目中,不需要附帶dll/lib等文件。

先看pro文件:

  1. QT -= gui
  2. TARGET = static_library
  3. TEMPLATE = lib
  4. CONFIG += staticlib
  5. SOURCES += \
  6. static_library.cpp
  7. HEADERS += \
  8. static_library.h
  9. unix {
  10. target.path = /usr/lib
  11. INSTALLS += target
  12. }

相比於動態鏈接庫差異是增加了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文件:

  1. #include <QCoreApplication>
  2. #include <static_library.h>
  3. #include <QDebug>
  4. int main(int argc, char *argv[]) {
  5. QCoreApplication a(argc, argv);
  6. qDebug()<<sum(1,2);
  7. Static_library l;
  8. qDebug()<<l.sum(1,2);
  9. return 0;
  10. }

運行即可,這裏就不需要吧.a文件復制到程序目錄了,因為靜態庫編譯的時候已經把需要的內容編譯到exe程序了。

3. QLibrary動態加載動態庫

3.1. 介紹

在動態庫使用那裏,直接編譯運行會無法打開,把dll文件拷貝到運行目錄才能打開程序,否則會提示缺少文件,然後就沒有然後了。。。

不知道是否註意到有些程序是主動提示缺少文件錯誤的?會在程序運行到需要庫文件時提示缺少此庫,並且可以自定義提示內容,而不是用系統默認的錯誤提示,若想實現此功能需要動態加載。當然在庫供應方只給了dll文件沒給.h的時候也能使用,換句話說動態加載方式在編寫項目時不需要.h文件,也不需要在pro文件增加“libs+=”

先看幫助文檔:http://doc.qt.io/qt-5/qlibrary.html

接口很簡單:

  1. QLibrary(QObject *parent = Q_NULLPTR)
  2. QLibrary(const QString &fileName, QObject *parent = Q_NULLPTR)
  3. QLibrary(const QString &fileName, int verNum, QObject *parent = Q_NULLPTR)
  4. QLibrary(const QString &fileName, const QString &version, QObject *parent = Q_NULLPTR)
  5. ~QLibrary()
  6. QString errorString() const
  7. QString fileName() const
  8. bool isLoaded() const
  9. bool load()
  10. LoadHints loadHints() const
  11. QFunctionPointer resolve(const char *symbol)
  12. void setFileName(const QString &fileName)
  13. void setFileNameAndVersion(const QString &fileName, int versionNumber)
  14. void setFileNameAndVersion(const QString &fileName, const QString &version)
  15. void setLoadHints(LoadHints hints)
  16. bool unload()

註意load用完了記著unload,還可以做version版本判斷,其他的不說了,直接看簡單範例。

3.2. 範例

不需要吧dll文件放到工程目錄就行,因為編譯的時候不會訪問它,只需要放到運行目錄即可

  1. #include <QCoreApplication>
  2. #include <QString>
  3. #include <QDebug>
  4. #include <QLibrary>
  5. typedef int (*myfun)(int, int);//定義函數格式
  6. int main(int argc, char *argv[]) {
  7. QCoreApplication a(argc, argv);
  8. QLibrary test_dll("library.dll");//加載dll
  9. if(test_dll.load()) {//判斷是否加載成功
  10. myfun fun1 = (myfun)test_dll.resolve("sum");//獲取dll的函數
  11. if (fun1) {//判斷是否獲取到此函數
  12. double result;
  13. result=fun1(1,2);//和正常調用函數一樣了
  14. qDebug()<<QString(QStringLiteral("load ok, result:"))+
  15. QString::number(result);
  16. }
  17. else {
  18. //函數解析失敗
  19. qDebug()<<QStringLiteral("dll function load error");
  20. }
  21. }
  22. else {
  23. qDebug()<<QStringLiteral("dll load error");//dll文件加載失敗
  24. }
  25. return 0;
  26. }

範例很簡單,然後運行就會發現給出的結果是:dll function load error

此時不要盲目的去找檢查typedef 的錯誤,其實這個程序沒錯,錯的是之前的dll項目。

因為c++為了適應函數重載,對於函數名稱在編譯過程中會做一定的修改,所以此時找sum肯定找不到,應該修改動態庫的項目文件,把頭文件改成:

  1. #ifndef LIBRARY_H
  2. #define LIBRARY_H
  3. #include <QtCore/qglobal.h>
  4. #if defined(LIBRARY_LIBRARY)
  5. # define LIBRARYSHARED_EXPORT Q_DECL_EXPORT
  6. #else
  7. # define LIBRARYSHARED_EXPORT Q_DECL_IMPORT
  8. #endif
  9. class LIBRARYSHARED_EXPORT Library {
  10. public:
  11. Library();
  12. int sum(int a, int b);
  13. };
  14. extern "C" int LIBRARYSHARED_EXPORT sum(int a, int b);
  15. #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動態加載庫

Qt動態連接庫/靜態連接庫創建與使用,QLibrary動態加載庫