COM 앱 예제(ActiveQt)

COM 앱 예제는 ActiveQt를 사용하여 COM을 통해 자동화할 수 있는 Qt 애플리케이션을 개발하는 방법을 보여줍니다. 다양한 QObject 기반 클래스가 실행 중인 Qt 애플리케이션의 GUI와 통신하는 COM 객체로 노출됩니다. 이러한 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 는 애플리케이션 객체를 나타냅니다. 이 클래스는 문서 목록과 식별자에 액세스할 수 있는 읽기 전용 속성 documentsid 을 노출합니다. 읽기/쓰기 속성 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 클래스는 최종적으로 애플리케이션에서 문서를 나타냅니다. 각 문서는 애플리케이션의 탭 위젯에서 페이지로 표시되며, 문서의 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 클래스의 구현은 탭 위젯을 위한 새 페이지를 만들고 제목 속성에 해당 페이지의 제목을 사용합니다. 문서가 삭제되면 페이지가 삭제됩니다.

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()를 호출하기 전에 이 작업을 수행하는데, 이는 슬롯에 대한 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 라이브러리를 빌드해야 합니다. 그런 다음 qmakeexamples\activeqt\comapp 에서 make 도구를 실행합니다.

예제 프로젝트 @ 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.