10

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) { }
Community
  • 1
  • 1
Horst Walter
  • 13,663
  • 32
  • 126
  • 228

1 Answers1

17

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:

static struct Data {
  int argc = 1;
  char *argv[2] = {strdup("dummy"), {}};
  QApplication app{argc, argv};
  MainWindow win;
} *d;

static void startup() {
  d = new Data;
  d->win.show();
  d->app.processEvents();
}

static void shutdown() {
  delete d;
}

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) {}
  ~AppThread() { quit(); wait(); }
}

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

extern "C" void __stdcall DLLStop(void) {
  delete qApp->thread();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Does it really work? http://doc.qt.nokia.com/4.7-snapshot/thread-basics.html#gui-thread-and-worker-thread says that you cannot have widgets in non-primary threades, although I have no idea how this behaves if your main app doesn't define `QApplication`. And what if the main app, or some other lib already uses qt? – j_kubik Jun 15 '12 at 23:33
  • 1
    It's not a problem to have multiple instances of QApplication as long as they are isolated, as will happen if you have multiple threads, one per binary module (DLL or EXE), each having in effect its own copy of Qt. The "main app" is not special in any way, it's just one more binary module in the address space of the running process. If your DLL doesn't link to the same instance of Qt as the EXE does, it'll be OK. Usually your DLL will be a C DLL, and you won't even be guaranteed to link to the same C++ runtime. Heck, you can't even guarantee the EXE and DLL using ABI compatible C++ compilers. – Kuba hasn't forgotten Monica Jun 16 '12 at 04:24
  • I think you should watch this port: http://stackoverflow.com/questions/9777911/how-do-i-create-a-window-in-different-qt-threads – jamk Dec 11 '12 at 09:29
  • dear Ober, I've tried your suggestion, but I did not make it work, :((, may you give me a help? here is my page link: https://stackoverflow.com/questions/54667520/create-qt-ui-from-dllmain-qt-ui-not-show – vg0x00 Feb 13 '19 at 10:12
  • @ReinstateMonica, did you manage to find a solution for OSX? – rkudinov Aug 25 '20 at 22:37
  • There is no such solution. MacOS 10.x does not allow the GUI event loop to run anywhere but in the primary thread. I haven't checked if things got any better in MacOS 12. – Kuba hasn't forgotten Monica Aug 26 '20 at 16:08
  • It seems they did not :( – rkudinov Aug 27 '20 at 11:43