COM アプリケーションの例 (ActiveQt)

このCOMアプリケーションのサンプルでは、ActiveQtを使用して、COMを介して自動化可能なQtアプリケーションを開発する方法を示します。QObject をベースとしたさまざまなクラスがCOMオブジェクトとして公開され、実行中のQtアプリケーションのGUIと通信します。これらのCOMオブジェクトのAPIは、標準的なCOMアプリケーション(例えばMicrosoft Officeのアプリケーション)のAPIに似るように設計されています。

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

最初のクラス `Application ` は、アプリケーションオブジェクトを表します。このクラスは、ドキュメントのリストにアクセスするための読み取り専用プロパティ `documents ` および `id `、ならびに識別子を公開しています。読み書き可能なプロパティ `visible ` は、アプリケーションの `QTabWidget` ベースのユーザーインターフェースを表示するかどうかを制御し、スロット `quit() ` はアプリケーションを終了させます。

RegisterObject属性は、このクラスのインスタンスがCOMの実行中オブジェクトテーブル(ROT)に登録されるように設定されています。これにより、COMクライアントはすでにインスタンス化されているCOMオブジェクトに接続できるようになります。

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

DocumentList クラスは、ドキュメントのリストを格納します。このクラスは、ドキュメントの数を読み取り、インデックスによって各ドキュメントにアクセスし、新しいドキュメントを作成するための API を提供します。application プロパティは、ルートオブジェクトを返します。

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

Document クラスは、アプリケーション内のドキュメントを表します。各ドキュメントは、アプリケーションのタブウィジェット内の1つのページとして表現され、ドキュメントのAPIを通じて読み書き可能なタイトルを持っています。application プロパティも、同様にルートオブジェクトを返します。

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

Document クラスの実装では、タブウィジェット用の新しいページを作成し、そのページのタイトルを title プロパティとして使用します。ドキュメントが削除されると、そのページも削除されます。

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

DocumentList の実装は単純明快です。

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"

Application クラスは、コンストラクタでユーザーインターフェースを初期化し、setVisible() の実装でその表示と非表示を切り替えます。オブジェクト名(id プロパティを通じてアクセス可能)は、この COM オブジェクトが COM によって作成されたことを示すために、"From QAxFactory " に設定されます。 なお、QTabWidget を削除するデストラクタは存在しません。その代わりに、quit() スロット内で削除が行われ、その後、シングルショットタイマーを介してquit()が呼び出されます。これは、スロットへのCOM呼び出しが確実に完了するようにするために必要な処理です。

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

クラスは、QAxFactory マクロを使用してサーバーからエクスポートされます。外部からインスタンス化できるのはApplication オブジェクトのみです。その他のAPIは、Application APIを介してそれぞれのオブジェクトにアクセスした後にのみ使用できます。

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

main() エントリポイント関数はQApplication を作成し、アプリケーションが COM によって起動された場合は、そのままイベントループに入ります。アプリケーションがユーザーによって起動された場合は、Application オブジェクトが作成され、オブジェクト名が「From Application」に設定されます。 その後、COMサーバーが起動され、アプリケーションオブジェクトがCOMに登録されます。これにより、COMクライアントはクライアント固有のAPIを通じてこのオブジェクトにアクセスできるようになります。

アプリケーションの終了は明示的に制御されます。COMによってアプリケーションが起動された場合は、クライアントコードがquit()を呼び出す必要があります。ユーザーによってアプリケーションが起動された場合は、最後のウィンドウが閉じられた時点でアプリケーションは終了します。

最後に、ユーザーインターフェースが表示され、イベントループが開始されます。

これで、単純な Visual Basic アプリケーションからこの Qt アプリケーションにアクセスできるようになります。VB で、新しい「Standard Exe」プロジェクトを開始し、comappLib タイプライブラリへのプロジェクト参照を追加します。リストボックス「DocumentList」、静的ラベル「DocumentsCount」、およびコマンドボタン「NewDocument」を含むフォームを作成します。 最後に、フォームのコードを次のように実装します。

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

このサンプルをビルドするには、まずQAxServer ライブラリをビルドする必要があります。その後、qmake を実行し、examples\activeqt\comapp でmakeツールを実行してください。

サンプルプロジェクト @ 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.