从 dll 启动 Qt GUI(在 DLLStart 函数中)

2021-12-09 00:00:00 dll qt c++

我必须从暴露 DLLStartDLLStop 的 dll 启动 Qt GUI.main中正常的(.exe)方式如下:

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

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

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

推荐答案

在 Windows 上工作的一种方法是在单独的 QThread 中启动 QApplication.它不可移植――它不适用于 OS X(我正在研究修复).

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).

但是,您不需要单独的线程.如果您将代码注入到正在运行的应用程序中,则它已经有一个事件循环.你只需要创建一个全局的 QApplication 对象就可以了.事件循环已在运行,因此您无需调用 exec().Qt 的窗口与本机事件循环集成,在这方面一切都很好.

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.

您确实需要调用一次 QCoreApplication::processEvents.它将当前的应用程序实例集成到 windows 事件循环中,就是这样.

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

startup()shutdown() 应该在适当的时间(在进程附加和分离时)调用.

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.

它不可移植,这就是 Qt 文档反对它的原因.它在 Windows 上运行良好.主线程不是魔术――不是在 Windows 上.OS X 上的 Cocoa 在某种程度上很笨拙,显然是不可能的:(.

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 :(.

请注意,如果加载 DLL 的应用程序已经使用 Qt,那么您无需再执行任何操作.确保使用相同的 C++ 编译器编译 DLL,链接到相同的 C++ 运行时,并使用与应用程序使用的二进制兼容的 Qt 版本.这样您就不需要自己的 QApplication 实例.要完成一些有用的工作,请显示一个 Widget 或使用计时器实例化一些 QObjects 以使它们忙碌.您也可以使用 QMetaObject::invokeMethod(obj, "mySlot", Qt::QueuedConnection) 而不是使用计时器:当控制返回到事件循环时将进行调用.

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.

请注意,我在这里有点讽刺:如果您是使用 DLL 的应用程序的作者,则上一段中的条件将可靠地满足.否则――忘记它.

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

相关文章