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 mit qtmain.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-DatentypCOM-Eigenschaft
boolVARIANT_BOOL
QStringBSTR
intint
uintint ohne Vorzeichen
doubledouble
qlonglongCY
qulonglongCY
QColorOLE_COLOR
QDateDATE
QDateTimeDATE
QTimeDATE
QFontIFontDisp*
QPixmapIPictureDisp*
QVariantVARIANT
QVariantList (dasselbe wie QList<QVariant>)SAFEARRAY(VARIANT)
QStringListSAFEARRAY(BSTR)
QByteArraySAFEARRAY(BYTE)
QRectBenutzerdefinierter Typ
QSizeBenutzerdefinierter Typ
QPointBenutzerdefinierter Typ

Die Qt-Datentypen, die für Parameter in Signalen und Slots unterstützt werden, sind:

Qt-DatentypCOM-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:

OptionErgebnis
-regserverRegistriert den Server in der Systemregistrierung
-regserverperuserRegistriert den Server in der Systemregistrierung für den aktuellen Benutzer (seit 5.14)
-unregserverDeregistriert den Server aus der Systemregistrierung
-unregserverperuserHebt die Registrierung des Servers aus der Systemregistrierung für den aktuellen Benutzer auf (seit 5.14)
-activexStartet die Anwendung als ActiveX-Server
-dumpidl <file> -version x.ySchreibt 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

MyFactory(const QUuid &, const QUuid &);

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üsselBedeutung des Wertes
VersionDie Version der Klasse (1.0 ist Standard)
BeschreibungEine Zeichenkette, die die Klasse beschreibt.
KlasseIDDie ID der Klasse. Falls nicht angegeben, müssen Sie QAxFactory::classID neu implementieren.
SchnittstellenIDDie ID der Schnittstelle. Falls nicht angegeben, müssen Sie QAxFactory::interfaceID neu implementieren.
EreignisseIDDie ID der Ereignisschnittstelle. Wenn nicht angegeben, werden keine Signale als COM-Ereignisse angezeigt.
DefaultPropertyDie angegebene Eigenschaft stellt die Standardeigenschaft dieser Klasse dar. D.h. die Standardeigenschaft einer Drucktaste wäre "Text".
StandardSignalDas angegebene Signal stellt das Standardsignal dieser Klasse dar. D.h. das Standardsignal einer Drucktaste ist "clicked".
LizenzSchlüsselDie 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.
LagerereignisseObjekte zeigen Stock-Events an, wenn der Wert "yes" ist. Siehe QAxFactory::hasStockEvents()
ToSuperClassObjekte zeigen die Funktionalität aller Superklassen bis einschließlich des Klassennamens im Wert an. Siehe QAxFactory::exposeToSuperClass()
EinfügbarWenn 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.
AggregierbarWenn der Wert "nein" ist, unterstützt die Klasse keine Aggregation. Standardmäßig wird die Aggregation unterstützt.
ErstellbarWenn 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).
RegisterObjectWenn 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.
MIMEDas 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.
CoClassAliasDer 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 KategorienListe 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.