Использование библиотеки на основе Qt в не-Qt-приложении
Я делаю это правильно?
У моего клиента есть группа, в которой я занимаюсь разработкой клиент-серверных приложений на основе Qt с множеством забавных виджетов и сокетов.
Другая группа в компании хочет использовать упакованную версию классов провайдеров клиентских данных на основе QTcpSocket. (Что делает в основном то, на что это похоже, предоставляет данные с сервера на клиентские дисплеи)
Однако у этой группы есть огромное приложение, созданное в основном из MFC, и это просто не изменится в ближайшее время. DLL на основе Qt также загружается с задержкой, поэтому его можно развернуть без этой функции в определенных конфигурациях.
У меня это работает, но это немного глупо. Вот мое решение на данный момент:
Конструктор класса оболочки 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);
}
Обратный вызов просто вызывает функцию processEvents (), используя timerID в качестве указателя на экземпляр класса. Документы 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 (), оно может передать 0 для eventTimerInterval и вызвать BLAHBLAH::processEvents().
Есть мысли по этому поводу? Портирование этого приложения на Qt не вариант. Это не наше.
Кажется, это работает, но, возможно, здесь нарушено несколько предположений. Могу ли я просто создать QCoreApplication с фиктивными аргументами, подобными этому? Безопасно ли работать таким образом в очереди событий?
Я не хочу, чтобы это взорвалось мне в лицо позже. Мысли?
2 ответа
Изучая код 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;
};
У меня есть метод OpenApp в QObject (то есть moc'ed), как это:
// 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;
}
Пока все работает нормально, я не уверен, что мне нужно удалить приложение в конце, я обновлю свой ответ, если найду что-то.
Документация Qt для 4.5.2 гласит, что аргументы QCoreApplication должны иметь время жизни, равное периоду объекта приложения, поэтому вам не следует использовать локальные переменные.
Помимо этой мелочи:
Я борюсь с той же проблемой, и все, кажется, работает для меня тоже. Однако я бы порекомендовал быть очень осторожным во время выгрузки / выхода, поскольку, если вы используете цикл событий из другого приложения и этот цикл событий останавливается до того, как ваша библиотека выгружена, то при попытке закрыть все возможные неприятности могут возникнуть () сокеты и удалить объекты QObject.