1

Summary : I have a DLL which exports a function. In this function I am calling QApplication::allWidgets() which returns an empty list whereas if this function is in the application returns a list with 19 items.

If I am not mistaken there is a static list of items maintained by the framework. allWidgets() function is returning that list. To my knowledge DLL is supposed to be in the same address space as the application which means I should be able to access all the static variables from DLL. Then why does it make a difference whether the function is in DLL vs application?

Here is DLL code :

------------------------ 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();
}

Here is the application code snippet :

------------------------------ 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()));
    }
}

When I click the button results are :

Number of widgets from DLL 0

Number of widgets from Applicaton 19


I am using Qt 5.2 Mingw 64 Bit.

Update : Here is a workaround that appears to be working even though my question is still valid. If I pass a function pointer to the DLL and call the function pointer from DLL it returns 19 just like application.

As Chris pointed out DLL and Application appears to have two separate Data sections. DLL cannot normally access to the application's data section (which contains state information of the GUI) except through an interface to the data or a pointer to it. In my case this was not possible since my interface is generic. I tried to push this a bit by using GetProcAddress inside my DLL but I did not have much success but I think in theory this should work.

My current design is something like this :

------- 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
} 

I think I have to change my design in the following way to give each environment its own interface.

------- 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();
}
Lenz
  • 467
  • 3
  • 10
  • The version that is linked in the DLL is linked against code that accesses the QWidgetPrivate::allWidgets variable that is never instantiated (unless you create a QApplication inside the DLL), however, the function pointer that is passed in is linked against the QWidgetPrivate::allWidgets variable that is allocated when you create your QApplication. – Chris Mar 27 '15 at 19:07
  • I am not sure if I understand you. Are you suggesting a solution? Or you are saying that it would not work? If your solution is to instantiate a new QApplication, then I don't see how that would work, Since this would give me a new Widget map which would not contain the widget in which I am interested. – Lenz Mar 27 '15 at 20:55
  • I am basically saying that the direction that you are going isn't going to work. I've edited my solution to how I would go about solving this problem. – Chris Mar 27 '15 at 21:26
  • I think you are right to a large degree. I think as far as Qt5.2 build I am using, there appears to be two separate data sections. Dll and Application. But I think there is more to this story since I am playing around with Qt 4.7 not and I am in the DLL and my allWidgets is returning the same number as it does in application. According to your account this is impossible. My Qt 4.7 build is dynamic and Qt 5.2 is static. That's the only significant change. Any ideas what may be happening? – Lenz Mar 30 '15 at 02:00
  • I compiled Qt5.2 dynamically. There are still two data sections. So static or dynamic build does not appear to cause of qt4.7 vs qt5.2 discrepancy. – Lenz Mar 30 '15 at 02:39

1 Answers1

1

Firstly, I would avoid using std::string as a return value from the DLL. Unless you plan on using the same compiler (and version) and stl, there is a good chance that you won't be able to properly read the string.

Secondly, the problem is that QApplication::allWidgets accesses a private list. allWidgets is implemented like this:

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

Since you are calling it from a DLL, QWidgetPrivate::allWidgets hasn't been created (You didn't create a new QApplication inside the DLL). A good explanation as to why the QApplication in your application isn't shared with the DLL is what happens to global and static variables in a shared library.

These are the problems. Since it seems that you aren't really concerned about whether you are using different versions of the libraries and different compilers, then you could just pass the QWidgetList to the 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()));
    }
}
Community
  • 1
  • 1
Chris
  • 269
  • 2
  • 8
  • QApplication::allWidgets() is static function which means it does not matter if I create a new QApplication there will only one copy of the function. QWidgetPrivate::allWidgets is a global pointer. Moreover, this works with QT 4.7 – Lenz Mar 27 '15 at 20:13
  • Yes the QWP::allWidgets method is static, however, there is a QWP::allWidgets variable. When the test.cpp translation unit is compiled, it references that variable. When linked into the DLL it has that variable. When you compile and link your mainwindow.cpp translation unit your executable has a copy of that variable. When the application runs and executes ths QApplication constructor, the executables version of QWP::allWidgets variable is created, while the QWidgetPrivate::allWidgets variable that was compiled into the DLL is not constructed. – Chris Mar 27 '15 at 21:00
  • I think you are confusing dynamic library with static library. DLL is not compiled or linked with variables of the calling process. DLL is loaded into the calling process's address space. I believe they share the same Data region thus global variables. I think you are contending that DLL has its own Data region thus its own set of global variables. – Lenz Mar 27 '15 at 21:31
  • That is what I am contending, because a DLL does have it's own data region. The "only" different between a DLL and an EXE is that the EXE has a main() function. A dll's functions are relocated into the executable's address space but unless the variables in the DLL are exported they are not available in the calling space (on Windows). A global variable in the application is not accessible to the DLL. http://stackoverflow.com/questions/4451920/access-a-global-variable-in-a-dll – Chris Mar 27 '15 at 21:32
  • If I could change the interface I would have it long time ago. So obvious solution is not possible. I am using the same interface for Qt, Borland, Visual Studio. So I can't use specific stuff like QWidgetList etc. – Lenz Mar 30 '15 at 02:06
  • Not certain of your interface, but could you use a void *, and cast it to QWidgetList if you are compiling with Qt? – Chris Mar 30 '15 at 12:54
  • I thought about those sorts of things as well but they are just too irksome to my taste. I am still trying to understand how and why qt 4.7 appears to be working as if there is a single data section. – Lenz Mar 30 '15 at 18:04
  • Btw, Thank you for your responses. – Lenz Mar 30 '15 at 18:14
  • The other thing (besides a void*) is a typedef that is #ifdef'ed to the appropriate type, ie. #ifdef QT typedef QWidgetList Widgets #elif VC typedef CWidgetList Widgets #elif BORLAND typedef BWidgetList Widgets #endif and use the Widgets type in your code. – Chris Mar 31 '15 at 01:40
  • In fact, this is what I did to fix it. – Lenz Mar 31 '15 at 01:48