Qt 5.2 DLL Returns 与调用 DLL 的应用程序不同的结果
Qt 5.2 DLL Returns different results than Application calling the DLL
总结:我有一个导出函数的 DLL。在此函数中,我调用 QApplication::allWidgets() ,其中 returns 是一个空列表,而如果此函数在应用程序中,则 returns 是一个包含 19 个项目的列表。
如果我没记错的话,框架维护了一个静态项目列表。 allWidgets() 函数正在返回该列表。据我所知,DLL 应该与应用程序位于同一地址 space,这意味着我应该能够从 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();
}
这是应用程序代码片段:
----------------------------- 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 调用函数指针,它 returns 19 就像应用程序一样。
正如克里斯指出的那样,DLL 和应用程序似乎有两个独立的数据部分。 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();
}
首先,我会避免使用 std::string 作为 DLL 中的 return 值。除非您计划使用相同的编译器(和版本)和 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()));
}
}
总结:我有一个导出函数的 DLL。在此函数中,我调用 QApplication::allWidgets() ,其中 returns 是一个空列表,而如果此函数在应用程序中,则 returns 是一个包含 19 个项目的列表。
如果我没记错的话,框架维护了一个静态项目列表。 allWidgets() 函数正在返回该列表。据我所知,DLL 应该与应用程序位于同一地址 space,这意味着我应该能够从 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();
}
这是应用程序代码片段:
----------------------------- 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 调用函数指针,它 returns 19 就像应用程序一样。
正如克里斯指出的那样,DLL 和应用程序似乎有两个独立的数据部分。 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();
}
首先,我会避免使用 std::string 作为 DLL 中的 return 值。除非您计划使用相同的编译器(和版本)和 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()));
}
}