Erstellen von ActiveX-Servern in Qt
Das Modul QAxServer ist Teil des ActiveQt-Frameworks. Es besteht aus drei Klassen:
- QAxFactory definiert eine Factory für die Erstellung von COM-Objekten.
- QAxBindable bietet eine Schnittstelle zwischen dem Qt-Widget und dem COM-Objekt.
- QAxAggregated kann subclassed werden, um zusätzliche COM-Schnittstellen zu implementieren.
Es werden einige Beispielimplementierungen von ActiveX-Steuerelementen und COM-Objekten bereitgestellt.
Verwendung der Bibliothek
Um eine Standard-Qt-Anwendung mit Hilfe der QAxServer Bibliothek in einen COM-Server zu verwandeln, müssen Sie axserver
zur QT-Variable in Ihrer .pro
Datei hinzufügen.
Ein ausführbarer Server außerhalb des Prozesses wird aus einer .pro
Datei wie dieser erzeugt:
TEMPLATE = app QT += axserver RC_FILE = qaxserver.rc ...
Um einen prozessinternen Server zu erstellen, verwenden Sie eine .pro
Datei wie diese:
TEMPLATE = lib QT += axserver CONFIG += dll DEF_FILE = qaxserver.def RC_FILE = qaxserver.rc ...
Die Dateien qaxserver.rc
und qaxserver.def
sind Teil des Frameworks und können an ihrem üblichen Speicherort verwendet werden (geben Sie einen Pfad in der Datei .pro
an) oder in das Projektverzeichnis kopiert werden. Sie können diese Dateien ändern, solange sie eine beliebige Datei als Eintrag in der Typbibliothek enthalten, d. h. Sie können Versionsinformationen hinzufügen oder ein anderes Toolbox-Symbol angeben.
Die Verwendung des Moduls axserver
veranlasst das Tool qmake
, die erforderlichen Build-Schritte zum Build-System hinzuzufügen:
- Linken Sie die Binärdatei mit
qaxserver.lib
anstatt mitqtmain.lib
- Aufrufen des idc-Tools zur Erzeugung einer IDL-Datei für den COM-Server
- Kompilieren der IDL in eine Typbibliothek mit dem MIDL-Tool (Teil der Compiler-Installation)
- Hängen Sie die resultierende Typbibliothek als binäre Ressource an die Server-Binärdatei an (wiederum mit dem idc-Tool )
- Registrieren Sie den Server. Dieser Schritt erfordert möglicherweise administrative Rechte und kann durch Einstellen der Konfiguration
qaxserver_no_register
übersprungen werden.
Um den Nachbearbeitungsschritt zu überspringen, setzen Sie auch die Konfiguration qaxserver_no_postlink
.
Zusätzlich können Sie eine Versionsnummer über die Variable VERSION
angeben, z. B.
TEMPLATE = lib VERSION = 2.5 ...
Die angegebene Versionsnummer wird bei der Registrierung als Version der Typbibliothek und des Servers verwendet.
Out-of-Process vs. In-Process
Ob Ihr COM-Server als eigenständige ausführbare Datei oder als gemeinsam genutzte Bibliothek im Clientprozess ausgeführt werden soll, hängt hauptsächlich von der Art der COM-Objekte ab, die Sie im Server bereitstellen möchten.
Ein ausführbarer Server hat den Vorteil, dass er als eigenständige Anwendung ausgeführt werden kann, fügt jedoch erheblichen Overhead für die Kommunikation zwischen dem COM-Client und dem COM-Objekt hinzu. Wenn das Steuerelement einen Programmierfehler hat, stürzt nur der Serverprozess ab, auf dem das Steuerelement ausgeführt wird, und die Clientanwendung wird wahrscheinlich weiter ausgeführt. Nicht alle COM-Clients unterstützen ausführbare Server.
Ein prozessinterner Server ist normalerweise kleiner und hat eine schnellere Startzeit. Die Kommunikation zwischen Client und Server erfolgt direkt über virtuelle Funktionsaufrufe und verursacht nicht den für Remoteprozeduraufrufe erforderlichen Overhead. Wenn der Server jedoch abstürzt, stürzt wahrscheinlich auch die Client-Anwendung ab, und nicht alle Funktionen sind für prozessinterne Server verfügbar (z. B. Registrierung in der COM-Tabelle für laufende Objekte).
Beide Servertypen können Qt entweder als Shared Library verwenden oder statisch in die Server-Binärdatei gelinkt werden.
Typische Fehler während der Post-Build-Schritte
Damit die ActiveQt-spezifischen Nachbearbeitungsschritte funktionieren, muss der Server einige Anforderungen erfüllen:
- Alle exponierten Steuerelemente können erstellt werden, ohne dass eine QApplication Instanz vorhanden ist.
- Die anfängliche Verknüpfung des Servers umfasst eine temporäre Typbibliothek-Ressource
- Alle Abhängigkeiten, die für die Ausführung des Servers erforderlich sind, befinden sich im Systempfad (oder im Pfad, der von der aufrufenden Umgebung verwendet wird; beachten Sie, dass Visual Studio über einen eigenen Satz von Umgebungsvariablen verfügt, die im Dialogfeld Tools|Options|Directories aufgeführt sind).
Wenn diese Anforderungen nicht erfüllt sind, treten wahrscheinlich einer oder mehrere der folgenden Fehler auf:
Die Server-Ausführbarkeit stürzt ab
Um die IDL zu generieren, müssen die als ActiveX-Steuerelemente dargestellten Widgets instanziiert werden (der Konstruktor wird aufgerufen). Zu diesem Zeitpunkt existiert nichts anderes als ein QApplication Objekt. Ihr Widget-Konstruktor darf sich nicht darauf verlassen, dass andere Objekte erstellt werden, z. B. sollte er auf Null-Zeiger prüfen.
Um Ihren Server zu debuggen, führen Sie ihn mit -dumpidl outputfile aus und überprüfen Sie, wo er abstürzt.
Beachten Sie, dass keine Funktionen des Controls aufgerufen werden.
Die Server-Executable ist keine gültige Win32-Anwendung
Das Anhängen der Typbibliothek hat die Server-Binärdatei beschädigt. Dies ist ein Fehler in Windows und tritt nur bei Release-Builds auf.
Der erste Linking-Schritt muss eine Dummy-Typbibliothek in die ausführbare Datei linken, die später durch idc ersetzt werden kann. Fügen Sie eine Ressourcendatei mit einer Typbibliothek zu Ihrem Projekt hinzu, wie in den Beispielen gezeigt.
"Unable to locate DLL"
Das Build-System muss die ausführbare Serverdatei ausführen, um die Schnittstellendefinition zu generieren und den Server zu registrieren. Wenn sich eine dynamische Link-Bibliothek, gegen die der Server linkt, nicht im Pfad befindet, kann dies fehlschlagen (z. B. ruft Visual Studio den Server unter Verwendung der in der Option "Verzeichnisse" angegebenen Umgebungseinstellungen auf). Vergewissern Sie sich, dass sich alle DLLs und Plugins, die Ihr Server benötigt, in einem Verzeichnis befinden, das im Pfad aufgeführt ist, wie er in der Fehlermeldung angegeben ist (siehe auch Das Windows Deployment Tool).
"Datei kann nicht geöffnet werden ..."
Der ActiveX-Server konnte nicht ordnungsgemäß heruntergefahren werden, als der letzte Client seine Verwendung beendet hat. Normalerweise dauert es etwa zwei Sekunden, bis die Anwendung beendet wird, aber es kann sein, dass Sie den Task-Manager verwenden müssen, um den Prozess zu beenden (z. B. wenn ein Client die Steuerelemente nicht richtig freigibt).
Das Steuerelement kann nicht instanziiert werden
In diesem Fall kann es helfen, den Server als Administrator zu registrieren.
Implementieren von Controls
Um ein COM-Objekt mit Qt zu implementieren, erstellen Sie eine Unterklasse von QObject oder einer beliebigen bestehenden QObject Unterklasse. Wenn die Klasse eine Unterklasse von QWidget ist, wird das COM-Objekt ein ActiveX-Steuerelement sein.
#include <QWidget> class MyActiveX : public QWidget { Q_OBJECT
Das Q_OBJECT Makro wird benötigt, um die Meta-Objekt-Informationen über das Widget an das ActiveQt-Framework zu übermitteln.
Q_CLASSINFO("ClassID", "{1D9928BD-4453-4bdd-903D-E525ED17FDE5}") Q_CLASSINFO("InterfaceID", "{99F6860E-2C5A-42ec-87F2-43396F4BE389}") Q_CLASSINFO("EventsID", "{0A3E9F27-E4F1-45bb-9E47-63099BCCD0E3}")
Verwenden Sie das Makro Q_CLASSINFO(), um die COM-Kennungen für das COM-Objekt anzugeben. ClassID
und InterfaceID
sind erforderlich, während EventsID
nur notwendig ist, wenn Ihr Objekt Signale hat. Um diese Bezeichner zu erzeugen, verwenden Sie Systemwerkzeuge wie uuidgen
oder guidgen
.
Sie können für jede Ihrer Klassen zusätzliche Attribute angeben; Einzelheiten finden Sie unter Klasseninformationen und Tuning.
Q_PROPERTY(int value READ value WRITE setValue)
Verwenden Sie das Makro Q_PROPERTY(), um Eigenschaften für das ActiveX-Steuerelement zu deklarieren.
Deklarieren Sie einen Standardkonstruktor, der ein übergeordnetes Objekt annimmt, sowie Funktionen, Signale und Slots wie für jede QObject Unterklasse.
public: MyActiveX(QWidget *parent = 0) ... int value() const; public slots: void setValue(int v); ... signals: void valueChange(int v); ... };
Das ActiveQt-Framework stellt Eigenschaften und öffentliche Slots als ActiveX-Eigenschaften und -Methoden und Signale als ActiveX-Ereignisse zur Verfügung und konvertiert zwischen den Qt-Datentypen und den entsprechenden COM-Datentypen.
Datentypen
Die Qt-Datentypen, die für Eigenschaften unterstützt werden, sind:
Qt-Datentyp | COM-Eigenschaft |
---|---|
bool | VARIANT_BOOL |
QString | BSTR |
int | int |
uint | int ohne Vorzeichen |
double | double |
qlonglong | CY |
qulonglong | CY |
QColor | OLE_COLOR |
QDate | DATE |
QDateTime | DATE |
QTime | DATE |
QFont | IFontDisp* |
QPixmap | IPictureDisp* |
QVariant | VARIANT |
QVariantList (dasselbe wie QList<QVariant>) | SAFEARRAY(VARIANT) |
QStringList | SAFEARRAY(BSTR) |
QByteArray | SAFEARRAY(BYTE) |
QRect | Benutzerdefinierter Typ |
QSize | Benutzerdefinierter Typ |
QPoint | Benutzerdefinierter Typ |
Die Qt-Datentypen, die für Parameter in Signalen und Slots unterstützt werden, sind:
Qt-Datentyp | COM-Parameter |
---|---|
bool | [in] VARIANT_BOOL |
bool& | [in, out] VARIANT_BOOL* |
QString, const QString& | [in] BSTR |
QString& | [in, out] BSTR* |
QString& | [in, out] BSTR* |
int | [in] int |
int& | [ein, aus] 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& | [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** |
QList<QVariant>, const QList<QVariant>& | [in] SAFEARRAY(VARIANT) |
QList<QVariant>& | [in, out] SAFEARRAY(VARIANT)* |
QStringList, const QStringList& | [in] SAFEARRAY(BSTR) |
QStringList& | [in, out] SAFEARRAY(BSTR)* |
QByteArray, const QByteArray& | [in] SAFEARRAY(BYTE) |
QByteArray& | [in, out] SAFEARRAY(BYTE)* |
QObject* | [in] IDispatch* |
QRect& | [in, out] struct QRect (benutzerdefiniert) |
QSize& | [in, out] struct QSize (benutzerdefiniert) |
QPoint& | [in, out] struct QPoint (benutzerdefiniert) |
Ebenfalls unterstützt werden exportierte Enums und Flags (siehe Q_ENUM() und Q_FLAG()). Die in-Parameter-Typen werden auch als Rückgabewerte unterstützt.
Eigenschaften und Signale/Slots mit Parametern, die andere Datentypen verwenden, werden vom ActiveQt-Framework ignoriert.
Unter-Objekte
COM-Objekte können mehrere Unterobjekte haben, die ein Unterelement des COM-Objekts darstellen können. Ein COM-Objekt, das eine Tabellenkalkulationsanwendung mit mehreren Dokumenten darstellt, kann beispielsweise ein Unterobjekt für jedes Tabellenblatt bereitstellen.
Jede QObject Unterklasse kann als Typ für ein Unterobjekt in ActiveX verwendet werden, solange sie dem QAxFactory bekannt ist. Dann kann der Typ in Eigenschaften oder als Rückgabetyp oder Parameter eines Slots verwendet werden.
Eigenschaftsbenachrichtigung
Um die Eigenschaften für den ActiveX-Client bindungsfähig zu machen, verwenden Sie Mehrfachvererbung von der Klasse QAxBindable:
#include <QAxBindable> #include <QWidget> class MyActiveX : public QWidget, public QAxBindable { Q_OBJECT
Verwenden Sie bei der Implementierung der Eigenschaftsschreibfunktionen die Funktionen requestPropertyChange() und propertyChanged() der Klasse QAxBindable, damit ActiveX-Clients an die Steuerelementeigenschaften binden können.
Bedienen von Steuerelementen
Um einen COM-Server für das COM-System verfügbar zu machen, muss er in der Systemregistrierung mit fünf eindeutigen Bezeichnern registriert werden. Diese Kennungen werden von Tools wie guidgen
oder uuidgen
bereitgestellt. Die Registrierungsinformationen ermöglichen es COM, die Binärdatei zu lokalisieren, die ein angefordertes ActiveX-Steuerelement bereitstellt, Remoteprozeduraufrufe an das Steuerelement zu marshallieren und Typinformationen über die vom Steuerelement bereitgestellten Methoden und Eigenschaften zu lesen.
Um das COM-Objekt zu erstellen, wenn der Client es anfordert, muss der Server eine Implementierung von QAxFactory exportieren. Der einfachste Weg, dies zu tun, ist die Verwendung einer Reihe von Makros:
QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}", "{a8f21901-7ff7-4f6a-b939-789620c03d83}") QAXCLASS(MyWidget) QAXCLASS(MyWidget2) QAXTYPE(MySubType) QAXFACTORY_END()
Dadurch werden MyWidget
und MyWidget2
als COM-Objekte exportiert, die von COM-Clients erstellt werden können, und MySubType
wird als Typ registriert, der in Eigenschaften und Parametern von MyWidget
und MyWidget2
verwendet werden kann.
In QAxFactory class documentation wird erklärt, wie dieses Makro zu verwenden ist und wie man benutzerdefinierte Factories implementiert und verwendet.
Für ausführbare Server außerhalb des Prozesses können Sie eine main()-Funktion implementieren, um ein QApplication -Objekt zu instanziieren und die Ereignisschleife wie jede normale Qt-Anwendung zu starten. Standardmäßig wird die Anwendung als normale Qt-Anwendung gestartet, aber wenn Sie -activex
auf der Kommandozeile übergeben, wird sie als ActiveX-Server gestartet. Verwenden Sie QAxFactory::isServer(), um eine Standard-Anwendungsschnittstelle zu erstellen und auszuführen, oder um eine eigenständige Ausführung zu verhindern:
#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(); }
Dies ist jedoch nicht notwendig, da ActiveQt eine Standardimplementierung einer Hauptfunktion bereitstellt. Die Standardimplementierung ruft QAxFactory::startServer() auf, erstellt eine QApplication Instanz und ruft exec() auf.
Um die ausführbare ActiveX-Server-Datei zu erstellen, führen Sie qmake
aus, um das Makefile zu erzeugen, und verwenden Sie das Make-Tool Ihres Compilers wie für jede andere Qt-Anwendung. Der make-Prozess wird auch die Steuerelemente in der Systemregistrierung registrieren, indem er die resultierende ausführbare Datei mit der -regserver
Kommandozeilenoption aufruft.
Wenn der ActiveX Server eine ausführbare Datei ist, werden die folgenden Kommandozeilenoptionen unterstützt:
Option | Ergebnis |
---|---|
-regserver | Registriert den Server in der Systemregistrierung |
-regserverperuser | Registriert den Server in der Systemregistrierung für den aktuellen Benutzer (seit 5.14) |
-unregserver | Deregistriert den Server aus der Systemregistrierung |
-unregserverperuser | Hebt die Registrierung des Servers aus der Systemregistrierung für den aktuellen Benutzer auf (seit 5.14) |
-activex | Startet die Anwendung als ActiveX-Server |
-dumpidl <file> -version x.y | Schreibt die IDL des Servers in die angegebene Datei. Die Typbibliothek wird die Version x.y haben. |
In-Process-Server können mit dem auf allen Windows-Systemen verfügbaren Tool regsvr32
registriert werden.
Typische Kompilierzeitprobleme
Die aufgelisteten Compiler-/Linker-Fehler basieren auf denen, die vom Microsoft Visual C++ 6.0 Compiler ausgegeben werden.
"Keine überladene Funktion benötigt 2 Parameter"
Wenn der Fehler in Code auftritt, der das QAXCLASS() oder QAXFACTORY_DEFAULT() Makro verwendet, hatte die Widgetklasse keinen Konstruktor, der von der Standardfabrik verwendet werden kann. Fügen Sie entweder einen Standard-Widget-Konstruktor hinzu oder implementieren Sie eine benutzerdefinierte Factory, die keinen Konstruktor benötigt.
Wenn der Fehler in Code auftritt, der das Makro QAXFACTORY_EXPORT() verwendet, hatte die Unterklasse QAxFactory keinen geeigneten Konstruktor. Stellen Sie einen öffentlichen Klassenkonstruktor wie
für Ihre Fabrikklasse.
"Syntaxfehler: Falsches Suffix bei Nummer"
Die eindeutigen Bezeichner wurden nicht als Strings an das QAXFACTORY_EXPORT(), QAXFACTORY_BEGIN() oder QAXFACTORY_DEFAULT() Makro übergeben.
"Unaufgelöstes externes Symbol _ucm_instantiate"
Der Server exportiert keine Implementierung von QAxFactory. Verwenden Sie das Makro QAXFACTORY_EXPORT() in einer der Implementierungsdateien des Projekts, um eine Fabrik zu instanziieren und zu exportieren, oder verwenden Sie das Makro QAXCLASS() oder QAXFACTORY_DEFAULT(), um die Standardfabrik zu verwenden.
"_ucm_initialize bereits definiert in ..."
Der Server exportiert mehr als eine Implementierung eines QAxFactory, oder exportiert dieselbe Implementierung zweimal. Wenn Sie die Standardfabrik verwenden, darf das Makro QAXFACTORY_BEGIN() oder QAXFACTORY_DEFAULT() nur einmal im Projekt verwendet werden. Verwenden Sie eine benutzerdefinierte QAxFactory Implementierung und das QAXFACTORY_EXPORT() Makro, wenn der Server mehrere ActiveX Controls bereitstellt.
Verteilen von QAxServer-Binärdateien
ActiveX-Server, die mit Qt geschrieben wurden, können Qt entweder als gemeinsam genutzte Bibliothek verwenden oder Qt statisch in die Binärdatei einbinden lassen. Beide Möglichkeiten erzeugen ziemlich große Pakete (entweder wird die Server-Binärdatei selbst groß, oder Sie müssen die Qt DLL mitliefern).
Installation von Stand-Alone-Servern
Wenn Ihr ActiveX-Server auch als eigenständige Anwendung laufen kann, führen Sie die ausführbare Datei des Servers mit dem Befehlszeilenparameter -regserver
aus, nachdem Sie die ausführbare Datei auf dem Zielsystem installiert haben. Danach sind die vom Server bereitgestellten Steuerelemente für ActiveX-Clients verfügbar.
Installation von In-Process-Servern
Wenn Ihr ActiveX-Server Teil eines Installationspakets ist, verwenden Sie das von Microsoft bereitgestellte Tool regsvr32
, um die Steuerelemente auf dem Zielsystem zu registrieren. Wenn dieses Tool nicht vorhanden ist, laden Sie die DLL in Ihren Installationsprozess, lösen Sie das Symbol DllRegisterServer
auf und rufen Sie die Funktion auf:
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
Verteilen von Servern über das Internet
Wenn Sie Steuerelemente Ihres Servers in Webseiten verwenden wollen, müssen Sie den Server dem Browser, mit dem Ihre Seite angezeigt wird, zur Verfügung stellen und den Speicherort des Serverpakets in Ihrer Seite angeben.
Um den Speicherort eines Servers anzugeben, verwenden Sie das CODEBASE-Attribut im OBJECT-Tag Ihrer Web-Site. Der Wert kann auf die Serverdatei selbst, auf eine INF-Datei, die andere vom Server benötigte Dateien auflistet (z. B. die Qt-DLL), oder auf ein komprimiertes CAB-Archiv verweisen.
INF- und CAB-Dateien sind in fast jedem Buch über ActiveX- und COM-Programmierung sowie in der MSDN-Bibliothek und verschiedenen anderen Online-Ressourcen dokumentiert. Zu den Beispielen gehören INF-Dateien, die zum Erstellen von CAB-Archiven verwendet werden können:
[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
Das CABARC-Tool von Microsoft kann auf einfache Weise CAB-Archive erzeugen:
cabarc N simpleax.cab simpleax.exe simple.inf
Die INF-Dateien gehen von einem statischen Build von Qt aus, so dass keine Abhängigkeiten zu anderen DLLs in den INF-Dateien aufgeführt sind. Um einen ActiveX-Server zu verteilen, der von DLLs abhängig ist, müssen Sie die Abhängigkeiten hinzufügen und die Bibliotheksdateien mit dem Archiv bereitstellen.
Verwendung der Steuerelemente
Um die ActiveX-Steuerelemente zu verwenden, z. B. um sie in eine Webseite einzubetten, verwenden Sie den <object>
HTML-Tag.
<object ID="MyActiveX1" CLASSID="CLSID:ad90301a-849e-4e8b-9a91-0a6dc5f6461f"> ... <\object>
Um die Eigenschaften des Steuerelements zu initialisieren, verwenden Sie
<object ID=...> <param name="name" value="value"> <\object>
Wenn der Webbrowser Scripting unterstützt, verwenden Sie JavaScript, VBScript und Formulare, um das Steuerelement zu skripten. Die ActiveQt-Beispiele enthalten HTML-Demoseiten für die Beispielsteuerelemente.
Unterstützte und nicht unterstützte ActiveX-Clients
Die folgenden Angaben beruhen größtenteils auf unseren eigenen Erfahrungen mit ActiveX-Steuerelementen und Client-Anwendungen und sind keineswegs vollständig.
Unterstützte Clients
Diese Standardanwendungen arbeiten mit ActiveX-Steuerelementen, die mit ActiveQt entwickelt wurden. Beachten Sie, dass einige Clients nur prozessinterne Steuerelemente unterstützen.
- Internet Explorer
- Microsoft ActiveX-Steuerelement-Testcontainer
- Microsoft Visual Studio 6.0
- Microsoft Visual Studio.NET/2003
- Microsoft Visual Basic 6.0
- MFC- und ATL-basierte Container
- Sybase PowerBuilder
- ActiveQt-basierte Container
Microsoft Office-Anwendungen werden unterstützt, aber Sie müssen die Steuerelemente als "einfügbare" Objekte registrieren. Reimplementieren Sie QAxFactory::registerClass, um dieses Attribut zur COM-Klasse hinzuzufügen, oder setzen Sie die Klasseninformation "Insertable" für Ihre Klasse mit dem Makro Q_CLASSINFO auf "yes".
Nicht unterstützte Clients
Es ist uns nicht gelungen, ActiveQt-basierte COM-Objekte mit den folgenden Client-Anwendungen zum Laufen zu bringen.
- Borland C++ Builder (Versionen 5 und 6)
- Borland Delphi
Typische Laufzeitfehler
Der Server antwortet nicht
Wenn das System nicht in der Lage ist, den Server zu starten (überprüfen Sie mit dem Task-Manager, ob der Server einen Prozess ausführt), stellen Sie sicher, dass keine DLL, von der der Server abhängt, im Systempfad fehlt (z. B. die Qt-DLL!). Verwenden Sie einen Dependency Walker, um alle Abhängigkeiten der Server-Binärdatei anzuzeigen.
Wenn der Server läuft (z.B. wenn der Task-Manager einen Prozess auflistet), finden Sie im folgenden Abschnitt Informationen zur Fehlersuche in Ihrem Server.
Das Objekt kann nicht erstellt werden
Wenn der Server während des Build-Prozesses korrekt erstellt und registriert werden konnte, das Objekt aber z.B. von der OLE/COM Object Viewer-Anwendung nicht initialisiert werden kann, stellen Sie sicher, dass keine DLL, von der der Server abhängt, im Systempfad fehlt (z.B. die Qt-DLL). Verwenden Sie einen Dependency Walker, um alle Abhängigkeiten der Server-Binärdatei anzuzeigen.
Wenn der Server läuft, finden Sie im folgenden Abschnitt Informationen zum Debuggen des Servers.
Abstürze beim Entladen und Neuladen von COM-Servern
Wenn ActiveQt COM-Server Qt-Module verwenden, die über die in Qt Base enthaltenen hinausgehen, ist es notwendig, den COM-Server als prozessunabhängigen COM-Server zu aktivieren. Der Versuch, einen prozessinternen COM-Server zu aktivieren, der Module wie Qt Quick enthält, kann nach dem Entladen des COM-Servers zu einem Absturz führen.
Absturz oder unerwartetes Verhalten bei ausgehenden COM-Aufrufen
Beachten Sie, dass ein COM-Server außerhalb des Prozesses seine Nachrichtenwarteschlange verarbeitet, während er einen ausgehenden Aufruf an den Client durchführt. Dies kann zu unerwartetem Verhalten oder einem Absturz führen, wenn der Client gleichzeitig den Server aufruft. In diesem Fall wird der eingehende Aufruf im Server ausgeführt, bevor der ausgehende Aufruf zurückkehrt. Insbesondere, wenn der Client ein ActiveX-Steuerelement schließt, während das Steuerelement den Client zurückruft, kann dies zu einem Absturz führen. Solche Wiederholungsprobleme können durch Nachrichtenfilter (IMessageFilter und CoRegisterMessageFilter) entschärft werden.
Debuggen von Laufzeitfehlern
Um einen prozessinternen Server in Visual Studio zu debuggen, legen Sie das Serverprojekt als aktives Projekt fest und geben Sie in den Projekteinstellungen eine "ausführbare Datei für die Debug-Sitzung" an (verwenden Sie z. B. den ActiveX Test Container). Sie können Haltepunkte in Ihrem Code setzen und auch in ActiveQt- und Qt-Code einsteigen, wenn Sie die Debug-Version installiert haben.
Um einen ausführbaren Server zu debuggen, führen Sie die Anwendung in einem Debugger aus und starten Sie mit dem Befehlszeilenparameter -activex
. Starten Sie dann Ihren Client und erstellen Sie eine Instanz Ihres ActiveX-Steuerelements. COM wird den vorhandenen Prozess für den nächsten Client verwenden, der versucht, ein ActiveX-Steuerelement zu erstellen.
Klasseninformationen und Tuning
Um Attribute für jede COM-Klasse bereitzustellen, verwenden Sie das Makro Q_CLASSINFO, das Teil des Meta-Objektsystems von Qt ist.
Schlüssel | Bedeutung des Wertes |
---|---|
Version | Die Version der Klasse (1.0 ist Standard) |
Beschreibung | Eine Zeichenkette, die die Klasse beschreibt. |
KlasseID | Die ID der Klasse. Falls nicht angegeben, müssen Sie QAxFactory::classID neu implementieren. |
SchnittstellenID | Die ID der Schnittstelle. Falls nicht angegeben, müssen Sie QAxFactory::interfaceID neu implementieren. |
EreignisseID | Die ID der Ereignisschnittstelle. Wenn nicht angegeben, werden keine Signale als COM-Ereignisse angezeigt. |
DefaultProperty | Die angegebene Eigenschaft stellt die Standardeigenschaft dieser Klasse dar. D.h. die Standardeigenschaft einer Drucktaste wäre "Text". |
StandardSignal | Das angegebene Signal stellt das Standardsignal dieser Klasse dar. D.h. das Standardsignal einer Drucktaste ist "clicked". |
LizenzSchlüssel | Die Objekterstellung erfordert den angegebenen Lizenzschlüssel. Der Schlüssel kann leer sein, um einen lizenzierten Rechner zu verlangen. Standardmäßig sind Klassen nicht lizenziert. Siehe auch den folgenden Abschnitt. |
Lagerereignisse | Objekte zeigen Stock-Events an, wenn der Wert "yes" ist. Siehe QAxFactory::hasStockEvents() |
ToSuperClass | Objekte zeigen die Funktionalität aller Superklassen bis einschließlich des Klassennamens im Wert an. Siehe QAxFactory::exposeToSuperClass() |
Einfügbar | Wenn der Wert "yes" ist, ist die Klasse als "einfügbar" registriert und wird in OLE 2-Containern (z. B. Microsoft Office) aufgeführt. Dieses Attribut ist standardmäßig nicht gesetzt. |
Aggregierbar | Wenn der Wert "nein" ist, unterstützt die Klasse keine Aggregation. Standardmäßig wird die Aggregation unterstützt. |
Erstellbar | Wenn der Wert "nein" ist, kann die Klasse nicht vom Client erstellt werden und ist nur über die API einer anderen Klasse verfügbar (d. h. die Klasse ist ein Subtyp). |
RegisterObject | Wenn der Wert "ja" ist, werden Objekte dieser Klasse bei OLE registriert und sind über die Tabelle der laufenden Objekte zugänglich (d. h., Clients können eine Verbindung zu einer bereits laufenden Instanz dieser Klasse herstellen). Dieses Attribut wird nur in Out-of-Process-Servern unterstützt. |
MIME | Das Objekt kann Daten und Dateien in dem im Wert angegebenen Format verarbeiten. Der Wert hat das Format mime:extension:description. Mehrere Formate werden durch ein Semikolon getrennt. |
CoClassAlias | Der Klassenname, der in der generierten IDL und in der Registrierung verwendet wird. Dies ist besonders nützlich für C++-Klassen, die in einem Namespace liegen - standardmäßig entfernt ActiveQt einfach das "::", damit die IDL kompiliert werden kann. |
Implementierte Kategorien | Liste von kommagetrennten Kategorie-IDs (CATID) UUIDs. Generischer Mechanismus zur Angabe zusätzlicher Container-Fähigkeiten, zusätzlich zu "control", "insertable" usw. Typische CATIDs sind CATID_InternetAware ("{0DE86A58-2BAA-11CF-A229-00AA003D7352}"), CATID_SafeForScripting ("{7DD95801-9882-11CF-9FA9-00AA006C42C4}") sowie benutzerdefinierte CATID-Werte. |
Beachten Sie, dass sowohl bei den Schlüsseln als auch bei den Werten zwischen Groß- und Kleinschreibung unterschieden wird.
Im Folgenden wird die Version 2.0 einer Klasse deklariert, die nur ihre eigene API offenlegt und im Dialogfeld "Objekte einfügen" von Microsoft Office-Anwendungen verfügbar ist.
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); ... };
Entwickeln von lizenzierten Komponenten
Wenn Sie Komponenten entwickeln, möchten Sie vielleicht kontrollieren, wer diese Komponenten instanziieren kann. Da die Server-Binärdatei an jeden beliebigen Client-Rechner ausgeliefert und dort registriert werden kann, ist es jedem möglich, diese Komponenten in seiner eigenen Software zu verwenden.
Die Lizenzierung von Komponenten kann mit verschiedenen Techniken erfolgen, z.B. kann der Code, der das Steuerelement erzeugt, einen Lizenzschlüssel bereitstellen, oder der Rechner, auf dem das Steuerelement laufen soll, muss lizenziert werden.
Um eine Qt-Klasse als lizenziert zu kennzeichnen, geben Sie mit dem Makro Q_CLASSINFO() einen "LicenseKey" an.
class MyLicensedControl : public QWidget { Q_OBJECT Q_CLASSINFO("LicenseKey", "<key string>") ... };
Der Schlüssel wird benötigt, um eine Instanz von MyLicensedControl
auf einem Rechner erstellen zu können, der selbst nicht lizenziert ist. Der lizenzierte Entwickler kann nun die Server-Binärdatei mit seiner Anwendung weiterverteilen, die das Steuerelement mit dem Wert von "LicenseKey" erstellt, während Benutzer der Anwendung das Steuerelement nicht ohne den Lizenzschlüssel erstellen können.
Wenn ein einziger Lizenzschlüssel für das Steuerelement nicht ausreicht (d. h. Sie möchten, dass verschiedene Entwickler unterschiedliche Lizenzschlüssel erhalten), können Sie einen leeren Schlüssel angeben, um anzuzeigen, dass das Steuerelement eine Lizenz benötigt, und QAxFactory::validateLicenseKey() neu implementieren, um zu überprüfen, ob eine Lizenz auf dem System vorhanden ist (z. B. durch eine Lizenzdatei).
Weitere Schnittstellen
ActiveX-Steuerelemente, die von ActiveQt-Servern bereitgestellt werden, unterstützen einen minimalen Satz von COM-Schnittstellen zur Implementierung der OLE-Spezifikationen. Wenn die ActiveX-Klasse von der Klasse QAxBindable erbt, kann sie auch zusätzliche COM-Schnittstellen implementieren.
Erstellen Sie eine neue Unterklasse von QAxAggregated und verwenden Sie die Mehrfachvererbung, um weitere COM-Schnittstellenklassen zu unterklassifizieren.
class AxImpl : public QAxAggregated, public ISomeCOMInterface { public: AxImpl() {} long queryInterface(const QUuid &iid, void **iface); // IUnknown QAXAGG_IUNKNOWN // ISomeCOMInterface ... }
Reimplementieren Sie die Funktion QAxAggregated::queryInterface(), um die zusätzlichen COM-Schnittstellen zu unterstützen.
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; }
Da ISomeCOMInterface
eine Unterklasse von IUnknown
ist, müssen Sie die Funktionen QueryInterface()
, AddRef()
, und Release()
implementieren. Verwenden Sie dazu das Makro QAXAGG_IUNKNOWN in Ihrer Klassendefinition. Wenn Sie die IUnknown
Funktionen manuell implementieren, delegieren Sie die Aufrufe an den Schnittstellenzeiger, der von der QAxAggregated::controllingUnknown() Funktion zurückgegeben wird, z.B.
HRESULT AxImpl::QueryInterface(REFIID iid, void **iface) { return controllingUnknown()->QueryInterface(iid, iface); }
Unterstützen Sie die Schnittstelle IUnknown
selbst nicht in Ihrer queryInterface()-Implementierung.
Implementieren Sie die Methoden der COM-Schnittstellen, und verwenden Sie QAxAggregated::object(), wenn Sie Aufrufe an die QObject -Unterklasse, die das Steuerelement implementiert, tätigen müssen.
Implementieren Sie in Ihrer QAxBindable Unterklasse QAxBindable::createAggregate(), um ein neues Objekt der QAxAggregated Unterklasse zurückzugeben.
class MyActiveX : public QWidget, public QAxBindable { Q_OBJECT public: MyActiveX(QWidget *parent); QAxAggregated *createAggregate() { return new AxImpl(); } };
Siehe auch ActiveQt Framework.
© 2025 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.