在 Qt 中访问 DLL 的 C++ 类成员函数

Posted

技术标签:

【中文标题】在 Qt 中访问 DLL 的 C++ 类成员函数【英文标题】:Accessing C++ class member functions of a DLL in Qt 【发布时间】:2017-05-04 12:11:31 【问题描述】:

我能够在 Qt main.cpp 中创建类的实例,但我的应用程序在尝试访问 DLL 的类成员函数时崩溃。

我该怎么做?

如果这是我的 dll.h

 #ifndef DIVFIXTURE_H
 #define DIVFIXTURE_H

#include<QObject>
#include<QVariant>

class __declspec(dllexport) DivFixture : public QObject

    Q_OBJECT
public:
    Q_INVOKABLE DivFixture();
    Q_INVOKABLE void setNumerator(QVariant num);
    Q_INVOKABLE void setDenominator(QVariant denom);
    Q_INVOKABLE QVariant quotient();

private:
    double numerator, denominator;
;

#endif

这是我的 dll.cpp

#include "testfixture.h"

DivFixture::DivFixture()

void DivFixture::setNumerator(QVariant num)

    numerator=num.toDouble();


void DivFixture::setDenominator(QVariant denom)

    denominator=denom.toDouble();


QVariant DivFixture::quotient()

    QVariant ret;
    ret=numerator/denominator;
    return ret;


//non-class function to return pointer to class
extern "C" __declspec(dllexport) DivFixture* create()

     return new DivFixture();

这是我导入 DLL 库的 Qt 应用程序的 main.cpp

QLibrary library(("C:\\somepath\\testFixture.dll");
if (!library.load())
    qDebug() << library.errorString() << endl;
if (library.load())
    qDebug() << "library loaded" << endl;
DivFixture  *r1=NULL;

typedef DivFixture* (*MyPrototype)();

auto myFunction = (MyPrototype)library.resolve("create");
qDebug()<<myFunction;
if (myFunction)
    r1=Function(); // able to create instance of DivFixture
    r1->quotient(); //I'm not able to access class member function

为什么我不能使用类实例访问类成员函数quotient()?程序在这一行崩溃r1-&gt;quotient();。我可以创建我的类DivFixture 的原型来访问其成员函数,而不是像typedef DivFixture* (*MyPrototype)(); 这样在DLL 中创建函数原型吗?

【问题讨论】:

请提供您的代码,最好是minimal reproducible example。 我已经编辑了我的问题,提供了代码 sn-p。 【参考方案1】:

注意:起初它并没有为我编译(提供的代码不完整),所以我不得不进行一些更改,希望我保留了您想要的功能。

首先,一些关于创建/使用 DLL 的 cmets:

    __declspec(dllexport) 仅在您要导入符号时才需要,与 create 一样。当您从 DLL 中创建对象时,不必导出类本身。 如果您以您的方式定义您的类,编译器也会等待定义,该定义恰好位于 DLL 中(并且该 DLL 未提供带有定义表的 .lib)。这意味着代码不会编译。 要解决此问题,您可以: 您也导出类,编译器会生成一个 .lib 文件(至少在 VS 中)并将您的应用程序与它链接起来,这意味着不需要所有 QLibrary 代码。 您将您的类转换为纯虚拟类(此处仅需要公共成员),将其派生到 DLL 的内部类(实现类)并使用 create 作为工厂(它创建了一个实现类的实例)。

由于您似乎打算使用QLibrary,也许是为了制作类似插件的功能,我向您展示了第二种解决方案,如果您更喜欢另一种,请告诉我,我会扩展回答以涵盖这一点。

基于纯虚类的解决方案

dll.h,公共头文件,暴露类的设计

#ifndef DIVFIXTURE_H
#define DIVFIXTURE_H

#include<QObject>
#include<QVariant>

class DivFixture : public QObject 
public:
  virtual ~DivFixture() 
  virtual Q_INVOKABLE void setNumerator(QVariant num) = 0;
  virtual Q_INVOKABLE void setDenominator(QVariant denom) = 0;
  virtual Q_INVOKABLE QVariant quotient() = 0;
;

#endif

dll_impl.h,不打算在 DLL 之外使用,存储实际

#ifndef DIVFIXTURE_IMPL_H
#define DIVFIXTURE_IMPL_H

#include "dll.h"

class DivFixture_Impl : public DivFixture 
public:
  DivFixture_Impl();
  virtual ~DivFixture_Impl() 

  virtual Q_INVOKABLE void setNumerator(QVariant num) override;
  virtual Q_INVOKABLE void setDenominator(QVariant denom) override;
  virtual Q_INVOKABLE QVariant quotient() override;

protected:
  double numerator, denominator;
;

#endif

dll.cpp,实际实现

#include "dll_impl.h"

DivFixture_Impl::DivFixture_Impl() : numerator(0.0), denominator(1.0) 

void DivFixture_Impl::setNumerator(QVariant num)

  numerator = num.toDouble();


void DivFixture_Impl::setDenominator(QVariant denom)

  denominator = denom.toDouble();
    

QVariant DivFixture_Impl::quotient()

  QVariant ret;
  ret = numerator / denominator;
  return ret;


// non-class function to return pointer to class
extern "C" __declspec(dllexport) DivFixture* create()

  return new DivFixture_Impl();

main.cpp

#include <QtCore>
#include "../dll/dll.h"

int main()

  QLibrary library("dll.dll");
  if (!library.load()) 
    qDebug() << library.errorString();
    return 0;
  

  qDebug() << "library loaded";

  typedef DivFixture * (*MyPrototype)();
  auto myFunction = (MyPrototype)library.resolve("create");
  qDebug() << myFunction;

  DivFixture* r1 = nullptr;
  if (myFunction)
    r1 = myFunction();

  if (r1 != nullptr) 
    r1->setDenominator(2.0);
    r1->setNumerator(10.0);
    auto r = r1->quotient();
    qDebug() << r.toDouble();
   else 
    qDebug() << "r1 not created!";
  

  return 0;

补充说明

关于crash,下面代码的第三行不管myFunction是否为null都会被执行,所以crash实际上可能是因为r1没有初始化。

if (myFunction)
    r1=myFunction();
    r1->quotient();

注意缩进和实际代码块之间的不一致。在这里使用大括号:

if (myFunction) 
    r1=myFunction();
    r1->quotient();

或者在使用之前检查r1 不为空:

if (myFunction)
    r1=myFunction();
if (r1 != nullptr)
    r1->quotient();

另外,这可能只是一个错字,但您检查了if (myFunction),但在下一行调用Function 而不是myFunction。头文件名也是如此。发布代码时要小心这些事情,因为它们很难重现您的问题,因此您可能不会得到答案或适当的答案。

最后,作为一个小说明,除非你想插入一个额外的空行,qDebug 已经在末尾添加了一个换行符,所以没有必要使用endl

【讨论】:

很高兴知道它很有用 当我试图访问正在导入此 DLL 的 applicationMain 中 DLL 的类成员函数时,您的解决方案对我很有用。但是现在,问题是我无法访问 DLL 的 QML 中暴露的 C++ 函数。例如,我正在访问 DLL 的 main.qml 中的 setDenominator 函数。但它抛出错误“TypeError:对象QObject(0xabe2f0)的属性'setDenominator'不是函数” @Rachitha,不幸的是我对 QML 不是很流利,而且这个解决方案可能与 QML 不完全兼容,而只能与 C++ 兼容。我建议您发布一个新问题,描述当前的解决方案(您可以链接到此问题/答案以避免再次解释所有内容),然后是 QML 的问题。通过这种方式,SO 中的其他人将很乐意为您提供帮助。 我发布了一个新问题,描述了当前的解决方案,然后是您提到的 QML 问题。谢谢您的建议。 @cbuchart

以上是关于在 Qt 中访问 DLL 的 C++ 类成员函数的主要内容,如果未能解决你的问题,请参考以下文章

编写从 C++ 应用程序链接的 Delphi DLL:访问 C++ 接口成员函数会导致访问冲突

Qt DLL总结-VS2008+Qt 使用QPluginLoader访问DLL

在 C++ DLL 的成员函数中返回自身的类

Qt Dll总结——创建及使用Qt的Dll(转载)

C# 我定义了一个静态类编译成DLL后,在另一个项目中引用这个DLL,但访问不了里面的成员

从 dll 访问类的静态成员