27 votes

Utilisation d'une DLL basée sur Qt dans une application non-Qt

Est-ce que je le fais bien ?

Un de mes clients a un groupe dans lequel je développe des applications client-serveur basées sur Qt, avec beaucoup de widgets amusants et de sockets.

Un autre groupe au sein de l'entreprise souhaite utiliser une version enveloppée des classes de fournisseur de données client basées sur QTcpSocket. (qui fait essentiellement ce qu'il semble, fournir des données du serveur aux écrans clients).

Cependant, ce groupe possède une énorme application construite principalement avec MFC, et cela n'est pas prêt de changer. La DLL basée sur Qt est également à chargement différé afin qu'elle puisse être déployée sans cette fonctionnalité dans certaines configurations.

J'ai réussi à le faire fonctionner, mais c'est un peu compliqué. Voici ma solution pour le moment :

Le constructeur de la classe wrapper DLL appelle QCoreApplication::instance() pour voir si elle est NULL ou non. S'il est NULL, il suppose qu'il est dans une application non-Qt, et crée sa propre instance QCoreApplication :

if (QCoreApplication::instance() == NULL)
{
    int argc = 1;
    char* argv[] = { "dummy.exe", NULL };
    d->_app = new QCoreApplication(argc, argv);  // safe?
}
else
    d->_app = NULL;

Il mettra ensuite en place un timer Windows pour appeler occasionnellement 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);
}

Le callback appelle simplement la fonction processEvents() en utilisant le timerID comme pointeur vers l'instance de la classe. La documentation de SetTimer() indique que lorsque le HWND est NULL, il ignore le timerID, ce qui semble donc parfaitement valable.

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

Je détruis ensuite l'instance de QCoreApplication en toute dernière chose dans le destructeur.

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

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

Si l'application hôte souhaite chronométrer elle-même les appels à processEvents(), elle peut passer 0 pour eventTimerInterval et appeler BLAHBLAH::processEvents() elle-même.

Des idées à ce sujet ? Porter cette application sur Qt n'est pas une option. Ce n'est pas la nôtre.

Cela semble fonctionner, mais il y a probablement plusieurs hypothèses qui sont brisées ici. Puis-je simplement construire une QCoreApplication avec des arguments factices comme ça ? La file d'attente d'événements peut-elle fonctionner de cette manière ?

Je ne veux pas que ça m'explose à la figure plus tard. Tu en penses quoi ?

11voto

dashesy Points 376

En étudiant le code Qt, il semble que QCoreApplication soit nécessaire pour distribuer des messages à l'échelle du système, comme les événements de la minuterie. Des choses comme les signaux/slots et même QThreads n'en dépendent pas, à moins qu'elles ne soient liées à ces messages système. Voici comment je fais cela dans une bibliothèque partagée (de manière multiplateforme en utilisant Qt lui-même) et j'appelle en fait exec porque processEvents() seul ne traite pas tout.

J'ai un espace de noms global :

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

J'ai un OpenApp dans un QObject (qui est moc'ed) comme ceci :

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

Et voici OnExec fente :

if (QCoreApplication::instance() == NULL)
{
    QAppPriv::pApp = new QCoreApplication(QAppPriv::argc, QAppPriv::argv);
    QAppPriv::pApp->exec();
    if (QAppPriv::pApp)
        delete QAppPriv::pApp;
}

Jusqu'à présent, cela semble fonctionner correctement, je ne suis pas sûr que je doive supprimer l'application à la fin, je mettrai à jour ma réponse si je trouve quelque chose.

2voto

Gojir4 Points 72

Je pense que les classes "MFC to Qt Migration" peuvent vous aider.

Lien : http://doc.qt.nokia.com/solutions/4/qtwinmigrate/winmigrate-walkthrough.html

1voto

James Points 11054

La documentation de Qt pour 4.5.2 dit que les arguments de QCoreApplication doivent avoir une durée de vie aussi longue que l'objet de l'application - donc vous ne devriez pas vraiment utiliser les variables locales.

A part cette petite chose :

Je suis aux prises avec le même problème, et tout semble fonctionner pour moi aussi. Je recommanderais cependant d'être très prudent au moment du déchargement/de la sortie, car si vous utilisez la boucle d'événements d'une autre application et que cette boucle d'événements est arrêtée avant que votre bibliothèque ne soit déchargée, toutes sortes de choses désagréables peuvent se produire lorsque vous essayez de fermer les sockets() et de supprimer les QObjects.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X