COM App Beispiel (ActiveQt)

Das COM App Beispiel zeigt, wie man ActiveQt verwendet, um eine Qt-Anwendung zu entwickeln, die über COM automatisiert werden kann. Verschiedene QObject -basierte Klassen werden als COM-Objekte dargestellt, die mit der GUI der laufenden Qt-Anwendung kommunizieren. Die APIs dieser COM-Objekte wurden so gestaltet, dass sie den APIs von Standard-COM-Anwendungen, z.B. denen von Microsoft Office, ähneln.

class Application : public QObject
{
    Q_OBJECT

    Q_CLASSINFO("ClassID", "{b50a71db-c4a7-4551-8d14-49983566afee}")
    Q_CLASSINFO("InterfaceID", "{4a427759-16ef-4ed8-be79-59ffe5789042}")
    Q_CLASSINFO("RegisterObject", "yes")

    Q_PROPERTY(DocumentList* documents READ documents)
    Q_PROPERTY(QString id READ id)
    Q_PROPERTY(bool visible READ isVisible WRITE setVisible)

public:
    explicit Application(QObject *parent = nullptr);
    DocumentList *documents() const;

    QString id() const { return objectName(); }

    void setVisible(bool on);
    bool isVisible() const;

    QTabWidget *window() const { return m_ui.data(); }

public slots:
    void quit();

private:
    QScopedPointer <DocumentList> m_docs;
    QScopedPointer <QTabWidget> m_ui;
};

Die erste Klasse Application repräsentiert das Anwendungsobjekt. Sie stellt schreibgeschützte Eigenschaften documents und id zur Verfügung, um Zugriff auf die Liste der Dokumente und einen Bezeichner zu erhalten. Eine Lese-/Schreibeigenschaft visible steuert, ob die QTabWidget-basierte Benutzeroberfläche der Anwendung sichtbar sein soll, und ein Slot quit() beendet die Anwendung.

Das RegisterObject-Attribut wird gesetzt, um sicherzustellen, dass Instanzen dieser Klasse in der COM-Running Object Table (ROT) registriert sind - dies ermöglicht COM-Clients, sich mit einem bereits instanziierten COM-Objekt zu verbinden.

class DocumentList : public QObject
{
    Q_OBJECT

    Q_CLASSINFO("ClassID", "{496b761d-924b-4554-a18a-8f3704d2a9a6}")
    Q_CLASSINFO("InterfaceID", "{6c9e30e8-3ff6-4e6a-9edc-d219d074a148}")

    Q_PROPERTY(Application* application READ application)
    Q_PROPERTY(int count READ count)

public:
    explicit DocumentList(Application *application);

    int count() const;
    Application *application() const;

public slots:
    Document *addDocument();
    Document *item(int index) const;

private:
    QList<Document *> m_list;
};

Die Klasse DocumentList speichert eine Liste von Dokumenten. Sie stellt eine API zur Verfügung, um die Anzahl der Dokumente zu lesen, um auf jedes Dokument über den Index zuzugreifen und um ein neues Dokument zu erstellen. Die Eigenschaft application gibt das Stammobjekt zurück.

class Document : public QObject
{
    Q_OBJECT

    Q_CLASSINFO("ClassID", "{2b5775cd-72c2-43da-bc3b-b0e8d1e1c4f7}")
    Q_CLASSINFO("InterfaceID", "{2ce1761e-07a3-415c-bd11-0eab2c7283de}")

    Q_PROPERTY(Application *application READ application)
    Q_PROPERTY(QString title READ title WRITE setTitle)

public:
    explicit Document(DocumentList *list);
    virtual ~Document();

    Application *application() const;

    QString title() const;
    void setTitle(const QString &title);

private:
    QScopedPointer <QWidget> m_page;
};

Die Klasse Document schließlich repräsentiert ein Dokument in der Anwendung. Jedes Dokument wird durch eine Seite im Registerkarten-Widget der Anwendung dargestellt und hat einen Titel, der über die API des Dokuments gelesen und geschrieben werden kann. Die Eigenschaft application gibt wiederum das Stammobjekt zurück.

Document::Document(DocumentList *list)
: QObject(list)
{
    QTabWidget *tabs = list->application()->window();
    m_page.reset(new QWidget(tabs));
    m_page->setWindowTitle(tr("Unnamed"));
    tabs->addTab(m_page.data(), m_page->windowTitle());

    m_page->show();
}

Document::~Document() = default;

Application *Document::application() const
{
    return qobject_cast<DocumentList *>(parent())->application();
}

QString Document::title() const
{
    return m_page->windowTitle();
}

void Document::setTitle(const QString &t)
{
    m_page->setWindowTitle(t);

    QTabWidget *tabs = application()->window();
    int index = tabs->indexOf(m_page.data());
    tabs->setTabText(index, m_page->windowTitle());
}

Die Implementierung der Klasse Document erstellt eine neue Seite für das Registerkarten-Widget und verwendet den Titel dieser Seite für die Eigenschaft title. Die Seite wird gelöscht, wenn das Dokument gelöscht wird.

DocumentList::DocumentList(Application *application)
: QObject(application)
{
}

Application *DocumentList::application() const
{
    return qobject_cast<Application *>(parent());
}

int DocumentList::count() const
{
    return m_list.size();
}

Document *DocumentList::item(int index) const
{
    return m_list.value(index, nullptr);
}

Document *DocumentList::addDocument()
{
    Document *document = new Document(this);
    m_list.append(document);

    return document;
}

Die Implementierung von DocumentList ist sehr einfach.

Application::Application(QObject *parent)
: QObject(parent),
  m_ui(new QTabWidget),
  m_docs(new DocumentList(this))
{
    setObjectName(QStringLiteral("From QAxFactory"));
}

DocumentList *Application::documents() const
{
    return m_docs.data();
}

void Application::setVisible(bool on)
{
    m_ui->setVisible(on);
}

bool Application::isVisible() const
{
    return m_ui->isVisible();
}

void Application::quit()
{
    m_docs.reset();
    m_ui.reset();
    QTimer::singleShot(0 /*ms*/, qApp, &QCoreApplication::quit);
}

#include "main.moc"

Die Klasse Application initialisiert die Benutzeroberfläche im Konstruktor und zeigt und verbirgt sie in der Implementierung von setVisible(). Der Objektname (zugänglich über die Eigenschaft id ) wird auf "From QAxFactory " gesetzt, um anzuzeigen, dass dieses COM-Objekt von COM erstellt wurde. Beachten Sie, dass es keinen Destruktor gibt, der QTabWidget löschen würde - dies geschieht stattdessen im Slot quit(), bevor quit() durch einen Single-Shot-Timer aufgerufen wird, der notwendig ist, um sicherzustellen, dass der COM-Aufruf an den Slot vollständig ist.

QAXFACTORY_BEGIN("{edd3e836-f537-4c6f-be7d-6014c155cc7a}", "{b7da3de8-83bb-4bbe-9ab7-99a05819e201}")
   QAXCLASS(Application)
   QAXTYPE(Document)
   QAXTYPE(DocumentList)
QAXFACTORY_END()

Die Klassen werden vom Server mit Hilfe der QAxFactory Makros exportiert. Nur Application Objekte können von außen instanziiert werden - die anderen APIs können erst nach dem Zugriff auf die entsprechenden Objekte über die Application API verwendet werden.

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    app.setQuitOnLastWindowClosed(false);

    // started by COM - don't do anything
    if (QAxFactory::isServer())
        return app.exec();

    // started by user
    Application appobject;
    appobject.setObjectName(QStringLiteral("From Application"));

    QAxFactory::startServer();
    QAxFactory::registerActiveObject(&appobject);

    appobject.window()->setMinimumSize(300, 100);
    appobject.setVisible(true);

    QObject::connect(&app, &QGuiApplication::lastWindowClosed, &appobject, &Application::quit);

    return app.exec();
}

Die main()-Einstiegsfunktion erstellt eine QApplication und tritt nur in die Ereignisschleife ein, wenn die Anwendung von COM gestartet wurde. Wurde die Anwendung vom Benutzer gestartet, wird das Objekt Application erstellt und der Objektname auf "From Application" gesetzt. Dann wird der COM-Server gestartet, und das Anwendungsobjekt wird bei COM registriert. Es ist nun für COM-Clients über die clientspezifischen APIs zugänglich.

Das Beenden der Anwendung wird explizit gesteuert - wenn COM die Anwendung gestartet hat, muss der Client-Code quit() aufrufen; wenn der Benutzer die Anwendung gestartet hat, wird die Anwendung beendet, wenn das letzte Fenster geschlossen wurde.

Schließlich wird die Benutzeroberfläche sichtbar gemacht und die Ereignisschleife gestartet.

Eine einfache Visual Basic-Anwendung könnte nun auf diese Qt-Anwendung zugreifen. Starten Sie in VB ein neues "Standard Exe"-Projekt und fügen Sie einen Projektverweis auf die Typbibliothek comappLib hinzu. Erstellen Sie ein Formular mit einer Listbox "DocumentList", einem statischen Label "DocumentsCount" und einer Befehlsschaltfläche "NewDocument". Schließlich implementieren Sie den Code für das Formular wie folgt:

Private Application As comappLib.Application
Private MyApp As Boolean

Private Sub UpdateList()
    DocumentList.Clear
    DocumentsCount.Caption = Application.documents.Count
    For Index = 0 To Application.documents.Count - 1
       DocumentList.AddItem (Application.documents.Item(Index).Title)
    Next
End Sub

Private Sub Form_Load()
    On Error GoTo CreateNew
    Set Application = GetObject(, "comapp.Application")
    MyApp = False
    GoTo Initialized
CreateNew:
    On Error GoTo InitializeFailed
    Set Application = New Application
    Application.Visible = True
    MyApp = True
Initialized:
    Caption = Application.id
    UpdateList
InitializeFailed:
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If MyApp Then
        Application.quit
    End If
End Sub

Private Sub NewDocument_Click()
    Application.documents.addDocument
    UpdateList
End Sub

Um das Beispiel zu erstellen, müssen Sie zuerst die Bibliothek QAxServer erstellen. Führen Sie dann qmake und Ihr Make-Tool in examples\activeqt\comapp aus.

Beispielprojekt @ code.qt.io

© 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.