计算机/软件

QT中dll的生成及使用

2019-03-29  本文已影响0人  Magic11

C++中的库分为三种:静态库、动态库和导入库

1、静态库:

    静态库扩展名为.lib,静态链接(即代码会直接编译进可执行文件)。静态库是一个或多个obj文件的打包。

2、动态库和导入库

    和静态库不同,动态库不会编译进可执行文件中,多个程序引用动态库时,内存中实际只会有一份动态库的内容。
    用QT创建动态库时,实际生成两个文件,一个lib文件和一个dll文件,这个lib文件就是导入库。

    导入库是动态库的辅助库,导入库中不含代码,而是为链接程序提供信息,包含在.exe文件中建立动态链接时要用到的重定位表。

    导入库用于程序开发时,动态库用于程序运行时。

3、在项目中使用动态库的方法

1) 隐式加载

    这种方式和静态库的使用方法一样,注意此时要包含的是导入库而不是动态库,依然需要头文件,代码中可直接使用头文件中的函数名,并且这种方式在运行时需要动态库。

2) 显式加载(又称运行时动态链接):

    如果使用windows接口,则在代码中使用LoadLibrary()显式打开dll文件,使用GetProcAddress获取函数地址然后使用,使用完之后用FreeLibrary显式释放dll文件。这种方式不需要导入库及.h文件。
    在QT中则可以使用QLibrary的load方法。
    注:使用隐式加载时,如果进程在启动时未找到dll,则操作系统将终止此进程。但使用显式加载时则进程不会被终止。
    以上参考:https://blog.csdn.net/finewind/article/details/44959039
https://www.jianshu.com/p/8743a0edb1ee

4、隐示加载dll demo

创建一个TestDll项目,创建项目的时候选择 Library->C++库


image.png

pro文件

QT       -= gui

TARGET = TestDll
TEMPLATE = lib

DEFINES += TESTDLL_LIBRARY

SOURCES += testDll.cpp

HEADERS += testdll.h\
        testdll_global.h \
    basetest.h

unix {
    target.path = /usr/lib
    INSTALLS += target
}

testDll.h

#ifndef TESTDLL_H
#define TESTDLL_H
#include "basetest.h"
#include "testdll_global.h"

class TESTDLLSHARED_EXPORT TestDll : public BaseTest
{

public:
    TestDll();

    virtual int add(int a, int b);
};

extern "C" {
    TESTDLLSHARED_EXPORT BaseTest *getObj();
}

#endif // TESTDLL_H

basetest.h

#ifndef BASETEST
#define BASETEST

class BaseTest
{

public:
    BaseTest() {}

    virtual int add(int a, int b) = 0;
};

#endif // BASETEST

testdll_global.h

#ifndef TESTDLL_GLOBAL_H
#define TESTDLL_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(TESTDLL_LIBRARY)
#  define TESTDLLSHARED_EXPORT Q_DECL_EXPORT
#else
#  define TESTDLLSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // TESTDLL_GLOBAL_H

testDll.cpp

#include "testdll.h"


TestDll::TestDll()
{
}

int TestDll::add(int a, int b)
{
    return a + b;
}

BaseTest *getObj()
{
    return new TestDll();
}

构建之后会在build同时生成一个.lib和.dll文件


image.png

TestDll.lib即为导出库

创建一个DllUser项目

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = DllUser
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp

INCLUDEPATH += include

HEADERS  += mainwindow.h

FORMS    += mainwindow.ui

LIBS += lib/TestDll.lib

CONFIG(debug, debug|release){
    DESTDIR = bin/Debug
} else {
    DESTDIR = bin/Debug
}

在该工程的根目录下创建一个lib文件夹,将dll的导出库拷贝到该目录
在根目录下创建一个include文件夹,将dll的头文件拷贝到该目录
并将TestDll.dll拷贝至DllUser.exe的同级目录


image.png

mainwindow.cpp代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "include/testdll.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    int a = ui->lineEdit->text().toInt();
    int b = ui->lineEdit_2->text().toInt();

    TestDll obj;
    int sum = obj.add(a, b);
    QString str = QString::number(sum);
    ui->label->setText(str);
    ui->label->show();
}

5、显示加载dll demo

    显示加载如果只是导出一个函数比较简单,但是如果要导出一个类的话稍微有些麻烦,该demo主要演示如何从dll中导出一个类。
    首先,定义一个BaseTest抽象类作为TestDll的基类,BaseTest的头文件需要在使用Dll的项目中引用
DLL的项目代码在4中已展示
下面创建一个显示加载该dll的项目DllLoad, pro文件如下:

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = DllLoad
TEMPLATE = app

INCLUDEPATH += include


SOURCES += main.cpp\
        mainwindow.cpp

HEADERS  += mainwindow.h

FORMS    += mainwindow.ui

CONFIG(debug, debug|release){
    DESTDIR = bin/Debug
} else {
    DESTDIR = bin/Debug
}

注意需要在该项目的根目录下创建include目录,并将抽象类BaseTest的头文件basetest.h拷贝到该文件夹下,DllLoad不需要再引用dll的其他头文件
mainwindow.cpp的代码

#include <QApplication>
#include <QLibrary>
#include <QDebug>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "include/basetest.h"

typedef BaseTest* (*Get_Obj)(); //定义函数指针,以备调用
BaseTest *g_obj = NULL;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    loadDll();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::loadDll()
{
    QLibrary testLib("TestDll.dll");
    if (testLib.load()) {
        Get_Obj getObj = (Get_Obj)testLib.resolve("getObj");
        if (getObj) {
            qDebug()<<"getObj";
            g_obj = getObj();
        }
    }
}

void MainWindow::on_pushButton_clicked()
{
    int a = ui->lineEdit->text().toInt();
    int b = ui->lineEdit_2->text().toInt();

    if (g_obj) {
        int sum = g_obj->add(a, b);
        QString str = QString::number(sum);
        ui->label->setText(str);
        ui->label->show();
    }
}

将TestDll.dll拷贝至exe同级目录即可运行
所有代码已上传至:https://download.csdn.net/download/boo12355/11071455
以上参考:
https://blog.csdn.net/yysdsyl/article/details/2626109
编写DLL所学所思(2)——导出类
DLL导出类避免地狱问题的完美解决方案
dll导出类比较好的方式:(https://blog.csdn.net/qiangzi4646/article/details/79628260

引:
https://blog.csdn.net/u012150179/article/details/12346555
https://www.cnblogs.com/weizhixiang/p/6698532.html
https://blog.csdn.net/xuebing1995/article/details/78230626
https://blog.csdn.net/wb175208/article/details/86181626
http://blog.sina.com.cn/s/blog_713542d30102wxxj.html

C++中模块(DLL)对外暴露接口的几种方式:
https://blog.csdn.net/liubing8609/article/details/78906784

上一篇 下一篇

猜你喜欢

热点阅读