Erstellen von ActiveX-Servern in Qt
Das Modul „ QAxServer “ ist Teil des ActiveQt-Frameworks. Es besteht aus drei Klassen:
- QAxFactory definiert eine Factory zur Erstellung von COM-Objekten.
- QAxBindable stellt eine Schnittstelle zwischen dem Qt-Widget und dem COM-Objekt bereit.
- QAxAggregated kann unterklassifiziert 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 mithilfe der Bibliothek „ QAxServer “ in einen COM-Server umzuwandeln, müssen Sie in Ihrer Datei „ .pro “ den Eintrag „ axserver “ zur Variablen „QT“ hinzufügen.
Aus einer „ .pro “-Datei wie dieser wird ein prozessexterner ausführbarer Server generiert:
TEMPLATE = app
QT += axserver
RC_FILE = qaxserver.rc
...Um einen In-Process-Server zu erstellen, verwenden Sie eine „ .pro “-Datei wie folgt:
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 entweder von ihrem üblichen Speicherort aus 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 Typbibliotheks-Eintrag enthalten, d. h. Sie können Versionsinformationen hinzufügen oder ein anderes Toolbox-Symbol angeben.
Durch die Verwendung des Moduls „ axserver “ fügt das Tool „ qmake “ die erforderlichen Build-Schritte zum Build-System hinzu:
- Verknüpfen Sie die Binärdatei mit „
qaxserver.lib“ anstelle vonqtmain.lib - Rufen Sie das idc -Tool auf, um eine IDL-Datei für den COM-Server zu generieren
- Kompilieren Sie die IDL-Datei mit dem MIDL-Tool (Bestandteil der Compiler-Installation) zu einer Typbibliothek
- Fügen Sie die resultierende Typbibliothek als Binärressource an die Server-Binärdatei an (wiederum mithilfe des idc -Tools)
- Registrieren Sie den Server. Für diesen Schritt sind möglicherweise Administratorrechte erforderlich; er kann durch Setzen der Konfiguration „
qaxserver_no_register“ übersprungen werden.
Um den Nachbearbeitungsschritt zu überspringen, setzen Sie ebenfalls die Konfiguration „ qaxserver_no_postlink “.
Zusätzlich können Sie mithilfe der Variablen „ VERSION “ eine Versionsnummer 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 Client-Prozess ausgeführt werden soll, hängt hauptsächlich von der Art der COM-Objekte ab, die Sie auf dem Server bereitstellen möchten.
Ein ausführbarer Server hat den Vorteil, dass er als eigenständige Anwendung ausgeführt werden kann, verursacht jedoch einen erheblichen Mehraufwand bei der Kommunikation zwischen dem COM-Client und dem COM-Objekt. Wenn das Steuerelement einen Programmierfehler aufweist, stürzt nur der Serverprozess ab, der das Steuerelement ausführt, während die Client-Anwendung wahrscheinlich weiterläuft. Nicht alle COM-Clients unterstützen ausführbare Server.
Ein In-Process-Server ist in der Regel kleiner und hat eine kürzere Startzeit. Die Kommunikation zwischen Client und Server erfolgt direkt über virtuelle Funktionsaufrufe und verursacht keinen der für Remote-Prozeduraufrufe erforderlichen Mehraufwand. Stürzt der Server jedoch ab, stürzt wahrscheinlich auch die Client-Anwendung ab, und nicht alle Funktionen stehen für In-Process-Server zur Verfügung (z. B. die Registrierung in der Running-Object-Table der COM).
Beide Servertypen können Qt entweder als gemeinsam genutzte Bibliothek oder statisch in die Server-Binärdatei eingebunden nutzen.
Typische Fehler bei den Nachbearbeitungsschritten
Damit die ActiveQt-spezifischen Nachbearbeitungsschritte funktionieren, muss der Server bestimmte Voraussetzungen erfüllen:
- Alle bereitgestellten Steuerelemente können erstellt werden, ohne dass mehr als eine Instanz von ` QApplication ` vorhanden ist
- Die anfängliche Verknüpfung des Servers umfasst eine temporäre Typbibliotheksressource
- Alle für den Betrieb des Servers erforderlichen Abhängigkeiten befinden sich im Systempfad (oder im Pfad, der von der aufrufenden Umgebung verwendet wird; beachten Sie, dass Visual Studio über eigene Umgebungsvariablen verfügt, die im Dialogfeld „Extras | Optionen | Verzeichnisse“ aufgeführt sind).
Wenn diese Anforderungen nicht erfüllt sind, treten wahrscheinlich einer oder mehrere der folgenden Fehler auf:
Die ausführbare Serverdatei stürzt ab
Um die IDL zu generieren, müssen die als ActiveX-Steuerelemente bereitgestellten Widgets instanziiert werden (der Konstruktor wird aufgerufen). Zu diesem Zeitpunkt existiert nichts anderes als ein „ QApplication “-Objekt. Der Konstruktor Ihres Widgets darf nicht davon ausgehen, dass andere Objekte bereits erstellt wurden; er sollte beispielsweise auf Null-Zeiger prüfen.
Um Ihren Server zu debuggen, führen Sie ihn mit der Option `-dumpidl outputfile` aus und überprüfen Sie, an welcher Stelle er abstürzt.
Beachten Sie, dass keine Funktionen des Steuerelements aufgerufen werden.
Die Server-Ausführungsdatei ist keine gültige Win32-Anwendung
Das Einbinden der Typbibliothek hat die Server-Binärdatei beschädigt. Dies ist ein Fehler in Windows und tritt nur bei Release-Builds auf.
Im ersten Link-Schritt muss eine Dummy-Typbibliothek in die ausführbare Datei eingebunden werden, die später durch idc ersetzt werden kann. Fügen Sie Ihrem Projekt eine Ressourcendatei mit einer Typbibliothek hinzu, wie in den Beispielen gezeigt.
„DLL kann nicht gefunden werden“
Das Build-System muss die Server-Ausführungsdatei ausführen, um die Schnittstellendefinition zu generieren und den Server zu registrieren. Befindet sich eine dynamische Linkbibliothek, gegen die der Server verknüpft ist, nicht im Pfad, kann dies fehlschlagen (z. B. ruft Visual Studio den Server unter Verwendung der in der Option „Verzeichnisse“ angegebenen Umgebungseinstellungen auf). Stellen Sie sicher, dass sich alle von Ihrem Server benötigten DLLs und Plugins in einem Verzeichnis befinden, das im Pfad aufgeführt ist, wie er im Fehlermeldungsfeld angezeigt wird (siehe auch „Das Windows Deployment Tool“).
„Datei kann nicht geöffnet werden …“
Der ActiveX-Server konnte nicht ordnungsgemäß beendet werden, als der letzte Client die Nutzung einstellte. Normalerweise dauert es etwa zwei Sekunden, bis die Anwendung beendet ist, aber möglicherweise müssen Sie den Prozess über den Task-Manager beenden (z. B. wenn ein Client die Steuerelemente nicht ordnungsgemäß freigibt).
Das Steuerelement kann nicht instanziiert werden
In diesem Fall kann es helfen, den Server als Administrator anzumelden.
Implementierung von Steuerelementen
Um ein COM-Objekt mit Qt zu implementieren, erstellen Sie eine Unterklasse von ` QObject ` oder einer beliebigen bestehenden Unterklasse von ` QObject `. Wenn die Klasse eine Unterklasse von ` QWidget` ist, handelt es sich bei dem COM-Objekt um ein ActiveX-Steuerelement.
#include <QWidget>
class MyActiveX : public QWidget
{
Q_OBJECTDas Makro „ Q_OBJECT “ ist erforderlich, um dem ActiveQt-Framework die Metaobjektinformationen über das Widget bereitzustellen.
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-Bezeichner für das COM-Objekt anzugeben. „ ClassID “ und „ InterfaceID “ sind erforderlich, während „ EventsID “ nur notwendig ist, wenn Ihr Objekt Signale besitzt. Um diese Bezeichner zu generieren, verwenden Sie Systemtools wie „ uuidgen “ oder „ guidgen “.
Sie können für jede Ihrer Klassen zusätzliche Attribute angeben; weitere Informationen finden Sie unter „Klasseninformationen und Optimierung “.
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 entgegennimmt, sowie Funktionen, Signale und Slots wie bei jeder 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 sowie Signale als ActiveX-Ereignisse bereit und konvertiert zwischen den Qt-Datentypen und den entsprechenden COM-Datentypen.
Datentypen
Die folgenden Qt-Datentypen werden für Eigenschaften unterstützt:
| Qt-Datentyp | COM-Eigenschaft |
|---|---|
| bool | VARIANT_BOOL |
| QString | BSTR |
| int | int |
| uint | unsigned int |
| double | double |
| qlonglong | CY |
| qulonglong | CY |
| QColor | OLE_COLOR |
| QDate | DATE |
| QDateTime | DATE |
| QTime | DATE |
| QFont | IFontDisp* |
| QPixmap | IPictureDisp* |
| QVariant | VARIANT |
| QVariantList (entspricht QList<QVariant>) | SAFEARRAY(VARIANT) |
| QStringList | SAFEARRAY(BSTR) |
| QByteArray | SAFEARRAY(BYTE) |
| QRect | Benutzerdefinierter Typ |
| QSize | Benutzerdefinierter Typ |
| QPoint | Benutzerdefinierter Typ |
Die folgenden Qt-Datentypen werden für Parameter in Signalen und Slots unterstützt:
| 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& | [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* |
| 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 Typen der Eingabeparameter werden auch als Rückgabewerte unterstützt.
Eigenschaften und Signale/Slots, deren Parameter andere Datentypen verwenden, werden vom ActiveQt-Framework ignoriert.
Unterobjekte
COM-Objekte können mehrere Unterobjekte haben, die jeweils ein Unterelement des COM-Objekts darstellen können. Ein COM-Objekt, das eine Tabellenkalkulationsanwendung mit mehreren Dokumenten darstellt, kann beispielsweise für jede Tabelle ein Unterobjekt bereitstellen.
Jede Unterklasse von ` QObject ` kann in ActiveX als Typ für ein Unterobjekt verwendet werden, sofern sie dem ` QAxFactory` bekannt ist. Der Typ kann dann in Eigenschaften oder als Rückgabetyp bzw. Parameter eines Slots verwendet werden.
Eigenschaftsbenachrichtigung
Um die Eigenschaften für den ActiveX-Client bindbar zu machen, verwenden Sie Mehrfachvererbung von der Klasse ` QAxBindable `:
#include <QAxBindable>
#include <QWidget>
class MyActiveX : public QWidget, public QAxBindable
{
Q_OBJECTVerwenden Sie bei der Implementierung der Schreibfunktionen für Eigenschaften die Funktionen `requestPropertyChange()` und `propertyChanged()` der Klasse ` QAxBindable `, damit ActiveX-Clients eine Bindung an die Eigenschaften des Steuerelements herstellen können.
Bereitstellung von Steuerelementen
Um einen COM-Server für das COM-System verfügbar zu machen, muss er mithilfe von fünf eindeutigen Kennungen in der Systemregistrierung registriert werden. Diese Kennungen werden von Tools wie „ guidgen “ oder „ uuidgen “ bereitgestellt. Anhand der Registrierungsinformationen kann COM die Binärdatei lokalisieren, die ein angefordertes ActiveX-Steuerelement bereitstellt, Remote-Prozeduraufrufe an das Steuerelement weiterleiten und Typinformationen zu den vom Steuerelement bereitgestellten Methoden und Eigenschaften auslesen.
Um das COM-Objekt zu erstellen, wenn der Client es anfordert, muss der Server eine Implementierung von ` QAxFactory` exportieren. Am einfachsten geht dies mithilfe 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.
Im Dokument „ QAxFactory class documentation “ wird erläutert, wie dieses Makro verwendet wird und wie benutzerdefinierte Fabriken implementiert und genutzt werden.
Für prozessexterne ausführbare Server können Sie eine main()-Funktion implementieren, um ein QApplication -Objekt zu instanziieren und wie bei jeder normalen Qt-Anwendung in die Ereignisschleife einzutreten. Standardmäßig startet die Anwendung als normale Qt-Anwendung; wenn Sie jedoch „ -activex “ über die Befehlszeile ü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 erforderlich, da ActiveQt eine Standardimplementierung der main-Funktion bereitstellt. Die Standardimplementierung ruft ` QAxFactory::startServer()` auf, erstellt eine Instanz von ` QApplication ` und ruft `exec()` auf.
Um die ausführbare ActiveX-Server-Datei zu erstellen, führen Sie ` qmake ` aus, um die Makefile zu generieren, und verwenden Sie das Make-Tool Ihres Compilers wie bei jeder anderen Qt-Anwendung. Der Make-Prozess registriert die Steuerelemente außerdem in der Systemregistrierung, indem er die resultierende ausführbare Datei mit der Befehlszeilenoption ` -regserver ` aufruft.
Wenn es sich bei dem ActiveX-Server um eine ausführbare Datei handelt, werden die folgenden Befehlszeilenoptionen 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 | Löscht die Registrierung des Servers aus der Systemregistrierung |
-unregserverperuser | Löscht die Registrierung des Servers aus der Systemregistrierung für den aktuellen Benutzer (ab Version 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 hat die Version x.y |
In-Process-Server können mit dem auf allen Windows-Systemen verfügbaren Tool „ regsvr32 “ registriert werden.
Typische Probleme bei der Kompilierung
Die aufgeführten Compiler-/Linker-Fehler basieren auf den vom Microsoft Visual C++ 6.0-Compiler ausgegebenen Fehlern.
„Es gibt keine überladene Funktion mit 2 Parametern“
Wenn der Fehler in Code auftritt, der das Makro „ QAXCLASS()“ oder „QAXFACTORY_DEFAULT()“ verwendet, verfügt die Widget-Klasse über keinen Konstruktor, der von der Standard-Factory verwendet werden kann. Fügen Sie entweder einen Standard-Widget-Konstruktor hinzu oder implementieren Sie eine benutzerdefinierte Factory, die keinen solchen benötigt.
Tritt der Fehler in Code auf, der das Makro ` QAXFACTORY_EXPORT()` verwendet, verfügte die Unterklasse von ` QAxFactory ` über keinen geeigneten Konstruktor. Stellen Sie einen öffentlichen Klassenkonstruktor wie
für Ihre Factory-Klasse bereit.
„Syntaxfehler: ungültiges Suffix bei einer Zahl“
Die eindeutigen Bezeichner wurden nicht als Zeichenketten an die Makros ` QAXFACTORY_EXPORT()`, ` QAXFACTORY_BEGIN()` oder `QAXFACTORY_DEFAULT()` ü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 Factory zu instanziieren und zu exportieren, oder verwenden Sie das Makro ` QAXCLASS()` oder `QAXFACTORY_DEFAULT()`, um die Standard-Factory zu nutzen.
„_ucm_initialize bereits definiert in …“
Der Server exportiert mehr als eine Implementierung eines „ QAxFactory “ oder exportiert dieselbe Implementierung zweimal. Wenn Sie die Standard-Factory verwenden, darf das Makro „ QAXFACTORY_BEGIN()“ oder „QAXFACTORY_DEFAULT()“ im Projekt nur einmal verwendet werden. Verwenden Sie eine benutzerdefinierte „ QAxFactory “-Implementierung und das Makro „ QAXFACTORY_EXPORT()“, wenn der Server mehrere ActiveX-Steuerelemente bereitstellt.
Verteilung von QAxServer-Binärdateien
Mit Qt geschriebene ActiveX-Server können Qt entweder als gemeinsam genutzte Bibliothek verwenden oder Qt statisch in die Binärdatei einbinden. Beide Vorgehensweisen führen zu relativ großen Paketen (entweder wird die Server-Binärdatei selbst groß, oder Sie müssen die Qt-DLL mitliefern).
Installation von eigenständigen Servern
Wenn Ihr ActiveX-Server auch als eigenständige Anwendung ausgeführt werden kann, führen Sie die Server-Ausführungsdatei nach der Installation auf dem Zielsystem mit dem Befehlszeilenparameter „ -regserver “ aus. Danach stehen die vom Server bereitgestellten Steuerelemente den ActiveX-Clients zur Verfügung.
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. Falls 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 handlingBereitstellung von Servern über das Internet
Wenn Sie Steuerelemente Ihres Servers in Webseiten verwenden möchten, müssen Sie den Server für den Browser verfügbar machen, mit dem Ihre Seite angezeigt wird, und den Speicherort des Serverpakets in Ihrer Seite angeben.
Um den Speicherort eines Servers anzugeben, verwenden Sie das CODEBASE-Attribut im OBJECT-Tag Ihrer Webseite. Der Wert kann auf die Serverdatei selbst, auf eine INF-Datei, in der andere vom Server benötigte Dateien (z. B. die Qt-DLL) aufgelistet sind, oder auf ein komprimiertes CAB-Archiv verweisen.
INF- und CAB-Dateien werden in fast jedem verfügbaren Buch über ActiveX- und COM-Programmierung sowie in der MSDN-Bibliothek und verschiedenen anderen Online-Ressourcen behandelt. Die Beispiele enthalten 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=yesMit dem CABARC-Tool von Microsoft lassen sich CAB-Archive ganz einfach erstellen:
cabarc N simpleax.cab simpleax.exe simple.infDie INF-Dateien gehen von einer statischen Kompilierung von Qt aus, daher sind in den INF-Dateien keine Abhängigkeiten zu anderen DLLs aufgeführt. Um einen ActiveX-Server zu verteilen, der von DLLs abhängig ist, müssen Sie die Abhängigkeiten hinzufügen und die Bibliotheksdateien zusammen mit dem Archiv bereitstellen.
Verwendung der Steuerelemente
Um die ActiveX-Steuerelemente zu verwenden, z. B. um sie in eine Webseite einzubetten, verwenden Sie das HTML-Tag „ <object> “.
<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 Skripting unterstützt, verwenden Sie JavaScript, VBScript und Formulare, um das Steuerelement zu steuern. Die ActiveQt-Beispiele enthalten Demo-HTML-Seiten für die Beispiel-Steuerelemente.
Unterstützte und nicht unterstützte ActiveX-Clients
Die folgenden Angaben basieren größtenteils auf unseren eigenen Erfahrungen mit ActiveX-Steuerelementen und Client-Anwendungen und sind keineswegs vollständig.
Unterstützte Clients
Diese Standardanwendungen funktionieren mit ActiveX-Steuerelementen, die mit ActiveQt entwickelt wurden. Beachten Sie, dass einige Clients nur In-Process-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, allerdings müssen Sie die Steuerelemente als „Insertable“-Objekte registrieren. Implementieren Sie die Methode ` QAxFactory::registerClass ` neu, um dieses Attribut der COM-Klasse hinzuzufügen, oder setzen Sie die „Insertable“-Klasseninformation für Ihre Klasse mithilfe des Makros ` 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 reagiert nicht
Wenn das System den Server nicht starten kann (überprüfen Sie im 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 anzeigt), finden Sie im folgenden Abschnitt Informationen zur Fehlerbehebung bei Ihrem Server.
Das Objekt kann nicht erstellt werden
Wenn der Server während des Build-Prozesses korrekt erstellt und registriert werden konnte, das Objekt jedoch z. B. von der Anwendung „OLE/COM Object Viewer“ 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 zur Fehlerbehebung bei Ihrem Server.
Abstürze beim Entladen und erneuten Laden von COM-Servern
Wenn ActiveQt-COM-Server Qt-Module verwenden, die über die in Qt Base enthaltenen hinausgehen, muss der COM-Server als prozessunabhängiger COM-Server aktiviert werden. Der Versuch, einen prozessintegrierten 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 prozessexterner COM-Server seine Nachrichtenwarteschlange verarbeitet, während er einen ausgehenden Aufruf an den Client ausführt. Dies kann zu unerwartetem Verhalten oder einem Absturz führen, wenn der Client gleichzeitig den Server aufruft. In dieser Situation wird der eingehende Aufruf auf dem Server ausgeführt, bevor der ausgehende Aufruf zurückkehrt. Insbesondere wenn der Client ein ActiveX-Steuerelement schließt, während das Steuerelement gerade einen Rückruf an den Client ausführt, kann dies zu einem Absturz führen. Solche Reentrancy-Probleme lassen sich mithilfe von Nachrichtenfiltern (IMessageFilter und CoRegisterMessageFilter) abmildern.
Debuggen von Laufzeitfehlern
Um einen In-Process-Server in Visual Studio zu debuggen, legen Sie das Serverprojekt als aktives Projekt fest und geben Sie in den Projekteinstellungen eine Client-„ausführbare Datei für die Debug-Sitzung“ an (verwenden Sie z. B. den ActiveX-Test-Container). Sie können in Ihrem Code Haltepunkte setzen und bei installierter Debug-Version auch in den ActiveQt- und Qt-Code hineingehen.
Um einen ausführbaren Server zu debuggen, führen Sie die Anwendung in einem Debugger aus und starten Sie sie mit dem Befehlszeilenparameter „ -activex “. Starten Sie anschließend Ihren Client und erstellen Sie eine Instanz Ihres ActiveX-Steuerelements. COM verwendet den bestehenden Prozess für den nächsten Client, der versucht, ein ActiveX-Steuerelement zu erstellen.
Klasseninformationen und Optimierung
Um Attribute für jede COM-Klasse bereitzustellen, verwenden Sie das Makro „ Q_CLASSINFO “, das Teil des Metaobjektsystems von Qt ist.
| Schlüssel | Bedeutung des Werts |
|---|---|
| Version | Die Version der Klasse (Standardwert ist 1.0) |
| Beschreibung | Eine Zeichenkette, die die Klasse beschreibt. |
| ClassID | Die Klassen-ID. Sie müssen ` QAxFactory::classID ` neu implementieren, wenn diese nicht angegeben ist. |
| InterfaceID | Die Schnittstellen-ID. Sie müssen „ QAxFactory::interfaceID “ neu implementieren, wenn diese nicht angegeben ist. |
| EventsID | Die Ereignis-Schnittstellen-ID. Wenn keine Angabe erfolgt, werden keine Signale als COM-Ereignisse bereitgestellt. |
| Standard-Eigenschaft | Die angegebene Eigenschaft stellt die Standardeigenschaft dieser Klasse dar. D. h., die Standardeigenschaft einer Schaltfläche wäre „text“. |
| Standard-Signal | Das angegebene Signal stellt das Standardsignal dieser Klasse dar. Das Standardsignal einer Schaltfläche wäre beispielsweise „clicked“. |
| LicenseKey | Für die Objekterstellung ist der angegebene Lizenzschlüssel erforderlich. Der Schlüssel kann leer sein, um einen lizenzierten Rechner zu erfordern. Standardmäßig sind Klassen nicht lizenziert. Siehe auch den folgenden Abschnitt. |
| StockEvents | Objekte stellen Standardereignisse bereit, wenn der Wert „yes“ lautet. Siehe QAxFactory::hasStockEvents() |
| ToSuperClass | Objekte stellen die Funktionalität aller Oberklassen bis einschließlich des im Wert angegebenen Klassennamens bereit. Siehe QAxFactory::exposeToSuperClass() |
| Insertable | Wenn der Wert „yes“ lautet, ist die Klasse als „Insertable“ registriert und wird in OLE-2-Containern (z. B. Microsoft Office) aufgeführt. Dieses Attribut ist standardmäßig nicht gesetzt. |
| Aggregierbar | Wenn der Wert „no“ lautet, 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 Untertyp). |
| RegisterObject | Wenn der Wert „yes“ lautet, 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 im generierten IDL und in der Registrierung verwendet wird. Dies ist insbesondere für C++-Klassen nützlich, die in einem Namespace liegen – standardmäßig entfernt ActiveQt lediglich das „::“, damit das IDL kompiliert werden kann. |
| Implementierte Kategorien | Liste der durch Kommas getrennten Kategorie-IDs (CATID)-UUIDs. Generischer Mechanismus zur Angabe zusätzlicher Container-Fähigkeiten, zusätzlich zu „control“, „insertable“ usw. Typische CATIDs sind unter anderem „ CATID_InternetAware “ („{0DE86A58-2BAA-11CF-A229-00AA003D7352}“), „ CATID_SafeForScripting “ („{7DD95801-9882-11CF-9FA9-00AA006C42C4}“) sowie benutzerdefinierte CATID-Werte. |
Beachten Sie, dass bei Schlüsseln und Werten die Groß-/Kleinschreibung beachtet werden muss.
Im Folgenden wird Version 2.0 einer Klasse deklariert, die ausschließlich ihre eigene API bereitstellt 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);
...
};Entwicklung lizenzierter Komponenten
Wenn Sie Komponenten entwickeln, möchten Sie möglicherweise steuern, wer diese Komponenten instanziieren darf. Da die Server-Binärdatei auf jeden beliebigen Client-Rechner übertragen und dort registriert werden kann, ist es jedem möglich, diese Komponenten in seiner eigenen Software zu verwenden.
Die Lizenzierung von Komponenten kann mithilfe verschiedener Techniken erfolgen, z. B. kann der Code, der das Steuerelement erstellt, einen Lizenzschlüssel bereitstellen, oder der Rechner, auf dem das Steuerelement ausgeführt werden soll, muss lizenziert sein.
Um eine Qt-Klasse als lizenziert zu kennzeichnen, geben Sie mithilfe des Makros ` Q_CLASSINFO()` einen „LicenseKey“ an.
class MyLicensedControl : public QWidget
{
Q_OBJECT
Q_CLASSINFO("LicenseKey", "<key string>")
...
};Der Schlüssel ist erforderlich, 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 zusammen mit seiner Anwendung weiterverbreiten; diese erstellt das Steuerelement unter Verwendung des Werts von „LicenseKey“, während Nutzer der Anwendung das Steuerelement ohne den Lizenzschlüssel nicht erstellen können.
Falls 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 zu signalisieren, dass das Steuerelement eine Lizenz benötigt, und die Methode ` QAxFactory::validateLicenseKey()` neu implementieren, um zu überprüfen, ob auf dem System eine Lizenz vorhanden ist (z. B. über eine Lizenzdatei).
Weitere Schnittstellen
Von ActiveQt-Servern bereitgestellte ActiveX-Steuerelemente unterstützen einen minimalen Satz an COM-Schnittstellen zur Umsetzung 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 nutzen Sie Mehrfachvererbung, um weitere COM-Schnittstellenklassen zu unterklassen.
class AxImpl : public QAxAggregated, public ISomeCOMInterface
{
public:
AxImpl() {}
long queryInterface(const QUuid &iid, void **iface);
// IUnknown
QAXAGG_IUNKNOWN
// ISomeCOMInterface
...
}Implementieren Sie die Funktion ` QAxAggregated::queryInterface()` neu, 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 Funktionen ` IUnknown ` manuell implementieren, delegieren Sie die Aufrufe an den von der Funktion ` QAxAggregated::controllingUnknown()` zurückgegebenen Schnittstellenzeiger, z. B.
HRESULT AxImpl::QueryInterface(REFIID iid, void **iface)
{
return controllingUnknown()->QueryInterface(iid, iface);
}Unterstützen Sie die Schnittstelle „ IUnknown “ selbst nicht in Ihrer Implementierung von „ queryInterface()“.
Implementieren Sie die Methoden der COM-Schnittstellen und verwenden Sie ` QAxAggregated::object()`, wenn Sie Aufrufe an die ` QObject `-Unterklasse ausführen müssen, die das Steuerelement implementiert.
Implementieren Sie in Ihrer ` QAxBindable `-Unterklasse die Methode ` QAxBindable::createAggregate()`, um ein neues Objekt der Unterklasse ` QAxAggregated ` zurückzugeben.
class MyActiveX : public QWidget, public QAxBindable
{
Q_OBJECT
public:
MyActiveX(QWidget *parent);
QAxAggregated *createAggregate()
{
return new AxImpl();
}
};Siehe auch 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.