COM アプリの例(ActiveQt)

COM アプリの例では、ActiveQt を使用して COM 経由で自動化できる Qt アプリケーションを開発する方法を示します。さまざまなQObject ベースのクラスが COM オブジェクトとして公開され、実行中の Qt アプリケーションの GUI と通信します。これらのCOMオブジェクトのAPIは、標準的なCOMアプリケーションの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 はアプリケーション・オブジェクトを表します。これは、文書のリストと識別子にアクセスするための読み取り専用のプロパティdocumentsid を公開しています。読み書き可能なプロパティvisible は、アプリケーションのQTabWidget ベースのユーザーインターフェイスを表示するかどうかを制御し、スロットquit() はアプリケーションを終了します。

RegisterObject属性は、このクラスのインスタンスがCOMの実行オブジェクト・テーブル(ROT)に登録されていることを確認するために設定されます。

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 クラスは最終的にアプリケーション内のドキュメントを表します。各ドキュメントはアプリケーションのタブ・ウィジェットのページで表され、ドキュメントの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 プロパティからアクセス可能)は"From QAxFactory " に設定され、この COM オブジェクトが COM によって作成されたことを示します。QTabWidget を削除するようなデストラクタは存在しないことに注意してください。これは、quit() スロットで行われ、シングルショットタイマーを介してquit() を呼び出す前に行われます。

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に登録されます。これで、クライアント固有の API を介して COM クライアントからアクセスできるようになります。

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 と make ツールをexamples\activeqt\comapp で実行します。

サンプルプロジェクト @ code.qt.io

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。