問題描述
我必須從暴露 DLLStart
和 DLLStop
的 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()
調(diào)用,因為在 dll 中 DLLStart
需要立即返回(見下文).有什么解決方法嗎?備注:該問題與將 Qt GUI 添加到一個動態(tài)庫 ",但它不是完全重復(fù)的.
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(我正在研究修復(fù)).
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).
但是,您不需要單獨的線程.如果您將代碼注入到正在運行的應(yīng)用程序中,則它已經(jīng)有一個事件循環(huán).你只需要創(chuàng)建一個全局的 QApplication
對象就可以了.事件循環(huán)已在運行,因此您無需調(diào)用 exec()
.Qt 的窗口與本機事件循環(huán)集成,在這方面一切都很好.
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.
您確實需要調(diào)用一次 QCoreApplication::processEvents
.它將當(dāng)前的應(yīng)用程序?qū)嵗傻?windows 事件循環(huán)中,就是這樣.
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()
應(yīng)該在適當(dāng)?shù)臅r間(在進(jìn)程附加和分離時)調(diào)用.
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 上運行良好.主線程不是魔術(shù)——不是在 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 的應(yīng)用程序已經(jīng)使用 Qt,那么您無需再執(zhí)行任何操作.確保使用相同的 C++ 編譯器編譯 DLL,鏈接到相同的 C++ 運行時,并使用與應(yīng)用程序使用的二進(jìn)制兼容的 Qt 版本.這樣您就不需要自己的 QApplication
實例.要完成一些有用的工作,請顯示一個 Widget 或使用計時器實例化一些 QObjects
以使它們忙碌.您也可以使用 QMetaObject::invokeMethod(obj, "mySlot", Qt::QueuedConnection)
而不是使用計時器:當(dāng)控制返回到事件循環(huán)時將進(jìn)行調(diào)用.
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.
如果這是不可能的,那么下面是您唯一的選擇.據(jù)我所知,效果很好.
If that's not possible, then the below is your only option. Works just fine, as far as I can tell.
請注意,我在這里有點諷刺:如果您是使用 DLL 的應(yīng)用程序的作者,則上一段中的條件將可靠地滿足.否則——忘記它.
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();
}
這篇關(guān)于從 dll 啟動 Qt GUI(在 DLLStart 函數(shù)中)的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!