在 Qt 中构建 ActiveX 服务器
QAxServer 模块是ActiveQt框架的一部分。它由三个类组成:
- QAxFactory 定义了创建 COM 对象的工厂。
- QAxBindable 提供了 Qt Widget 与 COM 对象之间的接口。
- QAxAggregated 可被子类化以实现其他 COM 接口。
提供了一些 ActiveX 控件和 COM 对象的实现示例。
使用程序库
要使用QAxServer 库将标准 Qt 应用程序转化为 COM 服务器,必须在.pro
文件中的 QT 变量中添加axserver
。
进程外可执行服务器就是这样从.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
和qaxserver.def
文件是框架的一部分,可以在其常规位置使用(在.pro
文件中指定路径),也可以复制到项目目录中。你可以修改这些文件,只要其中包含任何作为类型库条目文件,即可以添加版本信息或指定不同的工具箱图标。
使用axserver
模块将导致qmake
工具向构建系统添加所需的构建步骤:
- 将二进制文件链接到
qaxserver.lib
,而不是qtmain.lib
- 调用idc工具为 COM 服务器生成 IDL 文件
- 使用 MIDL 工具(编译器安装的一部分)将 IDL 编译成类型库
- 将生成的类型库作为二进制资源附加到服务器二进制文件(同样使用idc工具)
- 注册服务器。这一步可能需要管理权限,可通过设置
qaxserver_no_register
配置跳过。
要跳过后处理步骤,也可设置qaxserver_no_postlink
配置。
此外,您还可以使用VERSION
变量指定版本号,例如
TEMPLATE = lib VERSION = 2.5 ...
指定的版本号将在注册时用作类型库和服务器的版本。
进程外与进程内
COM 服务器是作为独立的可执行文件运行,还是作为客户进程中的共享库运行,主要取决于服务器中要提供的 COM 对象类型。
可执行服务器的优点是可以作为独立应用程序运行,但会给 COM 客户端与 COM 对象之间的通信增加大量开销。如果控件出现编程错误,只有运行控件的服务器进程会崩溃,客户端程序可能会继续运行。并非所有 COM 客户端都支持可执行服务器。
进程内服务器通常更小,启动时间更短。客户端与服务器之间的通信直接通过虚拟函数调用完成,不会引入远程过程调用所需的开销。不过,如果服务器崩溃,客户端程序也有可能崩溃,而且进程内服务器并非所有功能都可用(如在 COM 的运行对象表中注册)。
这两种服务器类型都可以使用 Qt 作为共享库,或静态链接到服务器二进制文件中。
编译后步骤中的典型错误
要使 ActiveQt 特定的后处理步骤发挥作用,服务器必须满足一些要求:
- 除了一个QApplication 实例外,可以创建所有暴露的控件
- 服务器的初始链接包括临时类型库资源
- 运行服务器所需的所有依赖项都在系统路径中(或在调用环境使用的路径中;注意 Visual Studio 有自己的环境变量集,列在工具|选项|目录对话框中)。
如果不满足这些要求,可能会出现以下一种或多种错误:
服务器可执行文件崩溃
要生成 IDL,需要将作为 ActiveX 控件显示的部件实例化(调用构造器)。此时,除了一个QApplication 对象外,其他什么都不存在。您的部件构造函数不能依赖于任何其他对象的创建,例如,它应该检查空指针。
要调试服务器,请使用 -dumpidl outputfile 运行服务器并检查崩溃位置。
注意,不会调用控件的任何函数。
服务器可执行文件不是有效的 Win32 应用程序
附加类型库破坏了服务器二进制文件。这是 Windows 的一个错误,仅在发布版本中发生。
第一个链接步骤必须将一个假类型库链接到可执行文件中,之后再用 idc 将其替换。如示例所示,在项目中添加包含类型库的资源文件。
"无法定位 DLL
构建系统需要运行服务器可执行文件以生成接口定义并注册服务器。如果服务器链接的动态链接库不在路径中,则可能会失败(例如,Visual Studio 使用 "目录 "选项中指定的环境设置调用服务器)。请确保服务器所需的所有动态链接库和插件都位于打印在错误消息框中的路径所列出的目录中(另请参阅Windows 部署工具)。
"无法打开文件..."
当最后一个客户端停止使用 ActiveX 服务器时,服务器无法正常关闭。应用程序通常需要大约两秒钟的时间才能终止,但您可能需要使用任务管理器来杀死进程(例如,当客户端没有正确释放控件时)。
控件无法实例化
在这种情况下,将服务器注册为管理员可能会有所帮助。
实现控件
要使用 Qt XML 实现 COM 对象,请创建QObject 或任何现有QObject 子类。如果该类是QWidget 的子类,则 COM 对象将是 ActiveX 控件。
#include <QWidget> class MyActiveX : public QWidget { Q_OBJECT
需要使用Q_OBJECT 宏向 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
和InterfaceID
是必需的,而EventsID
只有在对象有信号时才有必要。要生成这些标识符,请使用uuidgen
或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); ... };
ActiveQt 框架会将属性和公共插槽公开为 ActiveX 属性和方法,将信号公开为 ActiveX 事件,并在 Qt 数据类型和等效 COM 数据类型之间进行转换。
数据类型
属性支持的 Qt 数据类型有
Qt 数据类型 | COM 属性 |
---|---|
布尔 | 变量_BOOL |
QString | BSTR |
int | int |
uint | 无符号 int |
双 | 双 |
qlonglong | CY |
qulonglong | CY |
QColor | OLE_COLOR |
QDate | 日期 |
QDateTime | 日期 |
QTime | 日期 |
QFont | IFontDisp* |
QPixmap | IPictureDisp* |
QVariant | 变量 |
QVariantList (同 < >)QListQVariant | safearray(variant) |
QStringList | safearray(bstr) |
QByteArray | safearray(byte) |
QRect | 用户定义类型 |
QSize | 用户自定义类型 |
QPoint | 用户自定义类型 |
信号和插槽中的参数支持以下 Qt 数据类型:
Qt 数据类型 | COM 参数 |
---|---|
bool | [in] 变量_BOOL |
bool& | [in, out] VARIANT_BOOL* |
QString, constQString& | [in] BSTR |
QString& | [in, out] BSTR* |
QString& | [in, out] BSTR* |
int | [in] int |
int& | [in,out] int |
uint | [in] 无符号 int |
uint& | [无符号 int* |
double | [in] double |
double& | [in, out] double* |
QColor, constQColor& | [in] OLE_COLOR |
QColor& | [in, out] OLE_COLOR* |
QDate, constQDate& | [in] DATE |
QDate& | [in, out] DATE* |
QDateTime, constQDateTime& | [in] DATE |
QDateTime& | [in, out] DATE* |
QFont, constQFont& | [in] IFontDisp* |
QFont& | [in, out] IFontDisp** |
QPixmap, constQPixmap& | [in] IPictureDisp* |
QPixmap& | [in, out] IPictureDisp** |
QList<QVariant>, constQList<QVariant>& | [in] SAFEARRAY(VARIANT) |
QList<QVariant>& | [in, out] SAFEARRAY(VARIANT)* |
QStringList, constQStringList& | [in] SAFEARRAY(BSTR) |
QStringList& | [in, out] SAFEARRAY(BSTR)* |
QByteArray, constQByteArray& | [in] SAFEARRAY(BYTE) |
QByteArray& | [in, out] SAFEARRAY(BYTE)* |
QObject* | [in] IDispatch* |
QRect与 | [in, out] structQRect (用户定义) |
QSize& | [in, out] structQSize (用户自定义) |
QPoint& | [in, out] 结构QPoint (用户定义) |
还支持导出枚举和标志(参见Q_ENUM() 和Q_FLAG() )。也支持参数内类型作为返回值。
ActiveQt 框架将忽略使用任何其他数据类型作为参数的属性和信号/插槽。
子对象
COM 对象可以有多个子对象,这些子对象可以表示 COM 对象的子元素。例如,代表多文档电子表格应用程序的 COM 对象可以为每个电子表格提供一个子对象。
在 ActiveX 中,任何QObject 子类都可用作子对象的类型,只要QAxFactory 已知。然后,该类型可在属性中使用,或用作槽的返回类型或参数。
属性通知
为使 ActiveX 客户端可绑定属性,请使用从QAxBindable 类的多重继承:
#include <QAxBindable> #include <QWidget> class MyActiveX : public QWidget, public QAxBindable { Q_OBJECT
在实现属性写入函数时,使用QAxBindable 类的 requestPropertyChange() 和 propertyChanged() 函数允许 ActiveX 客户端绑定控件属性。
为控件提供服务
要向 COM 系统提供 COM 服务器,必须使用五个唯一标识符在系统注册表中注册。这些标识符由guidgen
或uuidgen
等工具提供。注册信息允许 COM 本地化提供所请求 ActiveX 控件的二进制文件,将远程过程调用汇集到控件,并读取控件暴露的方法和属性的类型信息。
要在客户端请求时创建 COM 对象,服务器必须导出QAxFactory 的实现。最简单的方法是使用一组宏:
QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}", "{a8f21901-7ff7-4f6a-b939-789620c03d83}") QAXCLASS(MyWidget) QAXCLASS(MyWidget2) QAXTYPE(MySubType) QAXFACTORY_END()
这将导出MyWidget
和MyWidget2
作为 COM 客户端可以创建的 COM 对象,并将MySubType
注册为可在MyWidget
和MyWidget2
的属性和参数中使用的类型。
QAxFactory class documentation 解释了如何使用该宏,以及如何实现和使用自定义工厂。
对于进程外可执行服务器,您可以实现一个 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 提供了主函数的默认实现。默认实现调用QAxFactory::startServer() ,创建QApplication 实例并调用 exec() 。
要构建 ActiveX 服务器可执行文件,请运行qmake
生成 makefile,并使用编译器的 make 工具,就像其他 Qt 应用程序一样。制作过程还将通过调用-regserver
命令行选项生成的可执行文件,在系统注册表中注册控件。
如果 ActiveX 服务器是一个可执行文件,则支持以下命令行选项:
选项 | 结果 |
---|---|
-regserver | 在系统注册表中注册服务器 |
-regserverperuser | 为当前用户在系统注册表中注册服务器(自 5.14 起) |
-unregserver | 从系统注册表中取消注册服务器 |
-unregserverperuser | 为当前用户从系统注册表中取消注册服务器(自 5.14 版起) |
-activex | 将应用程序作为 ActiveX 服务器启动 |
-dumpidl <file> -version x.y | 将服务器 IDL 写入指定文件。类型库的版本为 x.y |
进程内服务器可使用所有 Windows 系统上的regsvr32
工具注册。
典型编译时问题
列出的编译器/链接器错误基于 Microsoft Visual C++ 6.0 编译器发布的错误。
"没有重载函数需要 2 个参数
当该错误出现在使用QAXCLASS() 或 QAXFACTORY_DEFAULT() 宏的代码中时,部件类没有可被默认工厂使用的构造函数。要么添加一个标准部件构造函数,要么实现一个不需要构造函数的自定义工厂。
当错误发生在使用QAXFACTORY_EXPORT() 宏的代码中时,QAxFactory 子类没有适当的构造函数。为工厂类提供一个公共类构造函数,如
的构造函数。
"语法错误:数字后缀错误
唯一标识符没有以字符串形式传入QAXFACTORY_EXPORT(),QAXFACTORY_BEGIN() 或 QAXFACTORY_DEFAULT() 宏。
"外部符号 _ucm_instantiate 未解决"。
服务器没有导出QAxFactory 的实现。请使用项目实现文件中的QAXFACTORY_EXPORT() 宏实例化并导出工厂,或使用QAXCLASS() 或 QAXFACTORY_DEFAULT() 宏使用默认工厂。
"_ucm_initialize 已在...中定义"。
服务器导出了多个QAxFactory 的实现,或导出了两次相同的实现。如果使用默认工厂,则QAXFACTORY_BEGIN() 或 QAXFACTORY_DEFAULT() 宏只能在项目中使用一次。如果服务器提供多个 ActiveX 控件,请使用自定义QAxFactory 实现和QAXFACTORY_EXPORT() 宏。
分发 QAxServer 二进制文件
使用 Qt 编写的 ActiveX 服务器可将 Qt 用作共享库,或将 Qt 静态链接到二进制文件中。这两种方式都会产生相当大的软件包(要么服务器二进制文件本身变得很大,要么你必须发送 Qt 动态链接库)。
安装独立服务器
如果 ActiveX 服务器也可作为独立应用程序运行,则在目标系统上安装可执行文件后,使用-regserver
命令行参数运行服务器可执行文件。之后,ActiveX 客户端就可以使用服务器提供的控件了。
安装进程内服务器
如果 ActiveX 服务器是安装包的一部分,请使用 Microsoft 提供的regsvr32
工具在目标系统上注册控件。如果没有该工具,请将 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
在互联网上分发服务器
如果要在网页中使用服务器中的控件,就必须让浏览器可以使用服务器来浏览网页,而且必须在网页中指定服务器软件包的位置。
要指定服务器的位置,请使用网站 OBJECT 标签中的 CODEBASE 属性。该值可以指向服务器文件本身、列出服务器所需的其他文件(如 Qt DLL)的 INF 文件或压缩的 CAB 存档。
几乎所有关于 ActiveX 和 COM 编程的书籍以及 MSDN 库和其他各种在线资源都对 INF 和 CAB 文件进行了说明。示例包括可用于构建 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>
如果网页浏览器支持脚本,则使用 JavaScript、VBScript 和窗体来编写控件脚本。ActiveQt 示例包括示例控件的 HTML 演示页面。
支持和不支持的 ActiveX 客户端
以下内容主要基于我们自己对 ActiveX 控件和客户端应用程序的实验,并不完整。
支持的客户端
这些标准应用程序可与使用 ActiveQt 开发的 ActiveX 控件配合使用。请注意,某些客户端只支持进程内控件。
- Internet Explorer
- 微软 ActiveX 控件测试容器
- 微软 Visual Studio 6.0
- 微软 Visual Studio.NET/2003
- 微软 Visual Basic 6.0
- 基于 MFC 和 ATL 的容器
- Sybase PowerBuilder
- 基于 ActiveQt 的容器
支持 Microsoft Office 应用程序,但需要将控件注册为 "可插入 "对象。重新实现QAxFactory::registerClass ,将此属性添加到 COM 类中,或使用Q_CLASSINFO 宏将 "可插入 "类信息设置为 "是"。
不支持的客户端
我们无法使基于 ActiveQt 的 COM 对象与以下客户端程序一起使用。
- Borland C++ Builder(版本 5 和 6)
- Borland Delphi
典型的运行时错误
服务器无响应
如果系统无法启动服务器(使用任务管理器检查服务器是否运行进程),请确保系统路径中没有丢失服务器所依赖的 DLL(例如 Qt DLL!)。使用依赖关系跟踪器查看服务器二进制文件的所有依赖关系。
如果服务器正在运行(例如,任务管理器列出了一个进程),请参阅以下部分,了解有关调试服务器的信息。
对象无法创建
如果服务器在构建过程中可以正确构建和注册,但对象无法被初始化,例如无法被 OLE/COM Object Viewer 应用程序初始化,请确保系统路径中没有缺少服务器所依赖的 DLL(例如 Qt DLL)。使用依赖关系步行器查看服务器二进制文件的所有依赖关系。
如果服务器已运行,请参阅以下部分了解调试服务器的信息。
卸载和重新加载 COM 服务器时崩溃
如果 ActiveQt COM 服务器使用的 Qt 模块超出了 Qt Base 中的模块,则有必要将 COM 服务器作为进程外 COM 服务器激活。如果试图激活包含Qt Quick 等模块的进程内 COM 服务器,可能会在卸载 COM 服务器后导致崩溃。
在向外调用 COM 时发生崩溃或意外行为
请注意,进程外 COM 服务器在向客户端执行向外调用时将处理其消息队列。如果客户端同时调用服务器,这可能会导致意外行为或崩溃。在这种情况下,服务器将在传出调用返回之前执行传入调用。特别是,如果客户端关闭了一个 ActiveX 控件,而该控件正在调回客户端,就会导致崩溃。这种重入问题可以使用消息过滤器(IMessageFilter 和 CoRegisterMessageFilter)来缓解。
调试运行时错误
要在 Visual Studio 中调试进程中的服务器,可将服务器项目设置为活动项目,并在项目设置中指定客户端 "调试会话的可执行文件"(例如使用 ActiveX 测试容器)。您可以在代码中设置断点,如果安装了调试版本,还可以进入 ActiveQt 和 Qt 代码。
要调试可执行服务器,请在调试器中运行应用程序,并使用命令行参数-activex
启动。然后启动客户端并创建 ActiveX 控件实例。在下一个客户端尝试创建 ActiveX 控件时,COM 将使用现有进程。
类信息和调整
要为每个 COM 类提供属性,请使用Q_CLASSINFO 宏,它是 Qt XML 元对象系统的一部分。
关键字 | 值的含义 |
---|---|
版本 | 类的版本(默认为 1.0) |
描述 | 描述类的字符串。 |
类 ID | 类 ID。如果未指定,则必须重新实现QAxFactory::classID 。 |
接口 ID | 接口 ID。如果未指定,则必须重新实现QAxFactory::interfaceID 。 |
事件 ID | 事件接口 ID。如果未指定,则不会将信号作为 COM 事件公开。 |
默认属性 | 指定的属性表示该类的默认属性。例如,按钮的默认属性是 "文本"。 |
默认信号 | 指定的信号表示该类的默认信号。例如,按钮的默认信号是 "clicked(点击)"。 |
许可证密钥 | 创建对象需要指定的许可证密钥。密钥可以为空,以要求使用已获许可的机器。默认情况下,类没有许可证。另请参阅以下部分。 |
库存事件 | 如果值为 "yes",对象将公开股票事件。请参见QAxFactory::hasStockEvents() |
至超类 | 对象显示所有超类的功能,直至并包括值中的类名。请参见QAxFactory::exposeToSuperClass() |
可插入 | 如果值为 "是",该类将注册为 "可插入 "类,并在 OLE 2 容器(即 Microsoft Office)中列出。默认情况下不设置该属性。 |
可聚合 | 如果值为 "否",则该类不支持聚合。默认情况下支持聚合。 |
可创建 | 如果该属性的值为 "否",则客户端无法创建该类,只能通过其他类的应用程序接口来创建(即该类是一个子类型)。 |
注册对象 | 如果值为 "是",则该类的对象将在 OLE 中注册,并可从运行中的对象表中访问(即客户端可以连接到已在运行的该类实例)。只有进程外服务器才支持该属性。 |
MIME | 对象可以处理值中指定格式的数据和文件。值的格式为 mime:extension:description。多个格式之间用分号隔开。 |
共用类名 | 在生成的 IDL 和注册表中使用的类名。这对于存在于命名空间中的 C++ 类特别有用--默认情况下,ActiveQt 只是移除":: "使 IDL 能够编译。 |
已实现的类别 | 逗号分隔的类别 ID (CATID) UUID 列表。除 "控制"、"可插入 "等之外,用于指定附加容器功能的通用机制。典型的 CATID 包括CATID_InternetAware ("{0DE86A58-2BAA-11CF-A229-00AA003D7352}")、CATID_SafeForScripting ("{7DD95801-9882-11CF-9FA9-00AA006C42C4}")以及用户定义的 CATID 值。 |
请注意,键和值都区分大小写。
下面声明了一个仅公开自己 API 的类的 2.0 版本,该类可在 Microsoft Office 应用程序的 "插入对象 "对话框中使用。
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); ... };
开发许可组件
如果开发组件,您可能希望控制谁可以实例化这些组件。由于服务器二进制文件可以发送到任何客户机并在其上注册,因此任何人都可以在自己的软件中使用这些组件。
可以使用多种技术对组件进行授权,例如,创建控件的代码可以提供一个授权密钥,或者需要对运行控件的机器进行授权。
要将 Qt 类标记为授权类,可使用Q_CLASSINFO() 宏指定一个 "LicenseKey"。
class MyLicensedControl : public QWidget { Q_OBJECT Q_CLASSINFO("LicenseKey", "<key string>") ... };
要在本身未获得许可的机器上创建MyLicensedControl
实例,就必须使用该密钥。获得许可的开发人员现在可以将服务器二进制文件与其应用程序一起重新发布,该应用程序将使用 "LicenseKey "的值创建控件,而应用程序的用户则不能在没有许可证密钥的情况下创建控件。
如果控件只有一个许可证密钥还不够(即希望不同的开发人员获得不同的许可证密钥),可以指定一个空密钥来表示控件需要许可证,并重新实现QAxFactory::validateLicenseKey() 来验证系统中是否存在许可证(即通过许可证文件)。
更多接口
ActiveQt 服务器提供的 ActiveX 控件支持一套最基本的 COM 接口,以实现 OLE 规范。当 ActiveX 类继承自QAxBindable 类时,它还可以实现其他 COM 接口。
创建QAxAggregated 的新子类,并使用多重继承对其他 COM 接口类进行子类化。
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
的子类,因此您必须实现QueryInterface()
、AddRef()
和Release()
函数。为此,请在类定义中使用 QAXAGG_IUNKNOWN 宏。如果手动实现IUnknown
函数,请将调用委托给QAxAggregated::controllingUnknown() 函数返回的接口指针,例如
HRESULT AxImpl::QueryInterface(REFIID iid, void **iface) { return controllingUnknown()->QueryInterface(iid, iface); }
在您的queryInterface() 实现中不支持IUnknown
接口本身。
实现 COM 接口的方法,如果需要调用实现控件的QObject 子类,请使用QAxAggregated::object() 。
在QAxBindable 子类中,实现QAxBindable::createAggregate() 以返回QAxAggregated 子类的新对象。
class MyActiveX : public QWidget, public QAxBindable { Q_OBJECT public: MyActiveX(QWidget *parent); QAxAggregated *createAggregate() { return new AxImpl(); } };
另请参阅 ActiveQt 框架。
© 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.