在非 Qt 应用程序中使用基于 Qt 的 DLL
我做对了吗?
我的一个客户有一个小组,我在那里开发基于 Qt 的客户端-服务器内容,其中包含许多有趣的小部件和套接字.
A client of mine has a group where I'm developing Qt-based client-server stuff with a lot of fun widget stuff and sockets.
公司内的另一个小组想要使用基于 QTcpSocket 的客户端数据提供程序类的包装版本.(基本上就是它听起来的样子,从服务器向客户端显示提供数据)
Another group within the company wants to use a wrapped version of the QTcpSocket-based client data provider classes. (Which does basically what it sounds like, provides data from the server to the client displays)
但是,该小组有一个主要使用 MFC 构建的庞大应用程序,而且这种情况在短期内不会改变.基于 Qt 的 DLL 也是延迟加载的,因此在某些配置中可以在没有此功能的情况下部署它.
However, that group has a huge application built mostly with MFC, and that is simply not going to change any time soon. The Qt-based DLL is also delay-loading so that it can be deployed without this feature in certain configurations.
我已经让它工作了,但它有点hacky.这是我目前的解决方案:
I've got it working, but it's a little hacky. Here's my solution at the moment:
DLL 包装类构造函数调用 QCoreApplication::instance() 来查看它是否为 NULL.如果它为 NULL,则假定它在非 Qt 应用程序中,并创建它自己的 QCoreApplication 实例:
The DLL wrapper class constructor calls QCoreApplication::instance() to see if it's NULL or not. If it's NULL, it assumes it's in a non-Qt app, and creates a QCoreApplication instance of it's own:
if (QCoreApplication::instance() == NULL)
{
int argc = 1;
char* argv[] = { "dummy.exe", NULL };
d->_app = new QCoreApplication(argc, argv); // safe?
}
else
d->_app = NULL;
然后它会设置一个windows定时器来偶尔调用processEvents():
It then will set up a windows timer to occasionally call processEvents():
if (eventTimerInterval > 0)
{
// STATE: start a timer to occasionally process the Qt events in the event queue
SetTimer(NULL, (UINT_PTR)this, eventTimerInterval, CDatabaseLayer_TimerCallback);
}
回调只是使用 timerID 作为指向类实例的指针来调用 processEvents() 函数.SetTimer() 文档说,当 HWND 为 NULL 时,它会忽略 timerID,因此这似乎是完全有效的.
The callback simply calls the processEvents() function using the timerID as a pointer to the class instance. The SetTimer() docs say when HWND is NULL it ignores the timerID, so this appears to be perfectly valid.
VOID CALLBACK BLAHBLAH_TimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
((BLAHBLAH*)idEvent)->processEvents(); // basically just calls d->_app->processEvents();
}
然后我销毁 QCoreApplication 实例作为析构函数中的最后一件事.
I then destroy the QCoreApplication instance as the very last thing in the destructor.
BLAHBLAH::~BLAHBLAH()
{
.. other stuff
QCoreApplication* app = d->_app;
d->_app = NULL;
delete d;
if (app != NULL)
delete app;
}
如果托管应用程序希望自己对 processEvents() 的调用计时,它可以为 eventTimerInterval 传递 0 并调用 BLAHBLAH::processEvents() 本身.
If the hosting application wishes to time the calls to processEvents() itself, it can pass 0 in for eventTimerInterval and call BLAHBLAH::processEvents() itself.
对此有什么想法吗?将该应用程序移植到 Qt 不是一种选择.这不是我们的.
Any thoughts on this? Porting that app to Qt is not an option. It's not ours.
它似乎有效,但这里可能有几个假设被打破.我可以用这样的虚拟参数构造一个 QCoreApplication 吗?事件队列以这种方式运行是否安全?
It appears to work, but there are probably several assumptions being broken here. Can I just construct a QCoreApplication with dummy arguments like that? Is the event queue safe to operate in this manner?
我不想以后这件事在我脸上炸开.想法?
I don't want this blowing up in my face later. Thoughts?
推荐答案
研究 Qt 代码,似乎需要 QCoreApplication 来调度系统范围的消息,例如计时器事件.诸如信号/插槽甚至 QThread 之类的东西都不依赖于它,除非它们与那些系统范围的消息相关.这是我在共享库中执行此操作的方法(以跨平台方式使用 Qt 本身)并且我实际上确实调用了 exec,因为 processEvents() 本身并不能处理所有内容.
Studying the Qt code it seems QCoreApplication is needed to dispatch system-wide messages such as timer events. Things like signal/slots and even QThreads do not depend on it unless they are related to those system-wide messages. Here is how I do this in a shared library (in a cross platform way using Qt itself) and I actually do call exec, because processEvents() alone does not process everything.
我有一个全局命名空间:
I have a global namespace:
// Private Qt application
namespace QAppPriv
{
static int argc = 1;
static char * argv[] = {"sharedlib.app", NULL};
static QCoreApplication * pApp = NULL;
static QThread * pThread = NULL;
};
我在 QObject 中有一个 OpenApp 方法(即 moc'ed),如下所示:
I have an OpenApp method in a QObject (that is moc'ed) like this:
// Initialize the app
if (QAppPriv::pThread == NULL)
{
// Separate thread for application thread
QAppPriv::pThread = new QThread();
// Direct connection is mandatory
connect(QAppPriv::pThread, SIGNAL(started()), this, SLOT(OnExec()), Qt::DirectConnection);
QAppPriv::pThread->start();
}
这里是 OnExec 插槽:
if (QCoreApplication::instance() == NULL)
{
QAppPriv::pApp = new QCoreApplication(QAppPriv::argc, QAppPriv::argv);
QAppPriv::pApp->exec();
if (QAppPriv::pApp)
delete QAppPriv::pApp;
}
到目前为止它似乎工作正常,我不确定最后是否需要删除该应用程序,如果我发现了什么我会更新我的答案.
So far it seems to be working fine, I am not sure if I need to delete the app at the end, I will update my answer if I find something.
相关文章