Sur cette page

Création de serveurs ActiveX sous Qt

Le module « QAxServer » fait partie du framework ActiveQt. Il se compose de trois classes :

  • QAxFactory définit une fabrique pour la création d’objets COM.
  • QAxBindable fournit une interface entre le widget Qt et l'objet COM.
  • QAxAggregated peut être sous-classé pour implémenter des interfaces COM supplémentaires.

Quelques exemples d'implémentation de contrôles ActiveX et d'objets COM sont fournis.

Utilisation de la bibliothèque

Pour transformer une application Qt standard en serveur COM à l’aide de la bibliothèque QAxServer, vous devez ajouter axserver à la variable QT dans votre fichier .pro.

Un serveur exécutable hors processus est généré à partir d’un fichier ` .pro ` comme celui-ci :

TEMPLATE = app
QT  += axserver

RC_FILE  = qaxserver.rc
...

Pour compiler un serveur en processus, utilisez un fichier .pro comme celui-ci :

TEMPLATE = lib
QT += axserver
CONFIG  += dll

DEF_FILE = qaxserver.def
RC_FILE  = qaxserver.rc
...

Les fichiers qaxserver.rc et qaxserver.def font partie du framework et peuvent être utilisés depuis leur emplacement habituel (en spécifiant un chemin d’accès dans le fichier .pro ) ou copiés dans le répertoire du projet. Vous pouvez modifier ces fichiers à condition qu’ils incluent n’importe quel fichier en tant qu’entrée de bibliothèque de types ; vous pouvez par exemple ajouter des informations de version ou spécifier une icône de boîte à outils différente.

L'utilisation du module axserver entraînera l'ajout par l'outil qmake des étapes de compilation requises au système de compilation :

  • Liez le binaire à qaxserver.lib au lieu de qtmain.lib
  • Appeler l’outil idc pour générer un fichier IDL pour le serveur COM
  • Compiler le fichier IDL en une bibliothèque de types à l’aide de l’outil MIDL (fourni avec l’installation du compilateur)
  • Joindre la bibliothèque de types obtenue en tant que ressource binaire au binaire du serveur (à nouveau à l’aide de l’outil idc )
  • Enregistrer le serveur. Cette étape peut nécessiter des privilèges d’administrateur et peut être ignorée en définissant la configuration « qaxserver_no_register ».

Pour ignorer l’étape de post-traitement, définissez également la configuration « qaxserver_no_postlink ».

De plus, vous pouvez spécifier un numéro de version à l’aide de la variable ` VERSION `, par exemple

TEMPLATE = lib
VERSION = 2.5
...

Le numéro de version spécifié sera utilisé comme version de la bibliothèque de types et du serveur lors de l’enregistrement.

Hors processus ou en processus

Le fait que votre serveur COM s’exécute en tant qu’exécutable autonome ou en tant que bibliothèque partagée au sein du processus client dépend principalement du type d’objets COM que vous souhaitez fournir dans le serveur.

Un serveur exécutable présente l’avantage de pouvoir s’exécuter en tant qu’application autonome, mais il ajoute une surcharge considérable à la communication entre le client COM et l’objet COM. Si le contrôle comporte une erreur de programmation, seul le processus serveur exécutant le contrôle se bloquera, et l’application cliente continuera probablement à s’exécuter. Tous les clients COM ne prennent pas en charge les serveurs exécutables.

Un serveur en cours de traitement est généralement plus léger et s’initialise plus rapidement. La communication entre le client et le serveur s'effectue directement par le biais d’appels de fonctions virtuelles et n’introduit pas la surcharge liée aux appels de procédure à distance. Cependant, si le serveur plante, l’application cliente risque de planter également, et toutes les fonctionnalités ne sont pas disponibles pour les serveurs en cours d’exécution (c’est-à-dire l’enregistrement dans la table des objets en cours d’exécution de COM).

Les deux types de serveurs peuvent utiliser Qt soit sous forme de bibliothèque partagée, soit lié statiquement au binaire du serveur.

Erreurs courantes lors des étapes post-compilation

Pour que les étapes de post-traitement spécifiques à ActiveQt fonctionnent, le serveur doit répondre à certaines exigences :

  • Tous les contrôles exposés doivent pouvoir être créés en présence d’une simple instance de ` QApplication `
  • La liaison initiale du serveur inclut une ressource de bibliothèque de types temporaire
  • Toutes les dépendances requises pour l'exécution du serveur se trouvent dans le chemin d'accès système (ou dans le chemin utilisé par l'environnement d'appel ; notez que Visual Studio dispose de son propre ensemble de variables d'environnement répertoriées dans la boîte de dialogue Outils|Options|Répertoires).

Si ces conditions ne sont pas remplies, une ou plusieurs des erreurs suivantes sont susceptibles de se produire :

L’exécutable du serveur plante

Pour générer l’IDL, les widgets exposés en tant que contrôles ActiveX doivent être instanciés (le constructeur est appelé). À ce stade, seul un objet ` QApplication ` existe. Le constructeur de votre widget ne doit pas dépendre de la création d’autres objets ; il doit notamment vérifier l’absence de pointeurs nuls.

Pour déboguer votre serveur, exécutez-le avec l’option -dumpidl fichier_de_sortie et vérifiez à quel moment il plante.

Notez qu’aucune fonction du contrôle n’est appelée.

L'exécutable du serveur n'est pas une application Win32 valide

L'ajout de la bibliothèque de types a corrompu le binaire du serveur. Il s'agit d'un bug de Windows qui ne se produit qu'avec les versions de production.

La première étape de liaison doit consister à lier une bibliothèque de types factice à l’exécutable, qui pourra ensuite être remplacée par idc. Ajoutez un fichier de ressources contenant une bibliothèque de types à votre projet, comme indiqué dans les exemples.

« Impossible de localiser la DLL »

Le système de compilation doit exécuter l'exécutable du serveur pour générer la définition d'interface et enregistrer le serveur. Si une bibliothèque de liens dynamiques à laquelle le serveur se lie ne se trouve pas dans le chemin d'accès, cette opération peut échouer (par exemple, Visual Studio appelle le serveur en utilisant les paramètres d'environnement spécifiés dans l'option « Répertoires »). Assurez-vous que toutes les DLL et tous les plug-ins requis par votre serveur se trouvent dans un répertoire figurant dans le chemin d’accès tel qu’il est indiqué dans la boîte de message d’erreur (voir également L’outil de déploiement Windows).

« Impossible d’ouvrir le fichier… »

Le serveur ActiveX n’a pas pu s’arrêter correctement lorsque le dernier client a cessé de l’utiliser. La fermeture de l’application prend généralement environ deux secondes, mais vous devrez peut-être utiliser le Gestionnaire des tâches pour forcer l’arrêt du processus (par exemple, lorsqu’un client ne libère pas correctement les contrôles).

Impossible d'instancier le contrôle

Dans ce cas, il peut être utile d’enregistrer le serveur en tant qu’administrateur.

Implémentation des contrôles

Pour implémenter un objet COM avec Qt, créez une sous-classe de ` QObject ` ou de n'importe quelle sous-classe existante de ` QObject `. Si la classe est une sous-classe de ` QWidget`, l'objet COM sera un contrôle ActiveX.

#include <QWidget>

class MyActiveX : public QWidget
{
    Q_OBJECT

La macro Q_OBJECT est nécessaire pour fournir au framework ActiveQt les informations de méta-objet concernant le widget.

Q_CLASSINFO("ClassID", "{1D9928BD-4453-4bdd-903D-E525ED17FDE5}")
Q_CLASSINFO("InterfaceID", "{99F6860E-2C5A-42ec-87F2-43396F4BE389}")
Q_CLASSINFO("EventsID", "{0A3E9F27-E4F1-45bb-9E47-63099BCCD0E3}")

Utilisez la macro Q_CLASSINFO() pour spécifier les identifiants COM de l’objet COM. Les identifiants ClassID et InterfaceID sont obligatoires, tandis que EventsID n’est nécessaire que si votre objet possède des signaux. Pour générer ces identifiants, utilisez des outils système tels que uuidgen ou guidgen.

Vous pouvez spécifier des attributs supplémentaires pour chacune de vos classes ; voir Informations sur les classes et optimisation pour plus de détails.

Q_PROPERTY(int value READ value WRITE setValue)

Utilisez la macro Q_PROPERTY() pour déclarer les propriétés du contrôle ActiveX.

Déclarez un constructeur standard prenant un objet parent, ainsi que des fonctions, des signaux et des slots comme pour toute sous-classe de QObject.

public:
    MyActiveX(QWidget *parent = 0)
    ...

    int value() const;

public slots:
    void setValue(int v);
    ...

signals:
    void valueChange(int v);
    ...

};

Le framework ActiveQt exposera les propriétés et les slots publics en tant que propriétés et méthodes ActiveX, et les signaux en tant qu’événements ActiveX, et effectuera la conversion entre les types de données Qt et les types de données COM équivalents.

Types de données

Les types de données Qt pris en charge pour les propriétés sont les suivants :

Type de données QtPropriété COM
boolVARIANT_BOOL
QStringBSTR
intint
uintint non signé
doubledouble
qlonglongCY
qulonglongCY
QColorOLE_COLOR
QDateDATE
QDateTimeDATE
QTimeDATE
QFontIFontDisp*
QPixmapIPictureDisp*
QVariantVARIANT
QVariantList (identique à QList<QVariant>)SAFEARRAY(VARIANT)
QStringListSAFEARRAY(BSTR)
QByteArraySAFEARRAY(BYTE)
QRectType défini par l'utilisateur
QSizeType défini par l'utilisateur
QPointType défini par l'utilisateur

Les types de données Qt pris en charge pour les paramètres des signaux et des slots sont les suivants :

Type de données QtParamètre COM
bool[in] VARIANT_BOOL
bool&[entrée, sortie] VARIANT_BOOL*
QString, const QString&[in] BSTR
QString&[in, out] BSTR*
QString&[in, out] BSTR*
int[entrée] int
int&[entrée, sortie] int
uint[in] unsigned int
uint&[in, out] unsigned int*
double[in] double
double&[in, out] double*
QColor, const QColor&[in] OLE_COLOR
QColor&[in, out] OLE_COLOR*
QDate, const QDate&[in] DATE
QDate&[entrée, sortie] DATE*
QDateTime, const QDateTime&[in] DATE
QDateTime&[entrée, sortie] DATE*
QFont, const QFont&[in] IFontDisp*
QFont&[entrée, sortie] IFontDisp**
QPixmap, const QPixmap&[in] IPictureDisp*
QPixmap&[in, out] IPictureDisp**
QList<QVariant>, const QList<QVariant>&[in] SAFEARRAY(VARIANT)
QList<QVariant>&[in, out] SAFEARRAY(VARIANT)*
QStringList, const QStringList&[in] SAFEARRAY(BSTR)
QStringList&[entrée, sortie] SAFEARRAY(BSTR)*
QByteArray, const QByteArray&[in] SAFEARRAY(BYTE)
QByteArray&[entrée, sortie] SAFEARRAY(BYTE)*
QObject*[in] IDispatch*
QRect& [entrée, sortie] struct QRect (définie par l'utilisateur)
QSize&[in, out] struct QSize (définie par l'utilisateur)
QPoint&[in, out] struct QPoint (définie par l'utilisateur)

Les énumérations et indicateurs exportés sont également pris en charge (voir Q_ENUM() et Q_FLAG()). Les types des paramètres d'entrée sont également pris en charge en tant que valeurs de retour.

Les propriétés et les signaux/slots dont les paramètres utilisent d’autres types de données sont ignorés par le framework ActiveQt.

Sous-objets

Les objets COM peuvent comporter plusieurs sous-objets représentant chacun un sous-élément de l’objet COM. Un objet COM représentant une application de tableur multi-documents peut, par exemple, fournir un sous-objet pour chaque feuille de calcul.

Toute sous-classe d’ QObject peut être utilisée comme type pour un sous-objet dans ActiveX, à condition qu’elle soit reconnue par l’ QAxFactory. Ce type peut alors être utilisé dans des propriétés, ou comme type de retour ou paramètre d’un slot.

Notification des propriétés

Pour rendre les propriétés liables pour le client ActiveX, utilisez l’héritage multiple à partir de la classe ` QAxBindable ` :

#include <QAxBindable>
#include <QWidget>

class MyActiveX : public QWidget, public QAxBindable
{
    Q_OBJECT

Lors de l’implémentation des fonctions d’écriture des propriétés, utilisez les fonctions requestPropertyChange() et propertyChanged() de la classe ` QAxBindable ` pour permettre aux clients ActiveX de se lier aux propriétés du contrôle.

Mise à disposition des contrôles

Pour qu’un serveur COM soit accessible au système COM, il doit être enregistré dans le registre système à l’aide de cinq identifiants uniques. Ces identifiants sont fournis par des outils tels que guidgen ou uuidgen. Les informations d’enregistrement permettent à COM de localiser le fichier binaire fournissant un contrôle ActiveX demandé, de marshaler les appels de procédure à distance vers le contrôle et de lire les informations de type concernant les méthodes et les propriétés exposées par le contrôle.

Pour créer l’objet COM lorsque le client en fait la demande, le serveur doit exporter une implémentation d’une interface QAxFactory. La manière la plus simple d’y parvenir consiste à utiliser un ensemble de macros :

QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}",
                 "{a8f21901-7ff7-4f6a-b939-789620c03d83}")
    QAXCLASS(MyWidget)
    QAXCLASS(MyWidget2)
    QAXTYPE(MySubType)
QAXFACTORY_END()

Cela permettra d’exporter MyWidget et MyWidget2 en tant qu’objets COM pouvant être créés par des clients COM, et d’enregistrer MySubType en tant que type pouvant être utilisé dans les propriétés et les paramètres de MyWidget et MyWidget2.

La page QAxFactory class documentation explique comment utiliser cette macro, ainsi que comment implémenter et utiliser des fabriques personnalisées.

Pour les serveurs exécutables hors processus, vous pouvez implémenter une fonction main() afin d’instancier un objet QApplication et d’entrer dans la boucle d’événements, comme pour n’importe quelle application Qt standard. Par défaut, l’application démarre comme une application Qt standard, mais si vous passez -activex e en ligne de commande, elle démarre en tant que serveur ActiveX. Utilisez QAxFactory::isServer() pour créer et exécuter une interface d’application standard, ou pour empêcher une exécution autonome :

#include <QApplication>
#include <QAxFactory>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    if (!QAxFactory::isServer()) {
        // create and show main window
    }
    return app.exec();
}

Cela n’est toutefois pas nécessaire, car ActiveQt fournit une implémentation par défaut de la fonction `main`. Cette implémentation par défaut appelle ` QAxFactory::startServer()`, crée une instance ` QApplication ` et appelle `exec()`.

Pour compiler l'exécutable du serveur ActiveX, exécutez ` qmake ` afin de générer le fichier `makefile`, puis utilisez l'outil `make` de votre compilateur comme pour toute autre application Qt. Le processus `make` enregistrera également les contrôles dans le registre système en appelant l'exécutable résultant avec l'option de ligne de commande ` -regserver `.

Si le serveur ActiveX est un exécutable, les options de ligne de commande suivantes sont prises en charge :

OptionRésultat
-regserverEnregistre le serveur dans le registre système
-regserverperuserEnregistre le serveur dans le registre système pour l'utilisateur actuel (depuis la version 5.14)
-unregserverDésenregistre le serveur du registre système
-unregserverperuserDésenregistre le serveur du registre système pour l'utilisateur actuel (depuis la version 5.14)
-activexLance l'application en tant que serveur ActiveX
-dumpidl <file> -version x.yÉcrit l'IDL du serveur dans le fichier spécifié. La bibliothèque de types portera la version x.y

Les serveurs en cours d'exécution peuvent être enregistrés à l'aide de l'outil « regsvr32 » disponible sur tous les systèmes Windows.

Problèmes courants lors de la compilation

Les erreurs de compilation/liaison répertoriées sont basées sur celles générées par le compilateur Microsoft Visual C++ 6.0.

« Aucune fonction surchargée ne prend 2 paramètres »

Lorsque cette erreur se produit dans du code utilisant la macro QAXCLASS() ou QAXFACTORY_DEFAULT(), cela signifie que la classe de widget ne dispose d’aucun constructeur pouvant être utilisé par l’usine par défaut. Ajoutez un constructeur de widget standard ou implémentez une usine personnalisée qui n’en nécessite pas.

Lorsque l’erreur se produit dans du code utilisant la macro QAXFACTORY_EXPORT(), la sous-classe d’ QAxFactory ne disposait pas d’un constructeur approprié. Fournissez un constructeur de classe public tel que

MyFactory(const QUuid &, const QUuid &);

pour votre classe de fabrique.

« Erreur de syntaxe : suffixe incorrect sur un nombre »

Les identifiants uniques n’ont pas été transmis sous forme de chaînes de caractères aux macros QAXFACTORY_EXPORT(), QAXFACTORY_BEGIN() ou QAXFACTORY_DEFAULT().

« Symbole externe non résolu _ucm_instantiate »

Le serveur n’exporte pas d’implémentation de la fonction ` QAxFactory`. Utilisez la macro ` QAXFACTORY_EXPORT()` dans l’un des fichiers d’implémentation du projet pour instancier et exporter une fabrique, ou utilisez la macro ` QAXCLASS()` ou `QAXFACTORY_DEFAULT()` pour utiliser la fabrique par défaut.

« _ucm_initialize déjà défini dans… »

Le serveur exporte plusieurs implémentations d’un QAxFactory, ou exporte deux fois la même implémentation. Si vous utilisez la fabrique par défaut, la macro QAXFACTORY_BEGIN() ou QAXFACTORY_DEFAULT() ne doit être utilisée qu’une seule fois dans le projet. Utilisez une implémentation personnalisée de QAxFactory et la macro QAXFACTORY_EXPORT() si le serveur fournit plusieurs contrôles ActiveX.

Distribution des binaires QAxServer

Les serveurs ActiveX écrits avec Qt peuvent utiliser Qt soit sous forme de bibliothèque partagée, soit en intégrant Qt de manière statique dans le binaire. Ces deux méthodes produisent des paquets assez volumineux (soit le binaire du serveur lui-même devient volumineux, soit vous devez fournir la DLL Qt).

Installation de serveurs autonomes

Lorsque votre serveur ActiveX peut également s’exécuter en tant qu’application autonome, lancez l’exécutable du serveur avec le paramètre de ligne de commande ` -regserver ` après avoir installé l’exécutable sur le système cible. Les contrôles fournis par le serveur seront alors disponibles pour les clients ActiveX.

Installation de serveurs en cours de traitement

Lorsque votre serveur ActiveX fait partie d’un package d’installation, utilisez l’outil « regsvr32 » fourni par Microsoft pour enregistrer les contrôles sur le système cible. Si cet outil n’est pas présent, chargez la DLL dans le processus de votre programme d’installation, résolvez le symbole « DllRegisterServer » et appelez la fonction :

HMODULE dll = LoadLibrary("myserver.dll");
typedef HRESULT(__stdcall *DllRegisterServerProc)();
DllRegisterServerProc DllRegisterServer =
    (DllRegisterServerProc)GetProcAddress(dll, "DllRegisterServer");

HRESULT res = E_FAIL;
if (DllRegisterServer)
    res = DllRegisterServer();
if (res != S_OK)
    // error handling

Mise à disposition des serveurs sur Internet

Si vous souhaitez utiliser les contrôles de votre serveur dans des pages Web, vous devez rendre ce serveur accessible au navigateur utilisé pour afficher votre page, et vous devez spécifier l'emplacement du package du serveur dans votre page.

Pour spécifier l'emplacement d'un serveur, utilisez l'attribut CODEBASE dans la balise OBJECT de votre site Web. La valeur peut pointer vers le fichier du serveur lui-même, vers un fichier INF répertoriant les autres fichiers requis par le serveur (par exemple, la DLL Qt), ou vers une archive CAB compressée.

Les fichiers INF et CAB sont documentés dans la quasi-totalité des ouvrages consacrés à la programmation ActiveX et COM, ainsi que dans la bibliothèque MSDN et diverses autres ressources en ligne. Les exemples incluent des fichiers INF pouvant servir à créer des archives CAB :

[version]
    signature="$CHICAGO$"
    AdvancedINF=2.0
 [Add.Code]
    simpleax.exe=simpleax.exe
 [simpleax.exe]
    file-win32-x86=thiscab
    clsid={DF16845C-92CD-4AAB-A982-EB9840E74669}
    RegisterServer=yes

L'outil CABARC de Microsoft permet de générer facilement des archives CAB :

cabarc N simpleax.cab simpleax.exe simple.inf

Les fichiers INF partent du principe d’une compilation statique de Qt ; par conséquent, aucune dépendance vis-à-vis d’autres DLL n’est répertoriée dans ces fichiers. Pour distribuer un serveur ActiveX dépendant de DLL, vous devez ajouter ces dépendances et fournir les fichiers de bibliothèque avec l’archive.

Utilisation des contrôles

Pour utiliser les contrôles ActiveX, par exemple pour les intégrer dans une page Web, utilisez la balise HTML <object>.

<object ID="MyActiveX1" CLASSID="CLSID:ad90301a-849e-4e8b-9a91-0a6dc5f6461f">
   ...
<\object>

Pour initialiser les propriétés du contrôle, utilisez

<object ID=...>
    <param name="name" value="value">
<\object>

Si le navigateur Web prend en charge les scripts, utilisez JavaScript, VBScript et les formulaires pour créer des scripts pour le contrôle. Les exemples ActiveQt comprennent des pages HTML de démonstration pour les contrôles présentés.

Clients ActiveX pris en charge et non pris en charge

Les informations suivantes s’appuient en grande partie sur nos propres expériences avec les contrôles ActiveX et les applications clientes, et ne sont en aucun cas exhaustives.

Clients pris en charge

Ces applications standard fonctionnent avec les contrôles ActiveX développés avec ActiveQt. Notez que certains clients ne prennent en charge que les contrôles « in-process ».

  • Internet Explorer
  • Conteneur de test de contrôles ActiveX de Microsoft
  • Microsoft Visual Studio 6.0
  • Microsoft Visual Studio.NET/2003
  • Microsoft Visual Basic 6.0
  • Conteneurs basés sur MFC et ATL
  • Sybase PowerBuilder
  • Conteneurs basés sur ActiveQt

Les applications Microsoft Office sont prises en charge, mais vous devez enregistrer les contrôles en tant qu’objets « Insertable ». Réimplémentez la méthode QAxFactory::registerClass pour ajouter cet attribut à la classe COM, ou définissez l’information de classe « Insertable » de votre classe sur « yes » à l’aide de la macro Q_CLASSINFO.

Clients non pris en charge

Nous n’avons pas réussi à faire fonctionner les objets COM basés sur ActiveQt avec les applications clientes suivantes.

  • Borland C++ Builder (versions 5 et 6)
  • Borland Delphi

Erreurs d'exécution courantes

Le serveur ne répond pas

Si le système ne parvient pas à démarrer le serveur (vérifiez dans le gestionnaire de tâches si le serveur exécute un processus), assurez-vous qu'aucune DLL dont dépend le serveur ne manque dans le chemin d'accès système (par exemple, la DLL Qt !). Utilisez un outil de analyse des dépendances pour afficher toutes les dépendances du binaire du serveur.

Si le serveur fonctionne (par exemple, si le gestionnaire de tâches répertorie un processus), consultez la section suivante pour obtenir des informations sur le débogage de votre serveur.

Impossible de créer l'objet

Si le serveur a pu être compilé et enregistré correctement pendant le processus de compilation, mais que l'objet ne peut pas être initialisé, par exemple par l'application OLE/COM Object Viewer, assurez-vous qu'aucune DLL dont dépend le serveur ne manque dans le chemin d'accès système (par exemple, la DLL Qt). Utilisez un outil de visualisation des dépendances pour afficher toutes les dépendances du binaire du serveur.

Si le serveur fonctionne, consultez la section suivante pour obtenir des informations sur le débogage de votre serveur.

Plantages lors du déchargement et du rechargement des serveurs COM

Si les serveurs COM Active Qt utilisent des modules Qt autres que ceux fournis dans Qt Base, il est nécessaire d’activer le serveur COM en tant que serveur COM hors processus. Tenter d’activer un serveur COM en processus incluant des modules tels que Qt Quick peut entraîner un plantage après le déchargement du serveur COM.

Plantage ou comportement inattendu lors d’appels COM sortants

Sachez qu’un serveur COM hors processus traite sa file d’attente de messages tout en effectuant un appel sortant vers le client. Cela peut entraîner un comportement inattendu ou un plantage si le client appelle le serveur au même moment. Dans ce cas, l’appel entrant sera exécuté sur le serveur avant que l’appel sortant ne revienne. En particulier, si le client ferme un contrôle ActiveX alors que celui-ci est en train de rappeler le client, cela peut entraîner un plantage. Ces problèmes de réentrance peuvent être atténués à l’aide de filtres de messages (IMessageFilter et CoRegisterMessageFilter).

Débogage des erreurs d’exécution

Pour déboguer un serveur en cours d’exécution dans Visual Studio, définissez le projet serveur comme projet actif, puis spécifiez un « exécutable pour la session de débogage » client dans les paramètres du projet (utilisez par exemple le conteneur de test ActiveX). Vous pouvez définir des points d’arrêt dans votre code et également passer en mode pas à pas dans le code ActiveQt et Qt si vous avez installé la version de débogage.

Pour déboguer un serveur exécutable, exécutez l’application dans un débogueur et lancez-la avec le paramètre de ligne de commande ` -activex`. Lancez ensuite votre client et créez une instance de votre contrôle ActiveX. COM utilisera le processus existant pour le prochain client tentant de créer un contrôle ActiveX.

Informations sur les classes et optimisation

Pour définir des attributs pour chaque classe COM, utilisez la macro ` Q_CLASSINFO `, qui fait partie du système de méta-objets de Qt.

CléSignification de la valeur
VersionLa version de la classe (1.0 par défaut)
DescriptionChaîne de caractères décrivant la classe.
ClassIDL'identifiant de la classe. Vous devez réimplémenter la méthode ` QAxFactory::classID ` si celui-ci n'est pas spécifié.
InterfaceIDL'ID de l'interface. Vous devez réimplémenter la méthode ` QAxFactory::interfaceID ` si ce paramètre n'est pas spécifié.
EventsIDL'ID de l'interface d'événements. Aucun signal n'est exposé en tant qu'événement COM si ce paramètre n'est pas spécifié.
DefaultPropertyLa propriété spécifiée représente la propriété par défaut de cette classe. Par exemple, la propriété par défaut d'un bouton serait « text ».
Signal par défautLe signal spécifié représente le signal par défaut de cette classe. Par exemple, le signal par défaut d'un bouton serait « clicked ».
LicenseKeyLa création d'un objet nécessite la clé de licence spécifiée. La clé peut être vide pour exiger une machine sous licence. Par défaut, les classes ne sont pas sous licence. Voir également la section suivante.
StockEventsLes objets exposent les événements boursiers si la valeur est « yes ». Voir QAxFactory::hasStockEvents()
ToSuperClassLes objets exposent les fonctionnalités de toutes les superclasses jusqu'à la classe dont le nom figure dans la valeur, incluse. Voir QAxFactory::exposeToSuperClass()
InsertableSi la valeur est « yes », la classe est enregistrée comme « Insertable » et figurera dans les conteneurs OLE 2 (par exemple, Microsoft Office). Cet attribut n’est pas activé par défaut.
AggregatableSi la valeur est « no », la classe ne prend pas en charge l’agrégation. Par défaut, l’agrégation est prise en charge.
CréableSi la valeur est « non », la classe ne peut pas être créée par le client et n’est accessible que via l’API d’une autre classe (c’est-à-dire que la classe est un sous-type).
RegisterObjectSi la valeur est « oui », les objets de cette classe sont enregistrés auprès d’OLE et accessibles depuis la table des objets en cours d’exécution (c’est-à-dire que les clients peuvent se connecter à une instance de cette classe déjà en cours d’exécution). Cet attribut n’est pris en charge que dans les serveurs hors processus.
MIMEL'objet peut gérer les données et les fichiers au format spécifié par la valeur. La valeur a le format mime:extension:description. Les formats multiples sont séparés par un point-virgule.
CoClassAliasNom de classe utilisé dans l’IDL généré et dans le registre. Ceci est particulièrement utile pour les classes C++ situées dans un espace de noms — par défaut, ActiveQt supprime simplement le « :: » pour permettre la compilation de l’IDL.
Catégories implémentéesListe d’UUID d’identifiants de catégorie (CATID) séparés par des virgules. Mécanisme générique permettant de spécifier des capacités supplémentaires pour les conteneurs, en plus de « control », « insertable », etc. Parmi les CATID courants, on trouve notamment CATID_InternetAware (« {0DE86A58-2BAA-11CF-A229-00AA003D7352} »), CATID_SafeForScripting (« {7DD95801-9882-11CF-9FA9-00AA006C42C4} ») ainsi que des valeurs CATID définies par l’utilisateur.

Notez que les clés et les valeurs sont sensibles à la casse.

Le code suivant déclare la version 2.0 d’une classe qui n’expose que sa propre API et qui est disponible dans la boîte de dialogue « Insérer des objets » des applications Microsoft Office.

class MyActiveX : public QWidget
{
    Q_OBJECT
    Q_CLASSINFO("Version", "2.0")
    Q_CLASSINFO("ClassID", "{7a4cffd8-cbcd-4ae9-ae7e-343e1e5710df}")
    Q_CLASSINFO("InterfaceID", "{6fb035bf-8019-48d8-be51-ef05427d8994}")
    Q_CLASSINFO("EventsID", "{c42fffdf-6557-47c9-817a-2da2228bc29c}")
    Q_CLASSINFO("Insertable", "yes")
    Q_CLASSINFO("ToSuperClass", "MyActiveX")
    Q_PROPERTY(...)

public:
    MyActiveX(QWidget *parent = 0);

    ...
};

Développement de composants sous licence

Si vous développez des composants, vous souhaiterez peut-être contrôler qui est autorisé à les instancier. Étant donné que le binaire serveur peut être distribué et enregistré sur n'importe quelle machine cliente, il est possible pour n'importe qui d'utiliser ces composants dans son propre logiciel.

La gestion des licences des composants peut s’effectuer à l’aide de diverses techniques : par exemple, le code créant le contrôle peut fournir une clé de licence, ou la machine sur laquelle le contrôle est censé s’exécuter doit disposer d’une licence.

Pour marquer une classe Qt comme soumise à licence, spécifiez une « LicenseKey » à l’aide de la macro Q_CLASSINFO().

class MyLicensedControl : public QWidget
{
    Q_OBJECT
    Q_CLASSINFO("LicenseKey", "<key string>")
    ...
};

Cette clé est nécessaire pour pouvoir créer une instance de ` MyLicensedControl ` sur une machine qui ne dispose pas elle-même de licence. Le développeur titulaire de la licence peut désormais redistribuer le binaire du serveur avec son application, ce qui permet de créer le contrôle à l’aide de la valeur de « LicenseKey », tandis que les utilisateurs de l’application ne peuvent pas créer le contrôle sans la clé de licence.

Si une seule clé de licence pour le contrôle ne suffit pas (c'est-à-dire si vous souhaitez que différents développeurs reçoivent des clés de licence distinctes), vous pouvez spécifier une clé vide pour indiquer que le contrôle nécessite une licence, puis réimplémenter la méthode QAxFactory::validateLicenseKey() afin de vérifier qu'une licence existe sur le système (par exemple via un fichier de licence).

Autres interfaces

Les contrôles ActiveX fournis par les serveurs ActiveQt prennent en charge un ensemble minimal d’interfaces COM permettant de mettre en œuvre les spécifications OLE. Lorsqu’une classe ActiveX hérite de la classe ` QAxBindable `, elle peut également implémenter des interfaces COM supplémentaires.

Créez une nouvelle sous-classe de ` QAxAggregated ` et utilisez l’héritage multiple pour créer des sous-classes de classes d’interfaces COM supplémentaires.

class AxImpl : public QAxAggregated, public ISomeCOMInterface
{
public:
    AxImpl() {}

    long queryInterface(const QUuid &iid, void **iface);

    // IUnknown
    QAXAGG_IUNKNOWN

    // ISomeCOMInterface
    ...
}

Réimplémentez la fonction ` QAxAggregated::queryInterface()` pour prendre en charge les interfaces COM supplémentaires.

long AxImpl::queryInterface(const QUuid &iid, void **iface)
{
    *iface = 0;
    if (iid == IID_ISomeCOMInterface)
        *iface = (ISomeCOMInterface *)this;
    else
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

ISomeCOMInterface étant une sous-classe de IUnknown, vous devrez implémenter les fonctions QueryInterface(), AddRef() et Release(). Pour ce faire, utilisez la macro QAXAGG_IUNKNOWN dans la définition de votre classe. Si vous implémentez manuellement les fonctions IUnknown, déléguez les appels au pointeur d’interface renvoyé par la fonction QAxAggregated::controllingUnknown(), par exemple :

HRESULT AxImpl::QueryInterface(REFIID iid, void **iface)
{
    return controllingUnknown()->QueryInterface(iid, iface);
}

Ne prenez pas en charge l’interface IUnknown elle-même dans votre implémentation de la fonction queryInterface().

Implémentez les méthodes des interfaces COM et utilisez QAxAggregated::object() si vous devez effectuer des appels vers la sous-classe QObject qui implémente le contrôle.

Dans votre sous-classe de ` QAxBindable `, implémentez la méthode ` QAxBindable::createAggregate()` pour renvoyer un nouvel objet de la sous-classe ` QAxAggregated `.

class MyActiveX : public QWidget, public QAxBindable
{
    Q_OBJECT

public:
    MyActiveX(QWidget *parent);

    QAxAggregated *createAggregate()
    {
        return new AxImpl();
    }
};

Voir également le framework ActiveQt.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.