Horst Walter Horst Walter - 3 months ago 74
C++ Question

Starting Qt GUI from dll (in DLLStart function)

I have to start a Qt GUI from a dll exposing

DLLStart
and
DLLStop
. The normal (.exe) approach in main is as follows:

int main(int argc, char *argv[]) {
QApplication a(argc, argv); Dialog w;
w.show();
return a.exec();
}


The problem is the blocking
a.exec()
call, since in the dll
DLLStart
needs to return immediately (see below). Any workaround for this? Remark: The question is sharing some common ground with " Adding a QT GUI to a Dynamic Library ", but it is no exact duplicate.

/** start module */
int __stdcall DLLStart(void) {
..
QApplication qaDll(ac, av); Dialog w;
w.show();
qaDll.exec();
return 0; // never reached
}

/** stop module */
void __stdcall DLLStop(void) { }

Answer

One way works on Windows is to start QApplication in a separate QThread. It's not portable -- it doesn't work on OS X (I'm researching a fix).

But, you don't need a separate thread. If you inject your code into a running application, it already has an event loop. You only need to create a global QApplication object and you're done. The event loop is already running, so you don't need to call exec(). Qt's windows integrate with the native event loop, and everything is good on that front.

You do need to call QCoreApplication::processEvents once. It will integrate the current application instance into the windows event loop, and that's it.

Thus, your startup code could look as follows:

int * argc = nullptr;
char ** argv = nullptr;
QApplication * app = nullptr;
MainWindow * win = nullptr;

static void startup() {
  argc = new int(1);
  argv = new char*[1];
  argv[0] = strdup("dummy");
  auto app = new QApplication(*argc, argv);
  auto w = new MainWindow;
  w->show();
  app->processEvents();
}

static void shutdown()
{
  delete win;
  delete app;
  free argv[0];
  delete [] argv;
  delete argc;
}

The startup() and shutdown() should be called at appropriate times (on process attach and detach).


Old answer follows. This is not completely up to date anymore.

A short example is below, for a complete self-contained example see my other answer.

It is not portable and that's why Qt documentation advises against it. It works just fine on Windows. The main thread is not magic -- not on Windows. Cocoa on OS X is clumsy in a way and makes it impossible, apparently :(.

Note that if the application that loads the DLL already uses Qt, then there's nothing further for you to do. Ensure you compile your DLL with the same C++ compiler, link against the same C++ runtime, and use a version of Qt that's binary compatible with the one used by application. You then don't need your own instance of QApplication. To get some useful work done, show a Widget or instantiate some QObjects with timers that will get them busy. You can also use QMetaObject::invokeMethod(obj, "mySlot", Qt::QueuedConnection) instead of using timers: the call will be made when control returns to the event loop.

If that's not possible, then the below is your only option. Works just fine, as far as I can tell.

Note that I'm a bit sarcastic here: The conditions in the previous paragraph will be met reliably maybe if you are the author of the application that uses the DLL. Otherwise -- forget about it.

class AppThread : public QThread {
  int & argc;
  char ** argv;
  int result;
  void run() {
    QApplication a(argc, argv);
    Dialog d;
    d.show();
    result = a.exec();
  }
public:
  AppThread(int & argc_, char ** argv_) : argc(argc_), argv(argv_) {}
}

int __stdcall DLLStart(void) {
  AppThread * thread = new AppThread(argc, argv);
  thread->start();
  return 0;
}

void __stdcall DLLStop(void) {
  qApp->thread()->quit();
  qApp->thread()->wait();  
}
Comments