10

我必须从公开的 dllDLLStartDLLStop. main中的正常(.exe)方法如下:

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

问题是阻塞a.exec()调用,因为在 dll 中DLLStart需要立即返回(见下文)。有什么解决方法吗?备注:这个问题与“将 Qt GUI 添加到动态库”有一些共同点,但它并不完全重复。

/** 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) { }
4

1 回答 1

16

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();
}
于 2012-06-15T19:02:18.447 回答