【问题标题】:Qt 5.2 DLL Returns different results than Application calling the DLLQt 5.2 DLL 返回与调用 DLL 的应用程序不同的结果
【发布时间】:2015-03-27 04:46:25
【问题描述】:

总结:我有一个导出函数的 DLL。在这个函数中,我调用 QApplication::allWidgets() ,它返回一个空列表,而如果这个函数在应用程序中,则返回一个包含 19 个项目的列表。

如果我没记错的话,有一个由框架维护的静态项目列表。 allWidgets() 函数正在返回该列表。据我所知,DLL 应该与应用程序位于同一地址空间,这意味着我应该能够从 DLL 访问所有静态变量。那为什么函数在 DLL 和应用程序中会有所不同呢?

这是 DLL 代码:

------------------------ test.h ----

extern "C"  TESTSHARED_EXPORT std::string DllFun();

------------ test.cpp --------------

std::string DllFun()
{
   std::stringstream temp;
   QList<QWidget *> list = QApplication::allWidgets();
   temp << "Number of widgets from DLL " << list.size();
   return temp.str();
}

这里是应用代码sn-p:

------------------ mainwindow.cpp -------------- --

std::string MainWindow::FunInternal()
{
    std::stringstream temp;
    QList<QWidget *> list = QApplication::allWidgets();
    temp << "Number of widgets from Applicaton " << list.size();
    return temp.str();
}

void MainWindow::on_pushButton_clicked()
{    
    typedef std::string (*FunType)();
    HMODULE handle = LoadLibrary(L"Test.dll");
    FunType DllFun = (FunType)GetProcAddress(handle, "DllFun");

    if (Fun)
    {
       std::string DllWidgets      = DllFun();
       std::string InternalWidgets = FunInternal();
       ui->textEdit->append(QString(DllWidgets.c_str()));
       ui->textEdit->append(QString("\n"));
       ui->textEdit->append(QString(InternalWidgets.c_str()));
    }
}

当我点击按钮结果是:

来自 DLL 0 的小部件数量

Applicaton 19 中的小部件数量


我正在使用 Qt 5.2 Mingw 64 位。

更新:即使我的问题仍然有效,这是一个似乎有效的解决方法。如果我将函数指针传递给 DLL 并从 DLL 调用函数指针,它会返回 19 ,就像应用程序一样。

正如 Chris 所指出的,DLL 和 Application 似乎有两个独立的数据部分。 DLL 不能正常访问应用程序的数据部分(其中包含 GUI 的状态信息),除非通过数据接口或指向它的指针。就我而言,这是不可能的,因为我的界面是通用的。我试图通过在我的 DLL 中使用 GetProcAddress 来推动这一点,但我没有取得太大的成功,但我认为理论上这应该可行。

我目前的设计是这样的:

------- genwidget.h
class MyWidget
{
public:
    void modifyWidget(HWND id);
}
------- qtwidget.cpp
#include <genwidget.h>
void MyWidget::modifyWidget(HWND id)
{
// find Panel widget
QWidget *widget = find_widget_from_list(id, QApplication::allWidgets())
// do work
} 
------- vcwidget.cpp
#include <genwidget.h>
void MyWidget::modifyWidget(HWND id)
{
// find Panel widget
Panel ^widget = find_widget_from_list(id);
// do work
} 

我认为我必须通过以下方式更改我的设计,以便为每个环境提供自己的界面。

------- genwidget.h
class MyWidget
{
public:
    virtual void modifyWidget() = 0;
}
------- qtwidget.h
class QtMyWidget : public MyWidget
{
 public:
    QtMyWidget(QWidget *panel);
    void modifyWidget();
}
------- vcwidget.h
class VcMyWidget : public MyWidget
{
public:
    VcMyWidget(Panel ^panel);
    void modifyWidget();
}

【问题讨论】:

  • 在 DLL 中链接的版本与访问从未实例化的 QWidgetPrivate::allWidgets 变量的代码链接(除非您在 DLL 中创建 QApplication),但是,函数指针是传入的链接与创建 QApplication 时分配的 QWidgetPrivate::allWidgets 变量相关联。
  • 我不确定我是否理解你。您是在建议解决方案吗?或者你是说它不起作用?如果您的解决方案是实例化一个新的 QApplication,那么我看不出它是如何工作的,因为这会给我一个新的 Widget 映射,其中不包含我感兴趣的小部件。
  • 我基本上是在说你前进的方向是行不通的。我已经编辑了我将如何解决这个问题的解决方案。
  • 我认为你在很大程度上是对的。我认为就我正在使用的 Qt5.2 构建而言,似乎有两个单独的数据部分。 DLL 和应用程序。但我认为这个故事还有更多内容,因为我不是在玩 Qt 4.7,而且我在 DLL 中,我的 allWidgets 返回的数字与它在应用程序中的数字相同。根据你的说法,这是不可能的。我的 Qt 4.7 构建是动态的,而 Qt 5.2 是静态的。这是唯一的重大变化。任何想法可能会发生什么?
  • 我动态编译了Qt5.2。还有两个数据部分。因此静态或动态构建似乎不会导致 qt4.7 与 qt5.2 的差异。

标签: c++ qt dll


【解决方案1】:

首先,我会避免使用 std::string 作为 DLL 的返回值。除非您打算使用相同的编译器(和版本)和 stl,否则您很有可能无法正确读取字符串。

其次,问题在于QApplication::allWidgets 访问的是私有列表。 allWidgets 是这样实现的:

QWidgetList QApplication::allWidgets()
{
    if (QWidgetPrivate::allWidgets)
        return QWidgetPrivate::allWidgets->toList();
    return QWidgetList();
}

由于您是从 DLL 调用它,因此尚未创建 QWidgetPrivate::allWidgets(您没有在 DLL 中创建新的 QApplication)。关于为什么您的应用程序中的 QApplication 不与 DLL 共享的一个很好的解释是 what happens to global and static variables in a shared library.

这些都是问题。由于您似乎并不真正关心您是否使用不同版本的库和不同的编译器,那么您可以将 QWidgetList 传递给 DLL。

// In test.h
extern "C"  TESTSHARED_EXPORT std::string DllFun(QWidgetList *list);

// In test.cpp
std::string DllFun(QWidgetList *list)
{
    std::stringstream temp;
    temp << "Number of widgets from DLL " << list.size();
    return temp.str();
}

// mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{    
    typedef std::string (*FunType)();
    HMODULE handle = LoadLibrary(L"Test.dll");
    FunType DllFun = (FunType)GetProcAddress(handle, "DllFun");

    if (Fun)
    {
       QWidgetList all = QApplication::allWidgets();
       std::string DllWidgets      = DllFun(&all);
       std::string InternalWidgets = FunInternal();
       ui->textEdit->append(QString(DllWidgets.c_str()));
       ui->textEdit->append(QString("\n"));
       ui->textEdit->append(QString(InternalWidgets.c_str()));
    }
}

【讨论】:

  • QApplication::allWidgets() 是静态函数,这意味着如果我创建一个新的 QApplication 并不重要,只有一个函数副本。 QWidgetPrivate::allWidgets 是一个全局指针。此外,这适用于 QT 4.7
  • 是的,QWP::allWidgets 方法是静态的,但是,有一个 QWP::allWidgets 变量。编译 test.cpp 翻译单元时,它会引用该变量。当链接到 DLL 时,它具有该变量。当您编译并链接您的 mainwindow.cpp 翻译单元时,您的可执行文件具有该变量的副本。当应用程序运行并执行这个 QApplication 构造函数时,会创建 QWP::allWidgets 变量的可执行版本,而不会构造编译到 DLL 中的 QWidgetPrivate::allWidgets 变量。
  • 我认为您将动态库与静态库混淆了。 DLL 不编译或与调用进程的变量链接。 DLL 被加载到调用进程的地址空间中。我相信它们共享相同的数据区域,因此是全局变量。我认为您在争辩说 DLL 有自己的数据区域,因此有自己的一组全局变量。
  • 这就是我所争论的,因为 DLL 确实有它自己的数据区域。 DLL 和 EXE 之间的“唯一”不同是 EXE 有一个 main() 函数。 dll 的函数被重新定位到可执行文件的地址空间,但除非 DLL 中的变量被导出,否则它们在调用空间中不可用(在 Windows 上)。 DLL 无法访问应用程序中的全局变量。 stackoverflow.com/questions/4451920/…
  • 如果我可以改变界面,我早就拥有了。所以明显的解决方案是不可能的。我对 Qt、Borland、Visual Studio 使用相同的界面。所以我不能使用 QWidgetList 等特定的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 2015-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多