Construire des serveurs ActiveX dans Qt
Le module QAxServer fait partie du cadre ActiveQt. Il se compose de trois classes :
- QAxFactory définit une usine 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 de 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 construire un serveur in-process, utilisez un fichier .pro comme suit :
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 à partir de leur emplacement habituel (spécifiez un chemin dans le fichier .pro ), ou copiés dans le répertoire du projet. Vous pouvez modifier ces fichiers tant qu'ils incluent un fichier quelconque en tant qu'entrée de la bibliothèque de type, c'est-à-dire que vous pouvez ajouter des informations sur la version ou spécifier une icône de boîte à outils différente.
En utilisant le module axserver, l'outil qmake ajoutera les étapes de construction nécessaires au système de construction :
- Lier le binaire à
qaxserver.libau lieu deqtmain.lib - Appeler l'outil idc pour générer un fichier IDL pour le serveur COM
- Compiler l'IDL dans une bibliothèque de types à l'aide de l'outil MIDL (qui fait partie de l'installation du compilateur).
- Attachez la bibliothèque de types résultante en tant que ressource binaire au binaire du serveur (toujours à l'aide de l'outil idc ).
- Enregistrer le serveur. Cette étape peut nécessiter des privilèges administratifs et peut être ignorée en définissant la configuration
qaxserver_no_register.
Pour sauter l'étape de post-traitement, définissez également la configuration qaxserver_no_postlink.
En outre, 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.
Out-of-Process vs In-Process
La question de savoir si votre serveur COM doit être exécuté en tant qu'exécutable autonome ou en tant que bibliothèque partagée dans le 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 être exécuté en tant qu'application autonome, mais il ajoute des frais généraux considérables à 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 client continuera probablement à fonctionner. Tous les clients COM ne prennent pas en charge les serveurs exécutables.
Un serveur in-process est généralement plus petit et a un temps de démarrage plus rapide. La communication entre le client et le serveur se fait directement par le biais d'appels de fonctions virtuelles et n'introduit pas les frais généraux requis pour les appels de procédures à distance. Cependant, si le serveur tombe en panne, l'application cliente est susceptible de tomber en panne également, et toutes les fonctionnalités ne sont pas disponibles pour les serveurs in-process (par exemple, l'enregistrement dans la table des objets en cours d'exécution de COM).
Les deux types de serveurs peuvent utiliser Qt soit en tant que bibliothèque partagée, soit en tant que lien statique dans le binaire du serveur.
Erreurs typiques durant les étapes de post-construction
Pour que les étapes de post-traitement spécifiques à ActiveQt fonctionnent, le serveur doit répondre à certaines exigences :
- Tous les contrôles exposés peuvent être créés sans rien d'autre qu'une instance QApplication.
- La liaison initiale du serveur inclut une ressource de bibliothèque de type temporaire.
- Toutes les dépendances nécessaires à l'exécution du serveur se trouvent dans le chemin du système (ou dans le chemin utilisé par l'environnement d'appel ; notez que Visual Studio possè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 risquent de se produire :
L'exécutable du serveur se bloque
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, il n'existe rien d'autre qu'un objet QApplication. Le constructeur de votre widget ne doit pas dépendre de la création d'autres objets, par exemple, il doit vérifier les pointeurs nuls.
Pour déboguer votre serveur, exécutez-le avec -dumpidl outputfile et vérifiez où il se 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'attachement de la bibliothèque de type a corrompu le binaire du serveur. Il s'agit d'un bogue de Windows qui ne se produit qu'avec les versions de développement.
La première étape de l'édition de liens consiste à lier une bibliothèque de types factice à l'exécutable, qui pourra ensuite être remplacée par idc. Ajoutez un fichier de ressources avec une bibliothèque de types à votre projet comme démontré 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 de l'interface et pour enregistrer le serveur. Si une bibliothèque de liens dynamiques à laquelle le serveur est lié ne se trouve pas dans le chemin d'accès, cela peut échouer (par exemple, Visual Studio appelle le serveur en utilisant les paramètres d'environnement spécifiés dans l'option "Directories"). Assurez-vous que toutes les DLL et tous les plugins requis par votre serveur se trouvent dans un répertoire répertorié dans le chemin d'accès indiqué dans le message d'erreur (voir également l 'outil de déploiement Windows).
"Impossible d'ouvrir le fichier ..."
Le serveur ActiveX n'a pas pu se fermer correctement lorsque le dernier client a cessé de l'utiliser. Il faut généralement environ deux secondes pour que l'application se termine, mais il se peut que vous deviez utiliser le gestionnaire de tâches pour tuer le processus (par exemple, lorsqu'un client ne libère pas les contrôles correctement).
Le contrôle ne peut pas être instancié
Dans ce cas, il peut être utile d'enregistrer le serveur en tant qu'administrateur.
Mise en œuvre des contrôles
Pour mettre en œuvre un objet COM avec Qt XML, créez une sous-classe de QObject ou toute 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 cadre ActiveQt les méta-informations sur 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. ClassID et InterfaceID sont obligatoires, tandis que EventsID n'est nécessaire que si votre objet comporte 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 Class Information and Tuning 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 cadre ActiveQt exposera les propriétés et les emplacements 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 Qt | Propriété COM |
|---|---|
| bool | VARIANT_BOOL |
| QString | BSTR |
| int | int |
| uint | int non signé |
| double | double |
| qlonglong | CY |
| qulonglong | CY |
| QColor | OLE_COLOR |
| QDate | DATE |
| QDateTime | DATE |
| QTime | DATE |
| QFont | IFontDisp* |
| QPixmap | IPictureDisp* |
| QVariant | VARIANT |
| QVariantList (identique à QList<QVariant>) | SAFEARRAY(VARIANTE) |
| QStringList | SAFEARRAY(BSTR) |
| QByteArray | SAFEARRAY(BYTE) |
| QRect | Type défini par l'utilisateur |
| QSize | Type défini par l'utilisateur |
| QPoint | Type défini par l'utilisateur |
Les types de données Qt pris en charge pour les paramètres des signaux et des emplacements sont les suivants :
| Type de données Qt | Paramètre COM |
|---|---|
| bool | [in] VARIANT_BOOL |
| bool& | [in, out] VARIANT_BOOL*, const. |
| QStringconst QString& | [in] BSTR |
| QString& | [in, out] BSTR* |
| QString& | [in, out] BSTR* |
| int | [in] int |
| int& | [in,out] 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*, const & [in] OLE_COLOR & [in, out] OLE_COLOR |
| QDate, const QDate& | [in] DATE |
| QDate& | [in, out] DATE* |
| QDateTime, const QDateTime& | [in] DATE |
| QDateTime& | [in, out] DATE* |
| QFont, const QFont& | [in] IFontDisp* |
| QFont& | [in, out] IFontDisp** |
| QPixmap, const QPixmap& | [in] IPictureDisp* |
| QPixmap& | [in, out] IPictureDisp**, const |
| QList<QVariant>, const QList<QVariant>& | [in] SAFEARRAY(VARIANT) |
| QList<QVariant>& | [in, out] SAFEARRAY(VARIANT)*, const & [in, out] SAFEARRAY(VARIANT) < >& |
| QStringList, const QStringList& | [in] SAFEARRAY(BSTR) |
| QStringList& | [in, out] SAFEARRAY(BSTR)*, const & [in] SAFEARRAY(BSTR) & |
| QByteArray, const QByteArray& | [in] SAFEARRAY(BYTE) |
| QByteArray& | [in, out] SAFEARRAY(BYTE)*, const. |
| QObject* | [in] IDispatch* |
| QRect& | [in, out] struct QRect (défini par l'utilisateur) |
| QSize& | [in, out] struct QSize (défini par l'utilisateur) |
| QPoint& | [in, out] struct QPoint (défini par l'utilisateur) |
Les enums et les drapeaux exportés sont également pris en charge (voir Q_ENUM() et Q_FLAG()). Les types de paramètres sont également pris en charge en tant que valeurs de retour.
Les propriétés et les signaux/emplacements dont les paramètres utilisent d'autres types de données sont ignorés par le cadre ActiveQt.
Sous-objets
Les objets COM peuvent avoir plusieurs sous-objets qui peuvent représenter un sous-élément de l'objet COM. Un objet COM représentant une application de feuille de calcul multi-documents peut par exemple fournir un sous-objet pour chaque feuille de calcul.
Toute sous-classe de QObject peut être utilisée comme type pour un sous-objet dans ActiveX, à condition qu'elle soit connue de QAxFactory. Le type peut alors être utilisé dans les 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 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.
Servir les contrôles
Pour qu'un serveur COM soit disponible pour le système COM, il doit être enregistré dans le registre du système à l'aide de cinq identificateurs uniques. Ces identifiants sont fournis par des outils tels que guidgen ou uuidgen. Les informations d'enregistrement permettent à COM de localiser le binaire fournissant un contrôle ActiveX demandé, de marshaller les appels de procédure à distance vers le contrôle et de lire les informations de type sur les méthodes et les propriétés exposées par le contrôle.
Pour créer l'objet COM lorsque le client le demande, le serveur doit exporter une implémentation de QAxFactory. La manière la plus simple de le faire est d'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 exportera MyWidget et MyWidget2 en tant qu'objets COM pouvant être créés par les clients COM, et enregistrera MySubType en tant que type pouvant être utilisé dans les propriétés et les paramètres de MyWidget et MyWidget2.
Le site QAxFactory class documentation explique comment utiliser cette macro et comment mettre en œuvre et utiliser des usines personnalisées.
Pour les serveurs exécutables hors processus, vous pouvez implémenter une fonction main() pour instancier un objet QApplication et entrer dans la boucle d'événements comme n'importe quelle application Qt normale. Par défaut, l'application démarre en tant qu'application Qt standard, mais si vous indiquez -activex sur la 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(); }
Ceci n'est cependant pas nécessaire car ActiveQt fournit une implémentation par défaut d'une fonction principale. L'implémentation par défaut appelle QAxFactory::startServer(), crée une instance QApplication et appelle exec().
Pour construire l'exécutable du serveur ActiveX, lancez qmake pour générer le fichier makefile, et utilisez l'outil make de votre compilateur comme pour toute autre application Qt. Le processus make enregistrera également les contrôles dans le registre du 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 :
| Option | Résultat |
|---|---|
-regserver | Enregistre le serveur dans le registre du système |
-regserverperuser | Enregistre le serveur dans le registre système pour l'utilisateur actuel (depuis la version 5.14). |
-unregserver | Désinscrire le serveur du registre système |
-unregserverperuser | Désinscrit le serveur du registre système pour l'utilisateur actuel (depuis la version 5.14) |
-activex | Démarre 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 type aura la version x.y |
Les serveurs en cours de traitement peuvent être enregistrés à l'aide de l'outil regsvr32 disponible sur tous les systèmes Windows.
Problèmes typiques au moment de la compilation
Les erreurs de compilation et d'édition de liens répertoriées sont basées sur celles émises par le compilateur Microsoft Visual C++ 6.0.
"Aucune fonction surchargée ne prend 2 paramètres"
Lorsque l'erreur se produit dans un code qui utilise la macro QAXCLASS() ou QAXFACTORY_DEFAULT(), la classe de widget n'a pas de constructeur pouvant être utilisé par la fabrique par défaut. Ajoutez un constructeur de widget standard ou implémentez une fabrique personnalisée qui n'en a pas besoin.
Lorsque l'erreur se produit dans un code qui utilise la macro QAXFACTORY_EXPORT(), la sous-classe QAxFactory n'a pas de constructeur approprié. Fournissez un constructeur de classe public comme
pour votre classe de fabrique.
"Erreur de syntaxe : mauvais suffixe sur le nombre"
Les identifiants uniques n'ont pas été transmis sous forme de chaînes de caractères dans la macro QAXFACTORY_EXPORT(), QAXFACTORY_BEGIN() ou QAXFACTORY_DEFAULT().
"Symbole externe non résolu _ucm_instantiate"
Le serveur n'exporte pas d'implémentation de 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 already defined in ..."
Le serveur exporte plus d'une implémentation d'une QAxFactory, ou exporte la même implémentation deux fois. 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.
Distribuer les binaires de QAxServer
Les serveurs ActiveX écrits avec Qt peuvent utiliser Qt soit comme une bibliothèque partagée, soit avoir Qt lié statiquement dans le binaire. Les deux méthodes produisent des paquets assez volumineux (soit le binaire du serveur lui-même devient volumineux, soit vous devez expédier la DLL Qt).
Installation de serveurs autonomes
Lorsque votre serveur ActiveX peut également fonctionner comme une application autonome, exécutez 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 d'exécution
Lorsque votre serveur ActiveX fait partie d'un paquet 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 votre processus 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
Distribution de serveurs sur l'internet
Si vous souhaitez utiliser des contrôles de votre serveur dans des pages web, vous devez mettre le serveur à la disposition du navigateur utilisé pour afficher votre page et vous devez spécifier l'emplacement du paquet de serveurs 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 dont le serveur a besoin (par exemple, la DLL Qt), ou vers une archive CAB compressée.
Les fichiers INF et CAB sont documentés dans presque tous les livres disponibles sur la programmation ActiveX et COM, ainsi que dans la bibliothèque MSDN et diverses autres ressources en ligne. Les exemples incluent des fichiers INF qui peuvent être utilisés pour construire 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=yesL'outil CABARC de Microsoft peut facilement générer des archives CAB :
cabarc N simpleax.cab simpleax.exe simple.inf
Les fichiers INF supposent une compilation statique de Qt, de sorte qu'aucune dépendance à d'autres DLL n'est répertoriée dans les fichiers INF. Pour distribuer un serveur ActiveX dépendant de DLL, vous devez ajouter les 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 la balise
<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 d'exemple.
Clients ActiveX pris en charge et non pris en charge
La liste suivante est largement basée sur nos propres expériences avec les contrôles ActiveX et les applications clientes, et n'est en aucun cas exhaustive.
Clients supportés
Ces applications standard fonctionnent avec des contrôles ActiveX développés avec ActiveQt. Notez que certains clients ne supportent que les contrôles in-process.
- Internet Explorer
- Conteneur de test de contrôle 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 "insérables". Réimplémentez QAxFactory::registerClass pour ajouter cet attribut à la classe COM ou définissez l'information de classe "Insérable" pour votre classe sur "oui" à 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 typiques
Le serveur ne répond pas
Si le système n'est pas en mesure de démarrer le serveur (vérifiez avec le gestionnaire de tâches si le serveur exécute un processus), assurez-vous qu'aucune DLL dont dépend le serveur n'est absente du chemin d'accès au système (par exemple, la DLL Qt !). Utilisez un outil de recherche de dépendances pour afficher toutes les dépendances du binaire du serveur.
Si le serveur fonctionne (par exemple, le gestionnaire de tâches répertorie un processus), consultez la section suivante pour obtenir des informations sur le débogage de votre serveur.
L'objet ne peut pas être créé
Si le serveur a pu être construit et enregistré correctement au cours du processus de construction, 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 au système (par exemple, la DLL Qt). Utilisez un outil de recherche de 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.
Crashes lors du déchargement et du rechargement des serveurs COM
Si les serveurs COM Active Qt utilisent des modules Qt autres que ceux trouvés dans Qt Base, il est nécessaire d'activer le serveur COM en tant que serveur COM hors processus. Toute tentative d'activation d'un serveur COM in-process qui inclut des modules tels que Qt Quick peut entraîner un plantage après le déchargement du serveur COM.
Crash ou comportement inattendu lors d'appels COM sortants
Sachez qu'un serveur COM hors processus traite sa file d'attente de messages pendant qu'il effectue un appel sortant vers le client. Cela peut entraîner un comportement inattendu ou un plantage si le client appelle en même temps le serveur. Dans ce cas, l'appel entrant sera exécuté dans le serveur avant que l'appel sortant ne revienne. En particulier, si le client ferme un contrôle ActiveX alors que le contrôle 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 de traitement dans Visual Studio, définissez le projet de serveur comme projet actif et spécifiez un client "exécutable pour la session de débogage" dans les paramètres du projet (par exemple, utilisez le conteneur de test ActiveX). Vous pouvez définir des points d'arrêt dans votre code, et également accéder au 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 démarrez avec le paramètre de ligne de commande -activex. Démarrez ensuite votre client et créez une instance de votre contrôle ActiveX. COM utilisera le processus existant pour le prochain client qui essaiera de créer un contrôle ActiveX.
Informations sur les classes et réglage
Pour fournir des attributs à 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 |
|---|---|
| Version | Version de la classe (1.0 par défaut) |
| Description | Une chaîne de caractères décrivant la classe. |
| ClassID | L'identifiant de la classe. Vous devez réimplémenter QAxFactory::classID s'il n'est pas spécifié. |
| InterfaceID | L'identifiant de l'interface. Vous devez réimplémenter QAxFactory::interfaceID si ce n'est pas spécifié. |
| EventsID | L'identifiant de l'interface d'événement. Aucun signal n'est exposé en tant qu'événement COM s'il n'est pas spécifié. |
| DefaultProperty | La 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 poussoir serait "text". |
| DefaultSignal | Le signal spécifié représente le signal par défaut de cette classe. Par exemple, le signal par défaut d'un bouton poussoir serait "clicked". |
| LicenseKey | La 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 n'ont pas de licence. Voir également la section suivante. |
| StockEvents | Les objets exposent les événements boursiers si la valeur est "yes". Voir QAxFactory::hasStockEvents() |
| ToSuperClass | Les objets exposent les fonctionnalités de toutes les super-classes jusqu'au nom de classe inclus dans la valeur. Voir QAxFactory::exposeToSuperClass() |
| Insérable | Si la valeur est "yes", la classe est enregistrée comme "insérable" et sera répertoriée dans les conteneurs OLE 2 (c'est-à-dire Microsoft Office). Cet attribut n'est pas défini par défaut. |
| Agrégeable | Si 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éable | Si la valeur est "no", la classe ne peut pas être créée par le client et n'est disponible qu'à travers l'API d'une autre classe (c'est-à-dire que la classe est un sous-type). |
| RegisterObject | Si la valeur est "oui", les objets de cette classe sont enregistrés avec OLE et accessibles à partir de la table d'objets en cours d'exécution (c'est-à-dire que les clients peuvent se connecter à une instance déjà en cours d'exécution de cette classe). Cet attribut n'est pris en charge que par les serveurs hors processus. |
| MIME | L'objet peut traiter des données et des fichiers au format spécifié dans la valeur. La valeur a le format mime:extension:description. Les formats multiples sont séparés par un point-virgule. |
| CoClassAlias | Le nom de classe utilisé dans l'IDL généré et dans le registre. Ceci est particulièrement utile pour les classes C++ qui vivent dans un espace de noms - par défaut, ActiveQt supprime simplement le ": :" pour que l'IDL se compile. |
| Catégories implémentées | Liste d'UUIDs d'ID de catégorie (CATID) séparés par des virgules. Mécanisme générique permettant de spécifier des capacités de conteneur supplémentaires, en plus de "control", "insertable", etc. Les CATID typiques comprennent 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.
Ce qui suit 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 voudrez peut-être contrôler qui peut les instancier. Étant donné que le binaire du serveur peut être expédié et enregistré sur n'importe quelle machine cliente, n'importe qui peut utiliser ces composants dans son propre logiciel.
L'octroi de licences pour les composants peut se faire à l'aide d'une variété de 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 être licenciée.
Pour marquer une classe Qt XML comme étant sous licence, spécifiez une "LicenseKey" en utilisant la macro Q_CLASSINFO().
class MyLicensedControl : public QWidget { Q_OBJECT Q_CLASSINFO("LicenseKey", "<key string>") ... };
La clé est nécessaire pour pouvoir créer une instance de MyLicensedControl sur une machine qui n'est pas elle-même sous licence. Le développeur sous licence peut maintenant redistribuer le binaire du serveur avec son application, qui crée le contrôle en utilisant la valeur de la "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 n'est pas suffisante (c'est-à-dire si vous voulez que différents développeurs reçoivent des clés de licence différentes), vous pouvez spécifier une clé vide pour indiquer que le contrôle nécessite une licence, et réimplémenter QAxFactory::validateLicenseKey() pour vérifier qu'une licence existe sur le système (c'est-à-dire par le biais d'un fichier de licence).
Autres interfaces
Les contrôles ActiveX fournis par les serveurs ActiveQt prennent en charge un ensemble minimal d'interfaces COM pour mettre en œuvre les spécifications OLE. Lorsque la classe ActiveX hérite de la classe QAxBindable, elle peut également mettre en œuvre des interfaces COM supplémentaires.
Créez une nouvelle sous-classe de QAxAggregated et utilisez l'héritage multiple pour sous-classer d'autres classes d'interfaces COM.
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; }
Comme ISomeCOMInterface est 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 les fonctions IUnknown manuellement, déléguez les appels au pointeur d'interface renvoyé par la fonction QAxAggregated::controllingUnknown(), par ex.
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 queryInterface().
Implémentez les méthodes des interfaces COM et utilisez QAxAggregated::object() si vous devez faire des appels à la sous-classe QObject qui implémente le contrôle.
Dans votre sous-classe QAxBindable, implémentez 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 aussi ActiveQt Framework.
© 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.