在Qt中构建ActiveX服务器

QAxServer 模块是 ActiveQt 框架的一部分。它包含三个类

  • QAxFactory 定义了COM对象的创建工厂。
  • QAxBindable 提供了Qt小部件和COM对象之间的接口。
  • QAxAggregated 可以被继承以实现额外的COM接口。

提供了一些 ActiveX控件和COM对象的示例实现

主题

使用库

要将标准Qt应用程序转换为使用 QAxServer 库的COM服务器,您必须在您的 .pro 文件中将 axserver 添加到QT变量中。

从这样的 .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.rcqaxserver.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使用在“目录”选项中指定的环境设置调用服务器)。确保您的服务器所需的全部DLL和插件都位于错误消息框中列出的目录中(有关更多信息,请参阅Windows部署工具)。

"无法打开文件 ..."

当最后一个客户端停止使用ActiveX服务器时,服务器无法正确关闭。通常应用程序终止需要大约两秒钟,但您可能必须使用任务管理器来终止进程(例如,当客户端未正确释放控件时)。

控件无法实例化

在这种情况下,将服务器作为管理员注册可能有所帮助。

实现控件

要在 Qt 中实现 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 标识符。需要 ClassIDInterfaceID,而 EventsID 仅有在您的对象有信号时才是必要的。生成这些标识符,可以使用类似于 uuidgenguidgen 的系统工具。

可以为每个类指定额外的属性;有关详细信息,请参阅 类信息和调整

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 属性
boolVARIANT_BOOL
QStringBSTR
intint
uintunsigned int
doubledouble
qlonglongCY
qulonglongCY
QColorOLE_COLOR
QDateDATE
QDateTimeDATE
QTimeDATE
QFontIFontDisp*
QPixmapIPictureDisp*
QVariantVARIANT
QVariantList(与 QList<QVariant> 相同)SAFEARRAY(VARIANT)
QStringListSAFEARRAY(BSTR)
QByteArraySAFEARRAY(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&[输入输出] 结构 QSize (用户定义)
QPoint&[输入输出] 结构 QPoint (用户定义)

还支持导出的枚举和标志(见 Q_ENUM() 和 Q_FLAG())。输入参数类型也支持作为返回值。

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 系统可用,必须在系统注册表中使用五个唯一标识符进行注册。这些标识符由类似 guidgenuuidgen 的工具提供。注册信息允许 COM 本地化提供请求的 ActiveX 控件的二进制文件,将远程过程调用封装到控件中,并读取控件公开的方法和属性的类型信息。

当客户端请求创建 COM 对象时,服务器必须导出一个 QAxFactory 的实现。做到这一点最简单的方法是使用一系列宏

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

这将导出 MyWidgetMyWidget2 作为 COM 对象,这些对象可以由 COM 客户端创建,并将 MySubType 注册为可以在 MyWidgetMyWidget2 的属性和参数中使用类型。

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 提供了默认的主函数实现。默认实现调用 QAxFactory::startServer(),创建一个 QApplication 实例并调用 exec()。

要构建 ActiveX 服务器可执行文件,请运行 qmake 生成 makefile,并使用编译器的 make 工具像任何其他 Qt 应用程序一样。构建过程还将通过使用 -regserver 命令行选项调用生成的可执行文件来在系统注册表中注册控件。

如果 ActiveX 服务器是一个可执行文件,则支持以下命令行选项

选项结果
-regserver在系统注册表中注册服务器
-regserverperuser自5.14版起,在系统注册表中为当前用户注册服务器
-unregserver从系统注册表中注销服务器
-unregserverperuser自5.14版起,从系统注册表中为当前用户注销服务器
-activex以ActiveX服务器的形式启动应用程序
-dumpidl <文件> -版本 x.y将服务器的IDL写入指定的文件。类型库将具有版本x.y

可以使用所有Windows系统上可用的regsvr32工具注册进程内服务器。

典型的编译时问题

所列出的编译器/链接器错误基于Microsoft Visual C++ 6.0编译器发布的错误。

“没有重载的函数使用2个参数”

当在代码中使用QAXCLASS()或QAXFACTORY_DEFAULT()宏时发生错误,表示的窗口小部件类没有可以使用默认工厂的构造函数。要么添加标准窗口小部件构造函数,要么实现一个不需要的定制工厂。

当使用QAXFACTORY_EXPORT()宏,QAxFactory子类没有适当的构造函数。为您的工厂类提供公共类构造函数

MyFactory(const QUuid &, const QUuid &);

“语法错误:数字的后缀不正确”

唯一标识符未作为字符串传递到QAXFACTORY_EXPORT()、QAXFACTORY_BEGIN()或QAXFACTORY_DEFAULT()宏。

“未解决的外部符号 _ucm_instantiate”

服务器未导出QAxFactory实现。在项目的实现文件中使用QAXFACTORY_EXPORT()宏来实例化和导出一个工厂,或者使用QAXCLASS()或QAXFACTORY_DEFAULT()宏来使用默认工厂。

"_ucm_initialize 已在 ... 中定义"

服务器导出了同一个QAxFactory的实现多次,或者导出了多个QAxFactory实现。如果您使用默认工厂,项目中的QAXFACTORY_BEGIN()或QAXFACTORY_DEFAULT()宏只能使用一次。如果服务器提供了多个ActiveX控件,请使用定制的QAxFactory实现和QAXFACTORY_EXPORT()宏。

分发QAxServer二进制文件

用Qt编写的ActiveX服务器可以使用Qt作为共享库,或者将Qt静态链接到二进制文件中。这两种方式都会生成相当大的包(要么服务器二进制文件本身变得很大,要么必须运送Qt DLL)。

安装独立服务器

当您的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

在互联网上分发服务器

如果您想在您的服务器在网页中使用的控件,需要将服务器提供给查看您页面的浏览器,并在您的页面上指定服务器包的位置。

要指定服务器位置,请在您的网站的对象标签中使用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

Microsoft的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控件和客户端应用程序的实验结果,并不完整。

受支持客户端

以下标准应用程序与ActiveQt开发的ActiveX控件一起工作。请注意,一些客户端只支持进程内控件。

  • Internet Explorer
  • Microsoft ActiveX控件测试容器
  • Microsoft Visual Studio 6.0
  • Microsoft Visual Studio.NET/2003
  • Microsoft 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 对象查看器应用程序),请确保系统中没有缺失服务器依赖的 DLL(例如 Qt DLL)。使用依赖关系跟踪器查看服务器二进制文件的所有依赖项。

如果服务器正在运行,请参阅以下部分以获取有关调试服务器的信息。

卸载和重新加载 COM 服务器时崩溃

如果 ActiveQt COM 服务器使用了 Qt 基础库之外的 Qt 模块,则需要将 COM 服务器激活为一个进程外 COM 服务器。尝试激活包含 Qt Quick 等模块的进程内 COM 服务器可能导致在卸载 COM 服务器后崩溃。

在出站 COM 调用期间崩溃或出现意外行为

请注意,进程外 COM 服务器在执行对客户端的出站调用时将正在处理其消息队列。如果在同一时间客户端也对服务器进行调用,这可能导致意外行为或崩溃。在这种情况下,入站调用将在出站调用返回之前在服务器上执行。特别是,如果客户端在控件调用客户端时关闭 ActiveX 控件,这可能导致崩溃。可以使用消息筛选器(IMessageFilter 和 CoRegisterMessageFilter)来减轻这种重入问题。

调试运行时错误

要在 Visual Studio 中调试进程内服务器,请将服务器项目设置为活动项目,并在项目设置中指定客户端“调试会话的可执行文件”(例如,使用 ActiveX 测试容器)。您可以在代码中设置断点,并且如果您安装了调试版本,还可以步入 ActiveQt 和 Qt 代码。

要调试可执行服务器,请在调试器中运行应用程序,并以命令行参数 -activex 开始。然后启动您的客户端并创建 ActiveX 控件的实例。COM 将使用现有进程为尝试创建 ActiveX 控件的下一个客户端。

类信息和调优

要为每个 COM 类提供属性,请使用 Q_CLASSINFO 宏,它是 Qt 元对象系统的一部分。

值的含义
版本类的版本(默认为 1.0)
描述描述类的字符串。
类 ID类 ID。如果没有指定,您必须重新实现 QAxFactory::classID。
接口 ID接口 ID。如果没有指定,您必须重新实现 QAxFactory::interfaceID。
事件 ID事件接口 ID。如果没有指定,则不暴露任何信号作为 COM 事件。
默认属性指定的属性代表类的默认属性,例如,按钮的默认属性是“text”。
默认信号指定的信号代表类的默认信号,例如,按钮的默认信号是“clicked”。
许可证密钥创建对象需要指定的许可证密钥。密钥可以留空以要求授权机器。默认情况下,类不要求授权。有关更多信息,请参阅以下部分。
库存事件如果值为“是”,则对象会公开库存事件。请参阅 QAxFactory::hasStockEvents()
要超级类如果值为类名,则对象会公开所有超级类的功能以及包括类名本身在内。请参阅 QAxFactory::exposeToSuperClass()
可插入如果值是"是",则类注册为"可插入",并将在 OLE 2 容器中列出(即 Microsoft Office)。此属性默认不设置。
可聚合如果值是"否",类不支持聚合。默认支持聚合。
可创建如果值是"否",客户端无法创建该类,它只通过另一个类的 API(例如,该类是子类型)可用。
RegisterObject如果值是"是",该类的对象将注册到 OLE 并可在运行对象表中访问(即客户端可以连接到该类的已运行实例)。此属性只在进程外服务器中受支持。
MIME对象可以处理值中指定的格式指定的数据和文件。值具有格式 mime:extension:description。多个格式由分号分隔。
CoClassAlias在生成的 IDL 和注册表中使用的类名。这对于在命名空间中存在的 C++ 类特别有用 - 默认情况下,ActiveQt 仅删除 "::" 以使 IDL 能够编译。
实现类别逗号分隔的类别 ID (CATID) UUID 列表。这是指定附加容器功能(除了"控制"、"可插入"等)的通用机制。典型的 CATID 包括 CATID_InternetAware ("{0DE86A58-2BAA-11CF-A229-00AA003D7352}"),CATID_SafeForScripting ("{7DD95801-9882-11CF-9FA9-00AA006C42C4}") 以及用户定义的 CATID 值。

注意键和值都区分大小写。

以下声明了一个只公开其自己的 API 并可在 Microsoft Office 应用程序的"插入对象"对话框中使用的类的第 2.0 版本。

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 控件支持实现 OLE 规范的最小 COM 接口集。当 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;
}

由于ISomeCOMInterfaceIUnknown的子类,您必须实现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框架

© 2024 Qt公司。此处包含的文档贡献的版权归各自所有者所有。此处提供的文档是根据自由软件基金会发布的GNU自由文档许可协议版本1.3的条款授予许可的。Qt及其相应标志是芬兰和/或其他国家的Qt公司在世界范围内的商标。所有其他商标均为各自所有者的财产。