1、关于项目文件.pro的资料
在QT的帮助文档中搜索:
- Third Party Libraries
- qmake Manual
- Adding Libraries to Projects
- Qt for Windows - Requirements
2、pro文件中使用相对路径需要注意的地方
- INCLUDE_PATH 后接的路径./代表的是pro所在目录
- LIBS 后接的./是可执行文件所在的目录,该目录会被DESTDIR影响
- DESTDIR后接的./代表的是pro文件所在目录
- INCLUDE_PATH后的路径不需要加双引号,如“INCLUDEPATH += …/MainFunc/”
- 示例: 链接本项目目录下的/bin下libabc.so
若DESTDIR = ./bin 则有两种写法:
LIBS += -L. -labc 此时.代表可执行目录,也就是DESTDIR
LIBS += -L/$$/ PWD/bin 此时$$PWD就代表此文件所在的目录
3、源代码中的"./"
源代码中的"./"代表构建目录,如“E:\QT_Project\StaticDynamicLibrary\build-DynamicCallDynamicLib-5_7_0_MSVC2013_32bit-Debug”
4、应用程序静态链接动态库
- QT中是通过QLibrary类来实现的;
- 应用程序不可以调用动态库中类的静态成员函数;
- 应用程序不能创建动态库中类的实例,只能动态库自己创建好后返回一个指针,然后应用程序通过这个指针调用类中的成员函数;
- 注意应用程序只能调用动态库中类的虚函数,不能调用非虚函数,也就是只能通过对象来调用,当然通过这个对象是调用不了非虚函数(包括静态成员函数);
- 如果应用程序调用了非虚函数(包含构造函数)就会出现“无法解析的外部符号”的错误;
- 这也解释了为什么插件类和接口类中开放给应用程序使用的函数都是虚函数,因为即便定义了非虚函数程序也调用不了;
- 对于动态库中定义的非成员函数,只要用_declspec(dllexport)将函数导出了,应用程序并不需要用_declspec(dllimport)将函数声明,因为并不是通过导入库知道函数的地址,而是通过QLibrary的resovle()函数获得;
- 应用程序通过动态库传出的对象指针来调用虚函数,也不需要一个用_declspec(dllimport)声明的类,当然还是需要定义一个类,毕竟要通过编译。
5、如何创建动态库
- 在头文件中将类和函数用关键字_declspec(dllexport)导出,如果是库中有多个文件,最好是定义一个然后单独放到一个头文件中,当这个头文件被编译到动态库中时它代表_declspec(dllexport),当这个头文件被编译到应用程序中时代表_declspec(dllimport),这样构造可以同时适应动态库被静态链接和动态链接。此外函数的编译最好加上extern “C”。
#ifndef MYDYNAMICLIB1_GLOBAL_H
#define MYDYNAMICLIB1_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(MYDYNAMICLIB1_LIBRARY)
# define MYDYNAMICLIB1SHARED_EXPORT Q_DECL_EXPORT
#else
# define MYDYNAMICLIB1SHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // MYDYNAMICLIB1_GLOBAL_H
#ifndef TABLEDELEGATE_H
#define TABLEDELEGATE_H
#include <QItemDelegate>
#include "mydynamiclib1_global.h"
class MYDYNAMICLIB1SHARED_EXPORT TableDelegate : public QItemDelegate
{
public:
TableDelegate(QObject* parent=0);
void paint(QPainter* painter,const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
public slots:
private:
QPixmap manImage;
QPixmap womanImage;
};
/*
* 一定要添加上extern “C”,编译器在生成DLL的时候,就会自动的按照C语言的编译方式进行编译,
* 只会编译出函数名,而不会编译上参数类型。
*/
extern "C"
{
MYDYNAMICLIB1SHARED_EXPORT void printLibName();
}
#endif // TABLEDELEGATE_H
- 项目文件中的设置,主要将TEMPLATE变量设置为lib,还有TARGET变量最好也设置下,因为在windows下debug模式的动态库都以d结尾,可以参考如下:
QT -= gui
QT += core widgets
TARGET = MyDynamicLib1
TEMPLATE = lib
DEFINES += MYDYNAMICLIB1_LIBRARY
win32:CONFIG(debug,debug|release):TARGET=$$join(TARGET,,,d)
SOURCES += \
tabledelegate.cpp
HEADERS +=\
mydynamiclib1_global.h \
tabledelegate.h
unix {
target.path = /usr/lib
INSTALLS += target
}
RESOURCES += \
mydynamiclib1.qrc
- 资源可以放到qrc文件中,它们将会被编译到动态库中。
5、两个工具
- Dependency Walker:可以使用Dependency Walker依赖性分析工具,查看exe的依赖关系
- Enigma Virtual Box:将多个文件封装到应用程序主文件,从而制作成为单执行文件的绿色软件。
6、如何静态链接动态库
- 拷贝动态库的头文件、lib文件和dll文件,放到I一个文件夹内,可以这样放置,新建一个以库名为文件名的文件夹,在文件夹内又新建三个文件夹,分别命名为include、lib和bin,分别放入头文件、lib文件和bin文件。
- 在项目文件中添加头文件的搜索路径、动态库的路径和名称,注意此时动态库的路径是指lib文件的路径,dll的路径也可以添加,但不需要添加库名。
注意:dll路径加了之后,可执行文件所在的目录即便不拷贝dll文件,应用程序的调试还是可以进行,不过如果双击可执行文件仍然不能启动。
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = StaticCallDynamicLib
TEMPLATE = app
win32:CONFIG(debug,debug|release): \
LIBS+= -L$$PWD/MyDynamicLib1/lib -lMyDynamicLib1d\
LIBS+= -L$$PWD/MyDynamicLib1/bin #dll文件的路径
else:win32:CONFIG(release,debug|release): LIBS+= -L$$PWD/MyDynamicLib1/lib -lMyDynamicLib1
INCLUDEPATH += ./MyDynamicLib1/include/
SOURCES += main.cpp\
widget.cpp \
contactformmodel.cpp
HEADERS += widget.h \
contactformmodel.h
- 总之,静态链接动态库需要动态库的头文件、lib文件和dll文件,指明位置后应用程序就可以任意的使用库中的函数和类了,要注意此时头文件中要导入类和函数的符号。
7、如何动态链接动态库
- 将动态库的头文件和dll文件拷贝到一个文件夹,存放的位置与静态链接的类似。
- 在工程文件中添加头文件的搜索路径,此时也可以添加dll文件的路径。
#-------------------------------------------------
#
# Project created by QtCreator 2020-02-19T17:55:18
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = DynamicCallDynamicLib
TEMPLATE = app
LIBS+= -L$$PWD/MyDynamicLib1/bin #dll文件的路径
INCLUDEPATH += ./MyDynamicLib1/include/
SOURCES += main.cpp\
widget.cpp \
contactformmodel.cpp
HEADERS += widget.h \
contactformmodel.h \
tabledelegate.h \
mydynamiclib1_global.h
- 新建一个QLibrary对象,将需要的动态库加载进来,注意要判断是否成功加载了。
- 加载成功后调用QLibrary的resolve()函数,获得动态库中函数的地址。
typedef void (*PRINT_FUNC)();
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QImage image;
qDebug()<<"load image:"<<image.load(":/images/resources/images/man.ico")<<endl;
QLibrary* lib=new QLibrary("MyDynamicLib1d",this);
if(!lib->load())
{
qDebug()<<"library MyDynamicLib1d load failed!"<<endl;
return;
}
PRINT_FUNC printFunc=(PRINT_FUNC)lib->resolve("printLibName");
if(printFunc)
printFunc();
}
- 使用动态库中类的方式:在动态库的类中要定义一个获得类实例的虚函数,应用程序获得这个实例的指针后,就可以通过这个指针调用类中其他的虚函数。
typedef MainFunc* (*getObj)();
typedef char* (*dllText)();
typedef int (*dllPrintInt)();
void Widget::testMainFunc()
{
QLibrary* mainFunDll = new QLibrary("./libs/MainFunc.dll");
if (mainFunDll->load()) {
getObj obj = (getObj)mainFunDll->resolve("getDllObj");
// MainFunc mainfunc;
MainFunc* mF = obj();
// mF->test2();
mF->printMsg(QStringLiteral("这是一个DLL调用程序"));
qDebug()<<mF->add(4, 7);
dllText dText = (dllText)mainFunDll->resolve("text");
qDebug() << QString::fromLocal8Bit(dText());
dllPrintInt dPrintInt = (dllPrintInt)mainFunDll->resolve("printInt");
qDebug() << dPrintInt();
} else {
QMessageBox* msgBox = new QMessageBox;
msgBox->setText(QStringLiteral("动态库加载失败!"));
msgBox->setWindowTitle(QStringLiteral("信息框"));
msgBox->show();
}
}
//库中的头文件mainfunc.h
#pragma once
#include "mainfunc_global.h"
#include <string>
class MAINFUNCSHARED_EXPORT MainFunc {
public:
MainFunc();
virtual void test1();
static void test2();
virtual int add(int a,int b);
virtual void printMsg(QString msg);
};
extern "C" {//一定要添加上
MAINFUNCSHARED_EXPORT MainFunc* getDllObj();
MAINFUNCSHARED_EXPORT char* text();
MAINFUNCSHARED_EXPORT int printInt();
}
8、创建静态链接库
不需要将类或者函数导出,只是项目文件要设置下,将CONFIG变量设置为staticlib。
#-------------------------------------------------
#
# Project created by QtCreator 2020-02-19T09:50:34
#
#-------------------------------------------------
QT += core widgets
#TARGET = MyStaticLib1
TEMPLATE = lib
CONFIG += staticlib
SOURCES += \
tabledelegate.cpp
win32:CONFIG(debug,debug|release): TARGET= $$join(TARGET,,,d)
HEADERS += \
tabledelegate.h
unix {
target.path = /usr/lib
INSTALLS += target
}
RESOURCES +=
9、静态链接静态库
将静态链接库的头文件和lib文件拷贝到一个新的文件夹,然后在项目文件中添加头文件的路径和lib文件的路径和库名。注意如果要使用静态链接库中的资源,要在main函数中用Q_INIT_RESOURCE将资源初始化,如Q_INIT_RESOURCE(mystaticlib); 。
#-------------------------------------------------
#
# Project created by QtCreator 2020-02-19T10:06:37
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = MainApplication
TEMPLATE = app
SOURCES += main.cpp\
widget.cpp \
contactformmodel.cpp
HEADERS += widget.h \
contactformmodel.h
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/MyStaticLib/ -lMyStaticLib1
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/MyStaticLib/ -lMyStaticLib1d
else:unix: LIBS += -L$$PWD/MyStaticLib/ -lMyStaticLib1
# 绝对路径,./开头就是相对路径
INCLUDEPATH += $$PWD/MyStaticLib
DEPENDPATH += $$PWD/MyStaticLib
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/MyStaticLib/libMyStaticLib1.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/MyStaticLib/libMyStaticLib1d.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/MyStaticLib/MyStaticLib1.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/MyStaticLib/MyStaticLib1d.lib
else:unix: PRE_TARGETDEPS += $$PWD/MyStaticLib/libMyStaticLib1.a
RESOURCES += \
10、C/C++ "#“与”##"的作用与应用
参考文章:https://blog.csdn.net/CSND_Ayo/article/details/72779614