Ejemplo de aplicación COM (ActiveQt)

El ejemplo COM App muestra cómo utilizar ActiveQt para desarrollar una aplicación Qt que puede automatizarse a través de COM. Diferentes clases basadas en QObject se exponen como objetos COM que se comunican con el GUI de la aplicación Qt en ejecución. Las APIs de estos objetos COM han sido diseñadas para parecerse a las APIs de las aplicaciones COM estándar; es decir, las de Microsoft Office.

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;
};

La primera clase Application representa el objeto de aplicación. Expone las propiedades de sólo lectura documents y id para acceder a la lista de documentos, y un identificador. Una propiedad de lectura/escritura visible controla si la interfaz de usuario de la aplicación basada en QTabWidget debe ser visible, y una ranura quit() termina la aplicación.

El atributo RegisterObject se establece para asegurarse de que las instancias de esta clase se registran en la tabla de objetos en ejecución (ROT) de COM, lo que permite a los clientes COM conectarse a un objeto COM ya instanciado.

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;
};

La clase DocumentList almacena una lista de documentos. Proporciona una API para leer el número de documentos, acceder a cada documento por índice y crear un nuevo documento. La propiedad application devuelve el objeto raíz.

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;
};

La clase Document representa finalmente un documento en la aplicación. Cada documento está representado por una página en el widget de pestañas de la aplicación, y tiene un título que es legible y escribible a través de la API del documento. La propiedad application devuelve de nuevo el objeto raíz.

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());
}

La implementación de la clase Document crea una nueva página para el widget de pestañas y utiliza el título de esa página para la propiedad title. La página se elimina cuando se borra el documento.

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;
}

La implementación de DocumentList es sencilla.

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"

La clase Application inicializa la interfaz de usuario en el constructor, y la muestra y oculta en la implementación de setVisible(). El nombre del objeto (accesible a través de la propiedad id ) se establece en "From QAxFactory " para indicar que este objeto COM ha sido creado por COM. Nótese que no hay un destructor que borre el QTabWidget - esto se hace en la ranura quit(), antes de llamar a quit() a través de un temporizador de disparo único, que es necesario para asegurar que la llamada COM a la ranura se ha completado.

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

Las clases se exportan desde el servidor utilizando las macros QAxFactory. Sólo los objetos Application pueden instanciarse desde el exterior - las otras APIs sólo pueden utilizarse después de acceder a los respectivos objetos a través de la API Application.

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();
}

La función de punto de entrada main() crea un QApplication, y sólo entra en el bucle de eventos si la aplicación ha sido iniciada por COM. Si la aplicación ha sido iniciada por el usuario, entonces se crea el objeto Application y el nombre del objeto se establece como "Desde Aplicación". A continuación, se inicia el servidor COM y el objeto de aplicación se registra en COM. Ahora los clientes COM pueden acceder a él a través de las API específicas del cliente.

La salida de la aplicación se controla explícitamente: si COM ha iniciado la aplicación, el código cliente debe llamar a quit(); si el usuario ha iniciado la aplicación, ésta termina cuando se cierra la última ventana.

Por último, se hace visible la interfaz de usuario y se inicia el bucle de eventos.

Una simple aplicación de Visual Basic podría ahora acceder a esta aplicación Qt. En VB, inicie un nuevo proyecto "Standard Exe" y añada una referencia de proyecto a la biblioteca de tipos comappLib. Cree un formulario con un listbox "DocumentList", una etiqueta estática "DocumentsCount" y un botón de comando "NewDocument". Por último, implemente el código para el formulario de la siguiente manera:

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

Para construir el ejemplo primero debes construir la librería QAxServer. A continuación, ejecute qmake y su herramienta make en examples\activeqt\comapp.

Proyecto de ejemplo @ code.qt.io

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