由于最近需要使用ActiveX,一般来说可以使用微软提供的MFC或者ATL框架来开发,由于我个人对这部分内容不是很熟悉,好在Qt也提供对于ActiveX的支持。本文主要记录个人学习ActiveX的一些内容,方便日后查阅。本文以Qt5(5.3.1)提供的ActiveX为参考,但是由于ActiveX这部分比较稳定,因此Qt4应该也是一样的。

  • 概述
Qt提供了QtActiveX模块来支持微软ActiveX的开发,Qt的ActiveX和COM的开发支持两种方式:
  1. 支持将已有的COM或者ActiveX空间引入到Qt的应用程序中
  2. 支持将Qt应用程序或者Qt的对象导出成COM对象或者ActiveX控件供他人使用
具体来说,Qt是通过ActiveXQt框架中的两个模块来支持上述所说的两种方式的:
  1. 使用QAxContainer模块,通过QAxObject和QAxWidget分别支持COM对象和ActiveX控件的开发,可以通过这两个对象将外部的COM或者ActiveX组件接入到Qt应用程序
  2. 使用QAxServer模块,通过QAxAggregated、QAxBindable和QAxFactory类,通过了进程内和可执行程序exe两种方式的COM Server模式,用来将Qt写的内容导出为COM或者ActiveX供他人使用。
下图简要的说明了QtActiveX的作用
  • 使用QtActiveX创建COM或ActiveX Server
在正式开始之前先对COM和ActiveX做一个简要的对比。COM(Component Object Model)是微软提出的一种技术,它定义了一种规范,通过COM可以轻松实现一种语言(如C#)调用另一种语言(如C++、VB等)开发的功能模块。ActiveX是微软主要针对互联网客户端设计的以COM为技术基础的一种实现,一般来说二者并没有本质的区别,仅有一些概念上的差异,一般来说:
1. ActiveX一般包含一个窗体界面,COM对象一般并没有界面
2. COM对象一般作为一个可调用的模块来使用,ActiveX一般嵌入在网页中使用
上述仅仅是一种使用上的惯例,但是并未强制一定这样
 
使用Qt作COM和ActiveX的开发需要使用QAxServer模块,这里面包含三个类:
1. QAxFactory定义了创建COM对象的工厂类
2. QAxBindable定义了COM对象与Qt对象之间的转换关系,也就是说Qt中的对象通过QAxBindable转换为COM中的要素
3. QAxAggregated定义了COM组件接口
 
  • Qt作为Server支持的模式
COM组件在开发出来之后有多种形式,可以是一个dll,也可以是一个exe可执行程序。可以在进程中被加载(一般最常用的模式),可以作为外部进程为其他进程提供服务,甚至可以是远程服务器上的进程为本地集成提供服务。Qt ActiveX 提供了In-Process和out-of-process executable两种方式的支持。简单来说就是提供了一种 Dll供应用程序调用也提供了一种可以作为运行的exe,为其他应用提供一些调用服务。
 
当作为独立exe时候,我们需要这样编写.pro文件:
  1. TEMPLATE = app
  2. QT  += axserver
  3. RC_FILE  = qaxserver.rc

当作为进程内dll的时候,需要这样写.pro文件

  1. TEMPLATE = lib
  2. QT += axserver
  3. CONFIG  += dll
  4. DEF_FILE = qaxserver.def
  5. RC_FILE  = qaxserver.rc
  6. ...

但是我们不用去操心这些内容,因为一般我们是用向导来生成Qt工程的,当你勾选ActiveX Server模式的时候,向导已经帮你写好这些内容了。

当使用QAxServer开发dll时,实际工程编译链接过程中会涉及到以下的过程:
1. 应用程序将会链接到qtserver.lib而不是qtmain.lib
2. idc工具会被调用,产生IDL文件(接口描述语言的接口描述文件)
3.调用MIDL工具编译IDL文件到类型库
4.调用idc工具将类型库附到server的二进制代码中
5. 注册dll
另外在.Pro文件中可以添加一个版本信息,这个版本信息会作类型库和server dll的版本号,添加方式使用VERSION变量即可:
  1. TEMPLATE = lib
  2. VERSION = 2.5
  3. ...
  • COM进程外和进程内使用方式的比较
进程外的使用方式是将COM写成一个exe可执行文件,当它运行的时候其他程序可以调用它提供的接口来开发,这样做如果某个调用它的程序出了bug,那么只有该程序会崩溃,其他调用不收影响,提供了更好的隔离性。缺点是需要更长的启动时间与跨进程通信的一些额外负担。
进程内的使用方式就很简单了,调用过程仅仅是通过类似虚函数这样的调用,需要加载的时间短、效率比较高。
  • 开发Server的过程
为了使用Qt实现COM对象,我们必须使用一个QObject的子类,如果该子类是QWidget的子类,那么这个COM对象就是一个ActiveX控件。代码如下:
  1. #include <QWidget>
  2. class MyActiveX : public QWidget
  3. {
  4. Q_OBJECT

只里面Q_OBJECT宏不能少,它提供了关于类MyActiveX的一些元数据信息, 接下来继续添加ActiveX的一些信息

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

Q_CLASSINFO定义了COM组件的一些信息,这里面ClassID和InterfaceID是必须的,当你需要使用COM中的事件时,EventsID就需要添加进来。里面的128位的字符串使用GUID.exe工具生成(微软提供的一个小工具,可以生成全球唯一的标识符,可以在系统文件夹或者visual Studio的工具菜单中找到),工具提供了多种模式,可以选择需要的模式复制添加到代码中:

除了上述基本必要的信息之外(ClassID实际上被写入注册表中,当控件被使用的时候会搜索注册表找到里面该COM控件dll的位置并加载,可以使用regedit.exe在HKEY_CLASSES_ROOT中找到很多已经注册的COM组件信息),Qt还提供了Q_CLASSINFO更多的内容,如下表所示:
名称 值和含义
Version 类的版本号,默认值是1.0
Description 类的描述
ClassID                                                  类的ID(COM中用来唯一确定一个类的方式)
InterfaceID 接口ID(COM中用来唯一确定接口的方式)
EventsID 事件ID
DefaultProperty 默认属性
DefaultSignal 默认的时间
LicenseKey 类的许可号,默认未开启,如果开启使用类需要许可号
StockEvents TODO:???
ToSuperClass 暴露父类的接口
Insertable 设置"yes"后可以被列到OLE2容器中,默认未设置
Aggregatable 默认是"yes",COM支持聚合
Creatable 设置为“no”调用者不能使用该类
RegisterObject 仅能用在进程外方式的COM中
MIME 该COM控件支持的文件格式描述
CoClassAlias 类的名称在IDL中被修改为CoClassAlias指定的名字

继续上面的代码,接下来可以添加一些属性到COM组件中,可以使用另一个宏Q_PROPERTY

  1. Q_PROPERTY(int value READ value WRITE setValue)

之后可以像写Qt程序那样来完成。

  1. public:
  2. MyActiveX(QWidget *parent = 0)
  3. ...
  4. int value() const;
  5. public slots:
  6. void setValue(int v);
  7. ...
  8. signals:
  9. void valueChange(int v);
  10. ...
  11. };

Qt的ActiveX框架会将Qt类中的要素转换为COM中的标准要素供其他调用者使用,具体来说:

 
 1.  Qt类中的属性和公有的插槽函数(slots)会被转换为COM中的属性和方法
 2.  Qt类中的信号(signals)会被转换成为COM组件中的事件
另外其他的数据类型转换之间的对应关系如下图所示:
 
 1. Qt中属性的数据类型与COM中数据类型的转换关系如下:
 
Qt数据类型 COM 属性数据类型
bool VARIANT_BOOL
QString BSTR
int int
uint unsigned int
double double
qlonglong CY
qulonglong CY
QColor OLE_COLOR
QDate DATE
QDateTime DATE
QTime DATE
QFont IFontDisp*
QPixmap IPixtureDisp*
QVariant VARIANT
QVariantList            SAFEARRAY(VARIANT)
QStringList SAFEARRAY(BSTR)
QByteArray SAFEARRAY(BYTE)
QRect User defined type
QSize User defined type
QPoint User defined type
2. Qt中信号和插槽函数的形式参数数据类型
Qt数据类型 对应COM的数据类型
bool [in] VARIANT_BOOL
bool& [in,out] VARIANT_BOOL*
QString, const Qtring& [in] BSTR
QString& [in, out] BSTR*
QStinrg& [in, out] BSTR*
int [in] int
int& [in, out]int
uint [in,out]unsigned int
uint& [int, out] unsigned int*
double [in] 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> [in]SAFEARRAY(VARIANT)
QList<QVariant>& [in,out]SAFEARRAY(VARIANT)*
QObject* [in] IDispatch*
此外QActiveX也可以通过Q_ENUMS和Q_FLAGS将枚举变量导出,如果使用了非上表中的类型来作为信号和插槽函数的参数类型,那么Qt ActiveX框架不会转换这些参数。
  • 发布COM控件
为了让Qt编写的代码可以作为一个COM组件被使用,必须提供获取COM对象的方法,在Qt中使用QAxFactory来完成,最简单的方法是使用下面的宏:
  1. QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}",
  2. "{a8f21901-7ff7-4f6a-b939-789620c03d83}")
  3. QAXCLASS(MyWidget)
  4. QAXCLASS(MyWidget2)
  5. QAXTYPE(MySubType)
  6. QAXFACTORY_END()

上面这段代码把MyWidget和MyWidget2导出为可供外部调用的COM对象,并注册MySubType类型可供MyWidget和MyWidget2中的属性和参数使用。

  • 进程外COM控件的编写
对于进程外可执行的COM组件,我们需要执行一个main函数来实例化一个QApplication,和一般的Qt程序类似。
  1. #include <QApplication>
  2. #include <QAxFactory>
  3. int main(int argc, char *argv[])
  4. {
  5. QApplication app(argc, argv);
  6. if (!QAxFactory::isServer()) {
  7. // create and show main window
  8. }
  9. return app.exec();
  10. }

http://blog.csdn.net/csxiaoshui/article/category/5705027

http://download.csdn.net/detail/csxiaoshui/9728972

Qt中使用ActiveX(3篇)的更多相关文章

  1. 在Qt中使用ActiveX控件

    Qt的windows商业版本提供了ActiveQt这个framework,使用这个组件我们可以在Qt中使用ActiveX控件,并且也开发基于Qt的ActiveX控件.ActiveQt包含了两个组件QA ...

  2. Qt中使用ActiveX控件

    (转自:http://blog.csdn.net/tingsking18/article/details/5403038) 在Qt中使用ActiveX控件 Qt的windows商业版本提供了Activ ...

  3. Qt学习日记篇-Qt中使用Curl和jsonCpp

    1.Qt中安装并使用jsonCPP库 1.1  官网下载.https://sourceforge.net/projects/jsoncpp/    解压文件得到 jsoncpp-src-0.5.0 文 ...

  4. Qt开发Activex笔记(二):Qt调用Qt开发的Activex控件

    若该文为原创文章,转载请注明原文出处本文章博客地址:https://blog.csdn.net/qq21497936/article/details/113789693 长期持续带来更多项目与技术分享 ...

  5. 如何向VS2010中插入ActiveX控件并且附带相应的类

    上两篇文章中我们已经讲述了ActiveX控件的一些相关知识,本文中,简单说明一下如何在我们自己的程序中使用ActiveX控件.(仍以我们上节课的例子为例) 我们打开VS2010编辑器,新建一个基于对话 ...

  6. 解析Qt中QThread使用方法

    本文讲述的是在Qt中QThread使用方法,QThread似乎是很难的一个东西,特别是信号和槽,有非常多的人(尽管使用者本人往往不知道)在用不恰当(甚至错误)的方式在使用QThread,随便用goog ...

  7. Qt中使用QProcess备份和恢复Mysql数据库

    分类: Qt2011-02-18 21:35 1395人阅读 评论(3) 收藏 举报 qtmysql数据库windowspathcmd . 使用Qt做MySQL数据库开发,遇到需要备份.还原数据库的问 ...

  8. 在QT中使用Irrlicht引擎的方法与步骤

      Ø 相关库,插件安装部分 本篇文档介绍在Qt5.2.0下面使用lrrlicht引擎在Qt窗口中输出(开发环境:vs2012) 1. 首先安装好Qt5.2.0,下载地址: http://downlo ...

  9. Qt中调用PolarSSL库(一)

    最近一直在学习SSL相关的知识,也是先了解理论相关的知识,主要是SSL相关的基本概念和连接建立过程,主要是基于PolarSSL开源库进行学习.学习完了之后就希望能给有所运用,就想用Qt写一个简单的程序 ...

随机推荐

  1. mark元素

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. glup Browsersync

    http://www.browsersync.cn/#installhttp://www.browsersync.cn/docs/gulp/http://www.mamicode.com/info-d ...

  3. 新提交审核app保留检查更新入口将被拒绝(读取App Store 版本号的)

    3月起要求关闭所有App内的检查更新功能,苹果App Store将向用户自动提示更新,新版本提交审核版本如果保留检查更新入口,审核时,将被拒绝. 以上拒绝的方案: 读取App Store的版本号. 替 ...

  4. Coder-Strike 2014 - Round 1 D. Giving Awards

    题目的意思是 老板给n个人发工资,x欠y的工资,the joy of person x from his brand new money reward will be much less, 老板想避免 ...

  5. HDU - The Suspects

    Description 严重急性呼吸系统综合症( SARS), 一种原因不明的非典型性肺炎,从2003年3月中旬开始被认为是全球威胁.为了减少传播给别人的机会, 最好的策略是隔离可能的患者. 在Not ...

  6. SVN版本控制工具使用学习

    SVN版本控制工具使用学习 Subversion是优秀的版本控制工具. 1.下载和搭建SVN服务器 http://subversion.apache.org/packages.html 类型有5种,推 ...

  7. linux 下如何打开core dump文件开关

    dump文件可以在程序crash时,方便我们查看程序crash的地方和上下文信息.在window下,要能生成dump文件,需要自己编写相应的代码.不过现在网上可以找到相应的代码,只要把它下载后然后加到 ...

  8. IEqualityComparer<T>

    在linq中使用union和distinct都不起作用,结果发现必须传入一个实现了IEqualityComparer<T>的比较器 public class CompareUser : I ...

  9. MySQL连接池

    1. using System; using System.Collections; using MySql.Data.MySqlClient; namespace Helper { /// < ...

  10. 【Go语言】集合与文件操作

    本文目录 1.数据集合的主要操作 1_1.字典的声明 1_2.字典的初始化和创建 1_3.字典的访问和操作 1_4.其他类型的数据集 2.文件操作 2_1.文件操作概述os包和path包 2_2.文件 ...