qt编写activex_Qt中使用ActiveX(一)
由于最近需要使用ActiveX,一般來(lái)說(shuō)可以使用微軟提供的MFC或者ATL框架來(lái)開發(fā),由于我個(gè)人對(duì)這部分內(nèi)容不是很熟悉,好在Qt也提供對(duì)于ActiveX的支持。本文主要記錄個(gè)人學(xué)習(xí)ActiveX的一些內(nèi)容,方便日后查閱。本文以Qt5(5.3.1)提供的ActiveX為參考,但是由于ActiveX這部分比較穩(wěn)定,因此Qt4應(yīng)該也是一樣的。
概述
Qt提供了QtActiveX模塊來(lái)支持微軟ActiveX的開發(fā),Qt的ActiveX和COM的開發(fā)支持兩種方式:
支持將已有的COM或者ActiveX空間引入到Qt的應(yīng)用程序中
支持將Qt應(yīng)用程序或者Qt的對(duì)象導(dǎo)出成COM對(duì)象或者ActiveX控件供他人使用
具體來(lái)說(shuō),Qt是通過(guò)ActiveXQt框架中的兩個(gè)模塊來(lái)支持上述所說(shuō)的兩種方式的:
使用QAxContainer模塊,通過(guò)QAxObject和QAxWidget分別支持COM對(duì)象和ActiveX控件的開發(fā),可以通過(guò)這兩個(gè)對(duì)象將外部的COM或者ActiveX組件接入到Qt應(yīng)用程序
使用QAxServer模塊,通過(guò)QAxAggregated、QAxBindable和QAxFactory類,通過(guò)了進(jìn)程內(nèi)和可執(zhí)行程序exe兩種方式的COM Server模式,用來(lái)將Qt寫的內(nèi)容導(dǎo)出為COM或者ActiveX供他人使用。
下圖簡(jiǎn)要的說(shuō)明了QtActiveX的作用
使用QtActiveX創(chuàng)建COM或ActiveX Server
在正式開始之前先對(duì)COM和ActiveX做一個(gè)簡(jiǎn)要的對(duì)比。COM(Component Object Model)是微軟提出的一種技術(shù),它定義了一種規(guī)范,通過(guò)COM可以輕松實(shí)現(xiàn)一種語(yǔ)言(如C#)調(diào)用另一種語(yǔ)言(如C++、VB等)開發(fā)的功能模塊。ActiveX是微軟主要針對(duì)互聯(lián)網(wǎng)客戶端設(shè)計(jì)的以COM為技術(shù)基礎(chǔ)的一種實(shí)現(xiàn),一般來(lái)說(shuō)二者并沒(méi)有本質(zhì)的區(qū)別,僅有一些概念上的差異,一般來(lái)說(shuō):
1. ActiveX一般包含一個(gè)窗體界面,COM對(duì)象一般并沒(méi)有界面
2. COM對(duì)象一般作為一個(gè)可調(diào)用的模塊來(lái)使用,ActiveX一般嵌入在網(wǎng)頁(yè)中使用
上述僅僅是一種使用上的慣例,但是并未強(qiáng)制一定這樣
使用Qt作COM和ActiveX的開發(fā)需要使用QAxServer模塊,這里面包含三個(gè)類:
1. QAxFactory定義了創(chuàng)建COM對(duì)象的工廠類
2. QAxBindable定義了COM對(duì)象與Qt對(duì)象之間的轉(zhuǎn)換關(guān)系,也就是說(shuō)Qt中的對(duì)象通過(guò)QAxBindable轉(zhuǎn)換為COM中的要素
3. QAxAggregated定義了COM組件接口
Qt作為Server支持的模式
COM組件在開發(fā)出來(lái)之后有多種形式,可以是一個(gè)dll,也可以是一個(gè)exe可執(zhí)行程序。可以在進(jìn)程中被加載(一般最常用的模式),可以作為外部進(jìn)程為其他進(jìn)程提供服務(wù),甚至可以是遠(yuǎn)程服務(wù)器上的進(jìn)程為本地集成提供服務(wù)。Qt ActiveX 提供了In-Process和out-of-process executable兩種方式的支持。簡(jiǎn)單來(lái)說(shuō)就是提供了一種 Dll供應(yīng)用程序調(diào)用也提供了一種可以作為運(yùn)行的exe,為其他應(yīng)用提供一些調(diào)用服務(wù)。
當(dāng)作為獨(dú)立exe時(shí)候,我們需要這樣編寫.pro文件:
TEMPLATE = app
QT += axserver
RC_FILE = qaxserver.rc當(dāng)作為進(jìn)程內(nèi)dll的時(shí)候,需要這樣寫.pro文件
TEMPLATE = lib
QT += axserver
CONFIG += dll
DEF_FILE = qaxserver.def
RC_FILE = qaxserver.rc
...但是我們不用去操心這些內(nèi)容,因?yàn)橐话阄覀兪怯孟驅(qū)?lái)生成Qt工程的,當(dāng)你勾選ActiveX Server模式的時(shí)候,向?qū)б呀?jīng)幫你寫好這些內(nèi)容了。
當(dāng)使用QAxServer開發(fā)dll時(shí),實(shí)際工程編譯鏈接過(guò)程中會(huì)涉及到以下的過(guò)程:
1. 應(yīng)用程序?qū)?huì)鏈接到qtserver.lib而不是qtmain.lib
2. idc工具會(huì)被調(diào)用,產(chǎn)生IDL文件(接口描述語(yǔ)言的接口描述文件)
3.調(diào)用MIDL工具編譯IDL文件到類型庫(kù)
4.調(diào)用idc工具將類型庫(kù)附到server的二進(jìn)制代碼中
5. 注冊(cè)dll
另外在.Pro文件中可以添加一個(gè)版本信息,這個(gè)版本信息會(huì)作類型庫(kù)和server dll的版本號(hào),添加方式使用VERSION變量即可:
TEMPLATE = lib
VERSION = 2.5
...
COM進(jìn)程外和進(jìn)程內(nèi)使用方式的比較
進(jìn)程外的使用方式是將COM寫成一個(gè)exe可執(zhí)行文件,當(dāng)它運(yùn)行的時(shí)候其他程序可以調(diào)用它提供的接口來(lái)開發(fā),這樣做如果某個(gè)調(diào)用它的程序出了bug,那么只有該程序會(huì)崩潰,其他調(diào)用不收影響,提供了更好的隔離性。缺點(diǎn)是需要更長(zhǎng)的啟動(dòng)時(shí)間與跨進(jìn)程通信的一些額外負(fù)擔(dān)。
進(jìn)程內(nèi)的使用方式就很簡(jiǎn)單了,調(diào)用過(guò)程僅僅是通過(guò)類似虛函數(shù)這樣的調(diào)用,需要加載的時(shí)間短、效率比較高。
開發(fā)Server的過(guò)程
為了使用Qt實(shí)現(xiàn)COM對(duì)象,我們必須使用一個(gè)QObject的子類,如果該子類是QWidget的子類,那么這個(gè)COM對(duì)象就是一個(gè)ActiveX控件。代碼如下:
#include
class MyActiveX : public QWidget
{
Q_OBJECT只里面Q_OBJECT宏不能少,它提供了關(guān)于類MyActiveX的一些元數(shù)據(jù)信息, 接下來(lái)繼續(xù)添加ActiveX的一些信息
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組件的一些信息,這里面ClassID和InterfaceID是必須的,當(dāng)你需要使用COM中的事件時(shí),EventsID就需要添加進(jìn)來(lái)。里面的128位的字符串使用GUID.exe工具生成(微軟提供的一個(gè)小工具,可以生成全球唯一的標(biāo)識(shí)符,可以在系統(tǒng)文件夾或者visual Studio的工具菜單中找到),工具提供了多種模式,可以選擇需要的模式復(fù)制添加到代碼中:
除了上述基本必要的信息之外(ClassID實(shí)際上被寫入注冊(cè)表中,當(dāng)控件被使用的時(shí)候會(huì)搜索注冊(cè)表找到里面該COM控件dll的位置并加載,可以使用regedit.exe在HKEY_CLASSES_ROOT中找到很多已經(jīng)注冊(cè)的COM組件信息),Qt還提供了Q_CLASSINFO更多的內(nèi)容,如下表所示:
名稱
值和含義
Version
類的版本號(hào),默認(rèn)值是1.0
Description
類的描述
ClassID
類的ID(COM中用來(lái)唯一確定一個(gè)類的方式)
InterfaceID
接口ID(COM中用來(lái)唯一確定接口的方式)
EventsID
事件ID
DefaultProperty
默認(rèn)屬性
DefaultSignal
默認(rèn)的時(shí)間
LicenseKey
類的許可號(hào),默認(rèn)未開啟,如果開啟使用類需要許可號(hào)
StockEvents
TODO:???
ToSuperClass
暴露父類的接口
Insertable
設(shè)置"yes"后可以被列到OLE2容器中,默認(rèn)未設(shè)置
Aggregatable
默認(rèn)是"yes",COM支持聚合
Creatable
設(shè)置為“no”調(diào)用者不能使用該類
RegisterObject
僅能用在進(jìn)程外方式的COM中
MIME
該COM控件支持的文件格式描述
CoClassAlias
類的名稱在IDL中被修改為CoClassAlias指定的名字
繼續(xù)上面的代碼,接下來(lái)可以添加一些屬性到COM組件中,可以使用另一個(gè)宏Q_PROPERTY
Q_PROPERTY(int value READ value WRITE setValue)之后可以像寫Qt程序那樣來(lái)完成。
public:
MyActiveX(QWidget *parent = 0)
...
int value() const;
public slots:
void setValue(int v);
...
signals:
void valueChange(int v);
...
};Qt的ActiveX框架會(huì)將Qt類中的要素轉(zhuǎn)換為COM中的標(biāo)準(zhǔn)要素供其他調(diào)用者使用,具體來(lái)說(shuō):
1. ?Qt類中的屬性和公有的插槽函數(shù)(slots)會(huì)被轉(zhuǎn)換為COM中的屬性和方法
2. ?Qt類中的信號(hào)(signals)會(huì)被轉(zhuǎn)換成為COM組件中的事件
另外其他的數(shù)據(jù)類型轉(zhuǎn)換之間的對(duì)應(yīng)關(guān)系如下圖所示:
1. Qt中屬性的數(shù)據(jù)類型與COM中數(shù)據(jù)類型的轉(zhuǎn)換關(guān)系如下:
Qt數(shù)據(jù)類型
COM 屬性數(shù)據(jù)類型
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中信號(hào)和插槽函數(shù)的形式參數(shù)數(shù)據(jù)類型
Qt數(shù)據(jù)類型
對(duì)應(yīng)COM的數(shù)據(jù)類型
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
[in]SAFEARRAY(VARIANT)
QList&
[in,out]SAFEARRAY(VARIANT)*
QObject*
[in] IDispatch*
此外QActiveX也可以通過(guò)Q_ENUMS和Q_FLAGS將枚舉變量導(dǎo)出,如果使用了非上表中的類型來(lái)作為信號(hào)和插槽函數(shù)的參數(shù)類型,那么Qt ActiveX框架不會(huì)轉(zhuǎn)換這些參數(shù)。
發(fā)布COM控件
為了讓Qt編寫的代碼可以作為一個(gè)COM組件被使用,必須提供獲取COM對(duì)象的方法,在Qt中使用QAxFactory來(lái)完成,最簡(jiǎn)單的方法是使用下面的宏:
QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}",
"{a8f21901-7ff7-4f6a-b939-789620c03d83}")
QAXCLASS(MyWidget)
QAXCLASS(MyWidget2)
QAXTYPE(MySubType)
QAXFACTORY_END()上面這段代碼把MyWidget和MyWidget2導(dǎo)出為可供外部調(diào)用的COM對(duì)象,并注冊(cè)MySubType類型可供MyWidget和MyWidget2中的屬性和參數(shù)使用。
進(jìn)程外COM控件的編寫
對(duì)于進(jìn)程外可執(zhí)行的COM組件,我們需要執(zhí)行一個(gè)main函數(shù)來(lái)實(shí)例化一個(gè)QApplication,和一般的Qt程序類似。
#include
#include
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!QAxFactory::isServer()) {
// create and show main window
}
return app.exec();
}
總結(jié)
以上是生活随笔為你收集整理的qt编写activex_Qt中使用ActiveX(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mysql my.ini utf8_修改
- 下一篇: cookie获取java_java中如何