COM App 範例 (ActiveQt)

The COM App example shows how to use ActiveQt to develop a Qt application that can be automated via COM. Different QObject based classes are exposed as COM objects that communicate with the GUI of the running Qt application. The APIs of those COM objects has been designed to resemble the APIs of standard COM applications; i.e. those from 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;
};
					

第 1 個類 應用程序 錶示應用程序對象。它暴露隻讀特性 documents and id 以訪問文檔列錶和標識符。讀/寫特性 visible 控製是否 QTabWidget 基應用程序的用戶界麵應該可見,和槽 quit() 終止應用程序。

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

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

The Document 類錶示最終應用程序文檔。各文檔由應用程序選項卡 Widget 中的頁麵錶示,且擁有的標題透過文檔 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()
{
}
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 類為選項卡 Widget 創建新頁麵,並將該頁麵標題用於 title 特性。會刪除頁麵當刪除文檔時。

DocumentList::DocumentList(Application *application)
: QObject(application)
{
}
Application *DocumentList::application() const
{
    return qobject_cast<Application *>(parent());
}
int DocumentList::count() const
{
    return m_list.count();
}
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;
}
					

The 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"
					

The 應用程序 類在構造函數中初始化用戶界麵,並展示和隱藏它在實現 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 宏。僅 應用程序 對象可以從外部實例化 - 纔可以使用其它 API 後於訪問各個對象縱觀 應用程序 API.

int main(int argc, char *argv[])
{
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    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 所啓動。若應用程序已由用戶所啓動,則 應用程序 對象被創建且對象名稱被設為 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)
    下一
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 .

文件: