在非Qt应用程序中使用基于Qt的DLL

29

我是否做得正确?

我的一个客户有一个小组,我正在开发基于Qt的客户端服务器系统,其中包含很多有趣的小部件和套接字。

公司内另一个小组想要使用封装后的QTcpSocket-based客户端数据提供程序类。(它基本上是从服务器向客户端显示器提供数据)

然而,该小组有一个使用MFC构建的巨大应用程序,并且这种情况不会很快改变。Qt-based DLL也是延迟加载的,以便可以在某些配置中部署而不需要此功能。

我已经让它工作了,但它有点hack。目前我的解决方案如下:

DLL包装类构造函数调用QCoreApplication::instance()来查看它是否为NULL。如果是,则假定它在非Qt应用程序中,并创建自己的QCoreApplication实例:

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

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,因此这似乎是完全有效的。

VOID CALLBACK BLAHBLAH_TimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    ((BLAHBLAH*)idEvent)->processEvents(); // basically just calls d->_app->processEvents();
}

在析构函数中,我会将 QCoreApplication 实例作为最后一个被销毁的对象。

BLAHBLAH::~BLAHBLAH()
{
    .. other stuff

   QCoreApplication* app = d->_app;
   d->_app = NULL;
   delete d;
   if (app != NULL)
       delete app;
}

如果托管应用程序希望自己定时调用 processEvents(),则可以将 eventTimerInterval 设为 0 并自己调用 BLAHBLAH::processEvents()。

你对此有什么想法?将该应用程序移植到 Qt 不是一种选择。 因为它不是我们的应用程序。

看起来它能工作,但可能违反了几个假设。 我可以像那样使用虚构的参数构造 QCoreApplication 吗?以这种方式操作事件队列是否安全?

我不想以后遇到问题。 有什么想法吗?


我不得不自己动手了。Qt 3 显然支持创建插件 DLL,例如 Chrome、Safari 和基于 Mozilla 的浏览器的 npapi 类型插件。但是这似乎已经从 Qt 4 中删除了。 - Chris Becke
3
谢谢提问!你知道现在在4.8中是否这是最佳途径,或者有更好的方法吗?实际上,我运行一个单独的QThread,并在那里创建和执行QCoreApplication(全局argc和argv)。 QThread本身不需要QCoreApplication,只有一些系统级别的消息,例如计时器需要调度程序。 - dashesy
2个回答

11

研究Qt代码后,似乎需要QCoreApplication来分发系统范围的消息,例如计时器事件。诸如信号/槽甚至QThreads也不依赖于它,除非它们与那些系统范围的消息相关。以下是我在共享库中以跨平台方式(使用Qt本身)执行此操作的方法,我实际上调用exec,因为仅使用processEvents()无法处理所有内容。

我有一个全局命名空间:

// Private Qt application
namespace QAppPriv
{
    static int argc = 1;
    static char * argv[] = {"sharedlib.app", NULL};
    static QCoreApplication * pApp = NULL;
    static QThread * pThread = NULL;
};

我在一个 moc 过的 QObject 中有一个 OpenApp 方法,如下所示:

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

目前看起来一切正常,我不确定是否需要在最后删除应用程序,如果我发现了什么,我会更新我的答案。


@ExpatEgghead 它实际上工作得非常好(https://github.com/dashesy/CereLink) - dashesy
你需要使用Qt::BlockingQueuedConnection。这将会阻塞直到槽在另一个线程中运行。当信号和槽存在于同一线程中时,应该使用Qt::DirectConnection。我认为在这里使用Qt::DirectConnection会导致系统回退到Qt::QueuedConnection,这就解释了为什么我的槽被异步调用的原因。 - user2836797
@It'sYourAppLLC 不,你错了。根据Qt的文档,信号started是从关联线程发出的,因此DirectConnection是正确的选择,因为OnExec函数需要在关联线程中运行。 - Jimmy Chen

1

Qt 4.5.2的文档指出,QCoreApplication的参数需要具有与应用程序对象一样长的生命周期 - 因此,您不应该使用局部变量。

除了这个小问题:

我正在处理同样的问题,而且对我来说似乎一切都很正常。然而,我建议在卸载/退出时非常小心,因为如果您正在使用另一个应用程序的事件循环,并且在卸载您的库之前停止了该事件循环,则在尝试关闭()套接字和删除QObjects时可能会发生各种崩溃性问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接