在 Qt 中構建 ActiveX 服務器

The QAxServer 模塊屬於 ActiveQt 框架。它由 3 個類組成:

某些 範例實現 的 ActiveX 控件和 COM 對象有提供。

話題:

使用庫

將標準 Qt 應用程序轉換成 COM 服務器是使用 QAxServer 庫,必須添加 axserver 到 QT 變量在 .pro 文件。

進程外可執行服務器的生成是從 .pro 文件像這樣:

TEMPLATE = app
QT  += axserver
RC_FILE  = qaxserver.rc
...
					

要構建進程內服務器,使用 .pro 文件像這樣:

TEMPLATE = lib
QT += axserver
CONFIG  += dll
DEF_FILE = qaxserver.def
RC_FILE  = qaxserver.rc
...
					

文件 qaxserver.rc and qaxserver.def 是框架的一部分,且通常可以使用其位置 (指定路徑在 .pro 文件),或拷貝到工程目錄。可以修改這些文件,隻要它包括如類型庫條目 (即:可以添加版本信息或指定不同工具框圖標) 的任何文件。

使用 axserver 模塊將導緻 qmake 工具將要求構建步驟添加到構建係統:

  • 將二進製文件鏈接到 qaxserver.lib 而不是 qtmain.lib
  • 調用 idc 工具為 COM 服務器生成 IDL 文件
  • 使用 MIDL 工具將 IDL 編譯成類型庫 (編譯器安裝的一部分)
  • 將結果類型庫作為二進製資源附加到服務器二進製文件 (再次使用 idc 工具)
  • 注冊服務器

要跳過後處理步驟,還要設置 qaxserver_no_postlink 配置。

此外,可以指定版本號使用 VERSION 變量,如

TEMPLATE = lib
VERSION = 2.5
...
					

指定的版本號將用作類型庫和服務器的版本,當注冊時。

進程外 vs. 進程內

COM 服務器是作為獨立可執行文件運行,還是作為在客戶端進程中的共享庫運行,主要取決於由服務器提供的 COM 對象類型。

可執行文件服務器擁有能作為獨立應用程序運行的優點,但會增加 COM 客戶端和 COM 對象之間的通信開銷。若控件擁有編程錯誤,僅運行控件的服務器進程會崩潰,且客戶端應用程序可能會繼續運行。並非所有 COM 客戶端,都支持可執行文件服務器。

進程內服務器通常更小,且啓動時更快。客戶端和服務器之間的通信是直接透過虛函數調用完成,且不會引入遠程過程調用所要求的開銷。不管怎樣,若服務器崩潰,客戶端應用程序也可能崩潰,且並非每個功能都可用於進程內服務器 (即:在 COM 運行對象錶中注冊)。

2 種服務器類型都可以使用 Qt 作為共享庫,或靜態鏈接到服務器二進製文件。

在後期構建步驟中的典型錯誤

對於 ActiveQt 使服務器工作的特定後處理步驟,必須滿足一些要求:

  • 什麼都不需要就可以創建所有暴露控件,但 QApplication 實例要存在
  • 服務器的初始鏈接包括臨時類型庫資源
  • 運行服務器要求的所有依賴都在係統路徑下 (或在用於調用環境的路徑下;注意,Visual Studio 在 "工具|選項|目錄" 對話框中有列齣自己的一組環境變量)。

若不滿足這些要求,可能齣現一種或多種下列錯誤:

服務器可執行文件崩潰

要生成 IDL (交互式數據語言),作為 ActiveX 控件暴露的 Widget 需要實例化 (調用構造函數)。此時,什麼都沒有除瞭 QApplication 對象存在。Widget 構造函數不得依賴要創建的任何其它對象 (如:應校驗 null 指針)。

要調試服務器,采用 -dumpidl outputfile 運行它並校驗它在哪裏崩潰。

注意,沒有調用控件的函數。

服務器可執行文件不是有效 Win32 應用程序

附加類型庫破壞服務器二進製。這是 Windows Bug 且隻發生於發行構建。

第一鏈接步驟必須將虛設類型庫鏈接到稍後可以被 idc 替換的可執行文件。將帶有類型庫的資源文件添加到工程,如範例演示。

無法定位 DLL

構建係統需要運行服務器可執行文件來生成接口定義,並注冊服務器。若服務器鏈接的動態鏈接庫不在路徑中,這可能失敗 (如:Visual Studio 使用 Directories 選項中指定的環境設置調用服務器)。確保服務器要求的所有 DLL 和插件都位於錯誤消息框打印的路徑列齣目錄下 (另請參閱 Windows 部署工具 ).

打不開文件 ...

ActiveX 服務器無法正確關閉,當使用它的最後一個客戶端停止時。應用程序終止通常花費大約 2 秒鍾,但可能必須使用任務管理器來殺除進程 (如:當客戶端未正確釋放控件時)。

無法實例化控件

在此情況下,以管理員身份可能有助於注冊服務器。

實現控件

要采用 Qt 實現 COM (組件對象模型) 對象,創建子類化的 QObject 或任何現有 QObject 子類。若類是子類化的 QWidget ,COM 對象將是 ActiveX 控件。

#include <QWidget>
class MyActiveX : public QWidget
{
    Q_OBJECT
					

The Q_OBJECT 宏要求將 Widget 有關的元對象信息提供給 ActiveQt 框架。

Q_CLASSINFO("ClassID", "{1D9928BD-4453-4bdd-903D-E525ED17FDE5}")
Q_CLASSINFO("InterfaceID", "{99F6860E-2C5A-42ec-87F2-43396F4BE389}")
Q_CLASSINFO("EventsID", "{0A3E9F27-E4F1-45bb-9E47-63099BCCD0E3}")
					

使用 Q_CLASSINFO () 宏以指定用於 COM 對象的 COM 標識符。 ClassID and InterfaceID 是必需的,而 EventsID 纔有必要當對象有信號時。要生成這些標識符,使用係統工具像 uuidgen or guidgen .

可以為每個類指定額外屬性;見 類信息和調諧 瞭解細節。

Q_PROPERTY(int value READ value WRITE setValue)
					

使用 Q_PROPERTY () 宏聲明 ActiveX 控件特性。

聲明接受父級對象的標準構造函數、函數、信號及槽像對於任何 QObject 子類。

public:
    MyActiveX(QWidget *parent = 0)
    ...
    int value() const;
public slots:
    void setValue(int v);
    ...
signals:
    void valueChange(int v);
    ...
};
					

The ActiveQt 框架將特性和公共槽暴露成 ActiveX 特性和方法,將信號暴露成 ActiveX 事件,並在 Qt 數據類型和等效 COM 數據類型之間轉換。

數據類型

特性支持的 Qt 數據類型包括:

Qt 數據類型 COM 特性
bool VARIANT_BOOL
QString BSTR
int int
uint 無符號 int
double double
qlonglong CY
qulonglong CY
QColor OLE_COLOR
QDate DATE
QDateTime DATE
QTime DATE
QFont IFontDisp*
QPixmap IPictureDisp*
QVariant VARIANT
QVariantList (same as QList < QVariant >) SAFEARRAY(VARIANT)
QStringList SAFEARRAY(BSTR)
QByteArray SAFEARRAY(BYTE)
QRect 用戶定義類型
QSize 用戶定義類型
QPoint 用戶定義類型

信號和槽中參數支持的 Qt 數據類型:

Qt 數據類型 COM 參數
bool [in] VARIANT_BOOL
bool& [in, out] VARIANT_BOOL*
QString , const QString & [in] BSTR
QString & [in, out] BSTR*
QString & [in, out] BSTR*
int [in] int
int& [in,out] int
uint [in] unsigned int
uint& [in, out] unsigned int*
double [in] double
double& [in, out] double*
QColor , const QColor & [in] OLE_COLOR
QColor & [in, out] OLE_COLOR*
QDate , const QDate & [in] DATE
QDate & [in, out] DATE*
QDateTime , const QDateTime & [in] DATE
QDateTime & [in, out] DATE*
QFont , const QFont & [in] IFontDisp*
QFont & [in, out] IFontDisp**
QPixmap , const QPixmap & [in] IPictureDisp*
QPixmap & [in, out] IPictureDisp**
QList < QVariant >, const QList < QVariant >& [in] SAFEARRAY(VARIANT)
QList < QVariant >& [in, out] SAFEARRAY(VARIANT)*
QStringList , const QStringList & [in] SAFEARRAY(BSTR)
QStringList & [in, out] SAFEARRAY(BSTR)*
QByteArray , const QByteArray & [in] SAFEARRAY(BYTE)
QByteArray & [in, out] SAFEARRAY(BYTE)*
QObject * [in] IDispatch*
QRect & [in, out] struct QRect (用戶定義)
QSize & [in, out] struct QSize (用戶定義)
QPoint & [in, out] struct QPoint (用戶定義)

還支持導齣枚舉和標誌 (見 Q_enums() 和 Q_flags())。也支持將 in 參數類型作為返迴值。

擁有使用任何其它數據類型的參數的特性和信號/槽,會被忽略由 ActiveQt 框架。

子對象

COM 對象可以擁有可以錶示 COM 對象子元素的多個子對象。例如,錶示多文檔電子錶格應用程序的 COM 對象,可以為每個電子錶格提供一個子對象。

任何 QObject 子類可以用作 ActiveX 子對象的類型,隻要知道 QAxFactory 。然後,類型可以用於特性,或作為槽的返迴類型 (或參數)。

特性通知

要使 ActiveX 客戶端特性可綁定,使用多繼承自 QAxBindable 類:

#include <QAxBindable>
#include <QWidget>
class MyActiveX : public QWidget, public QAxBindable
{
    Q_OBJECT
					

當實現特性寫入函數時,使用 QAxBindable 類的 requestPropertyChange() 和 propertyChanged() 函數以允許將 ActiveX 客戶端綁定到控件特性。

服務控件

要使 COM 服務器可用於 COM 係統,必須使用 5 個唯一標識符在係統注冊錶中注冊它。這些標識符的提供是通過工具像 guidgen or uuidgen 。注冊信息允許 COM 本地化二進製提供請求的 ActiveX 控件,編組控件的 RPC (遠程過程調用) 及讀取由控件暴露方法和特性的有關類型信息。

要創建客戶端詢問時的 COM 對象,服務器必須導齣實現的 QAxFactory 。做到這的最輕鬆方式是使用一組宏:

QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}",
                 "{a8f21901-7ff7-4f6a-b939-789620c03d83}")
    QAXCLASS(MyWidget)
    QAXCLASS(MyWidget2)
    QAXTYPE(MySubType)
QAXFACTORY_END()
					

這將導齣 MyWidget and MyWidget2 作為可以由 COM 客戶端創建的 COM 對象,和將注冊 MySubType 作為類型可以用於特性和參數對於 MyWidget and MyWidget2 .

The QAxFactory 類文檔編製 闡述如何使用此宏,及如何實現和使用自定義工廠。

對於進程外的可執行服務器,可以實現 main() 函數以實例化 QApplication 對象,並就像任何正常 Qt 應用程序進入事件循環。默認情況下,應用程序將作為標準 Qt 應用程序啓動,但若傳遞 -activex 在命令行中,它將作為 ActiveX 服務器啓動。使用 QAxFactory::isServer () 以創建和運行標準應用程序接口 (或阻止獨立執行):

#include <QApplication>
#include <QAxFactory>
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    if (!QAxFactory::isServer()) {
        // create and show main window
    }
    return app.exec();
}
					

不管怎樣,這沒必要,因為 ActiveQt 提供瞭 main 函數的默認實現。默認實現調用 QAxFactory::startServer (),創建 QApplication 實例並調用 exec()。

要構建 ActiveX 服務器可執行文件,運行 qmake 以生成 makefile 並使用編譯器 make 工具,如同任何其它 Qt 應用程序。make 進程還將在係統注冊錶中注冊控件,通過調用結果可執行文件采用 -regserver 命令行選項。

若 ActiveX 服務器是可執行文件,支持下列命令行選項:

選項 結果
-regserver 在係統注冊錶中注冊服務器
-unregserver 從係統注冊錶取消服務器的注冊
-activex 將應用程序作為 ActiveX 服務器啓動
-dumpidl <file> -version x.y 將服務器的 IDL 寫入指定文件。類型庫將擁有版本 x.y。

進程內服務器的注冊是使用 regsvr32 工具,可用於所有 Windows 係統。

典型編譯時問題

列齣的編譯器/鏈接器錯誤是基於由 Microsoft Visual C++ 6.0 編譯器發齣那些。

未獲得 2 參數的重載函數

當代碼齣現錯誤使用 QAXFACTORY_DEFAULT () 宏,Widget 類沒有可以用於默認工廠的構造函數。要麼添加標準 Widget 構造函數,要麼實現不要求的自定義工廠。

當代碼齣現錯誤使用 QAXFACTORY_EXPORT () 宏, QAxFactory 子類沒有閤適構造函數。提供公共類構造函數像

MyFactory(const QUuid &, const QUuid &);
					

為工廠類。

句法錯誤:壞的數字後綴

唯一標識符尚未作為字符串被傳遞給 QAXFACTORY_EXPORT () 或 QAXFACTORY_DEFAULT () 宏。

未解析的外部符號 _ucm_instantiate

服務器未導齣實現的 QAxFactory 。使用 QAXFACTORY_EXPORT () 宏在某個工程的實現文件中以實例化並導齣工廠,或使用 QAXFACTORY_DEFAULT () 宏以使用默認工廠。

_ucm_initialize 已定義於 ...

服務器導齣多個實現的 QAxFactory ,或導齣相同實現 2 次。若使用默認工廠, QAXFACTORY_DEFAULT () 宏隻得在工程中使用 1 次。使用自定義 QAxFactory 實現和 QAXFACTORY_EXPORT () 宏若服務器提供多個 ActiveX 控件。

分布 QAxServer 二進製文件

采用 Qt 編寫 ActiveX 服務器要麼可以使用 Qt 作為共享庫,要麼將 Qt 靜態鏈接到二進製。2 種方式都會産生相當大的包 (要麼服務器二進製本身變大,要麼必須隨附 Qt DLL)。

安裝獨立服務器

當 ActiveX 服務器還可以作為獨立應用程序運行時,運行服務器可執行文件采用 -regserver 命令行參數在目標係統安裝可執行文件後。之後,由服務器提供的控件將可用於 ActiveX 客戶端。

安裝進程內服務器

若 ActiveX 服務器是安裝包的一部分,使用 regsvr32 工具由 Microsoft 提供,以在目標係統注冊控件。若此工具不存在,將 DLL 加載到安裝程序進程,解析 DllRegisterServer 符號並調用函數:

HMODULE dll = LoadLibrary("myserver.dll");
typedef HRESULT(__stdcall *DllRegisterServerProc)();
DllRegisterServerProc DllRegisterServer =
    (DllRegisterServerProc)GetProcAddress(dll, "DllRegisterServer");
HRESULT res = E_FAIL;
if (DllRegisterServer)
    res = DllRegisterServer();
if (res != S_OK)
    // error handling
					
					

通過 Internet 分發服務器

若想要在網頁使用服務器控件,需要使服務器可用於查看頁麵的瀏覽器,且需要在頁麵中指定服務器包位置。

要指定服務器位置,使用網站 OBJECT 標簽的 CODEBASE 屬性。值會指嚮服務器文件本身、列齣服務器要求的其它文件的 INF 文件 (如 Qt DLL) 或被壓縮的 CAB 存檔。

INF 和 CAB 文件文檔化在關於 ActiveX 和 COM 編程的幾乎每一本書中,MSDN 庫及各種其它在綫資源中。範例包括可以用於構建 CAB 存檔的 INF 文件:

[version]
    signature="$CHICAGO$"
    AdvancedINF=2.0
 [Add.Code]
    simpleax.exe=simpleax.exe
 [simpleax.exe]
    file-win32-x86=thiscab
    clsid={DF16845C-92CD-4AAB-A982-EB9840E74669}
    RegisterServer=yes
					

來自微軟的 CABARC 工具可以輕鬆生成 CAB 存檔:

cabarc N simpleax.cab simpleax.exe simple.inf
					

INF 文件假定 Qt 是靜態構建的,所以 INF 文件中未列齣其它 DLL 的依賴。要根據 DLL 分發 ActiveX 服務器,必須添加依賴,並提供帶有存檔的庫文件。

使用控件

要使用 ActiveX 控件,如將其嵌入網頁,使用 <object> HTML 標簽。

<object ID="MyActiveX1" CLASSID="CLSID:ad90301a-849e-4e8b-9a91-0a6dc5f6461f">
   ...
<\object>
					

要初始化控件特性,使用

<object ID=...>
    <param name="name" value="value">
<\object>
					

若 Web 瀏覽器支持腳本,請使用 JavaScript、VBScript 及控件腳本錶單。 ActiveQt 範例 包括範例控件的演示 HTML 頁麵。

支持和不支持的 ActiveX 客戶端

以下很大程度上是基於我們自己的 ActiveX 控件和客戶端應用程序的經驗,且並不完整。

支持的客戶端

這些標準應用程序工作於 ActiveX 控件開發采用 ActiveQt 。注意,某些客戶端僅支持進程內控件。

  • Internet Explorer
  • 微軟 ActiveX 控件測試容器
  • 微軟 Visual Studio 6.0
  • 微軟 Visual Studio.NET/2003
  • 微軟 Visual Basic 6.0
  • 基於 MFC (微軟基礎類) 和 ATL (活動模闆庫) 的容器
  • Sybase PowerBuilder
  • ActiveQt 基容器

支持微軟 Office 應用程序,但需要將控件注冊為 "Insertable (可插入)" 對象。重實現 QAxFactory::registerClass 以將此屬性添加到 COM 類,或將類的 Insertable 類信息設為 yes 使用 Q_CLASSINFO 宏。

不支持的客戶端

我們沒有成功使 ActiveQt 基 COM 對象工作於下列客戶端應用程序。

  • Borland C++ 構建器 (第 5 和第 6 版)
  • Borland Delphi

典型運行時錯誤

服務器不響應

若係統無法啓動服務器 (采用任務管理器校驗服務器是否有運行進程),確保服務器依賴的 DLL 沒有從係統路徑下丟失 (如 Qt DLL)。使用依賴 walker (步行者) 能查看服務器二進製的所有依賴。

若服務器在運行 (如:任務管理器有列齣進程),見以下章節,瞭解調試服務器的有關信息。

無法創建對象

若在構建過程期間可以正確構建並注冊服務器,但無法初始化對象 (如:通過 OLE/COM 對象 Viewer 查看器應用程序),確保服務器依賴的 DLL 沒有從係統路徑下丟失 (如 Qt DLL)。使用依賴 walker (步行者) 能查看服務器二進製的所有依賴。

若服務器在運行,見以下章節瞭解調試服務器的有關信息。

調試運行時錯誤

To debug an in-process server in Visual Studio, set the server project as the active project, and specify a client "executable for debug session" in the project settings (e.g. use the ActiveX Test Container). You can set breakpoints in your code, and also step into ActiveQt and Qt code if you installed the debug version.

To debug an executable server, run the application in a debugger and start with the command line parameter -activex . Then start your client and create an instance of your ActiveX control. COM will use the existing process for the next client trying to create an ActiveX control.

類信息和調諧

To provide attributes for each COM class, use the Q_CLASSINFO macro, which is part of Qt's meta object system.

Key 值的含義
版本 類的版本 (默認為 1.0)
Description 描述類的字符串。
ClassID The class ID. You must reimplement QAxFactory::classID if not specified.
InterfaceID The interface ID. You must reimplement QAxFactory::interfaceID if not specified.
EventsID The event interface ID. No signals are exposed as COM events if not specified.
DefaultProperty The property specified represents the default property of this class. Ie. the default property of a push button would be "text".
DefaultSignal The signal specified respresents the default signal of this class. Ie. the default signal of a push button would be "clicked".
LicenseKey Object creation requires the specified license key. The key can be empty to require a licensed machine. By default classes are not licensed. Also see the following section.
StockEvents Objects expose stock events if value is "yes". See QAxFactory::hasStockEvents ()
ToSuperClass Objects expose functionality of all super-classes up to and including the class name in value. See QAxFactory::exposeToSuperClass ()
Insertable If the value is "yes" the class is registered to be "Insertable" and will be listed in OLE 2 containers (ie. Microsoft Office). This attribute is not be set by default.
Aggregatable If the value is "no" the class does not support aggregation. By default aggregation is supported.
Creatable If the value is "no" the class cannot be created by the client, and is only available through the API of another class (ie. the class is a sub-type).
RegisterObject If the value is "yes" objects of this class are registered with OLE and accessible from the running object table (ie. clients can connect to an already running instance of this class). This attribute is only supported in out-of-process servers.
MIME The object can handle data and files of the format specified in the value. The value has the format mime:extension:description. Multiple formats are separated by a semicolon.
CoClassAlias The classname used in the generated IDL and in the registry. This is esp. useful for C++ classes that live in a namespace - by default, ActiveQt just removes the "::" to make the IDL compile.
Implemented Categories List of comma-separated Category ID (CATID) UUIDs. Generic mechanism for specifying additional container capabilities, in addition to "control", "insertable" etc. Typical CATIDs include CATID_InternetAware ("{0DE86A58-2BAA-11CF-A229-00AA003D7352}"), CATID_SafeForScripting ("{7DD95801-9882-11CF-9FA9-00AA006C42C4}") as well as user-defined CATID values.

Note that both keys and values are case sensitive.

The following declares version 2.0 of a class that exposes only its own API, and is available in the "Insert Objects" dialog of Microsoft Office applications.

class MyActiveX : public QWidget
{
    Q_OBJECT
    Q_CLASSINFO("Version", "2.0")
    Q_CLASSINFO("ClassID", "{7a4cffd8-cbcd-4ae9-ae7e-343e1e5710df}")
    Q_CLASSINFO("InterfaceID", "{6fb035bf-8019-48d8-be51-ef05427d8994}")
    Q_CLASSINFO("EventsID", "{c42fffdf-6557-47c9-817a-2da2228bc29c}")
    Q_CLASSINFO("Insertable", "yes")
    Q_CLASSINFO("ToSuperClass", "MyActiveX")
    Q_PROPERTY(...)
public:
    MyActiveX(QWidget *parent = 0);
    ...
};
					

開發許可組件

If you develop components you might want to control who is able to instantiate those components. Since the server binary can be shipped to and registered on any client machine it is possible for anybody to use those components in his own software.

Licensing components can be done using a variety of techniques, e.g. the code creating the control can provide a license key, or the machine on which the control is supposed to run needs to be licensed.

To mark a Qt class as licensed specify a "LicenseKey" using the Q_CLASSINFO () 宏。

class MyLicensedControl : public QWidget
{
    Q_OBJECT
    Q_CLASSINFO("LicenseKey", "<key string>")
    ...
};
					

The key is required to be able to create an instance of MyLicensedControl on a machine that is not licensed itself. The licensed developer can now redistributes the server binary with his application, which creates the control using the value of "LicenseKey", while users of the application cannot create the control without the license key.

If a single license key for the control is not sufficient (ie. you want differnet developers to receive different license keys) you can specify an empty key to indicate that the control requires a license, and reimplement QAxFactory::validateLicenseKey () to verify that a license exists on the system (ie. through a license file).

更多接口

ActiveX 控件提供通過 ActiveQt servers support a minimal set of COM interfaces to implement the OLE specifications. When the ActiveX class inherits from the QAxBindable class it can also implement additional COM interfaces.

Create a new subclass of QAxAggregated and use multiple inheritance to subclass additional COM interface classes.

class AxImpl : public QAxAggregated, public ISomeCOMInterface
{
public:
    AxImpl() {}
    long queryInterface(const QUuid &iid, void **iface);
    // IUnknown
    QAXAGG_IUNKNOWN
    // ISomeCOMInterface
    ...
}
					

重實現 QAxAggregated::queryInterface () 函數以支持額外 COM 接口。

long AxImpl::queryInterface(const QUuid &iid, void **iface)
{
    *iface = 0;
    if (iid == IID_ISomeCOMInterface)
        *iface = (ISomeCOMInterface *)this;
    else
        return E_NOINTERFACE;
    AddRef();
    return S_OK;
}
					

由於 ISomeCOMInterface 是子類化的 IUnknown you will have to implement the QueryInterface() , AddRef() ,和 Release() functions. Use the QAXAGG_IUNKNOWN macro in your class definition to do that. If you implement the IUnknown functions manually, delegate the calls to the interface pointer returned by the QAxAggregated::controllingUnknown () function, e.g.

HRESULT AxImpl::QueryInterface(REFIID iid, void **iface)
{
    return controllingUnknown()->QueryInterface(iid, iface);
}
					

不支持 IUnknown 接口本身在您的 queryInterface() 實現。

Implement the methods of the COM interfaces, and use QAxAggregated::object () if you need to make calls to the QObject subclass implementing the control.

QAxBindable 子類,實現 QAxBindable::createAggregate () to return a new object of the QAxAggregated 子類。

class MyActiveX : public QWidget, public QAxBindable
{
    Q_OBJECT
public:
    MyActiveX(QWidget *parent);
    QAxAggregated *createAggregate()
    {
        return new AxImpl();
    }
};
					

另請參閱 ActiveQt 框架 .