Qt中使用ActiveX(3篇)


由於最近需要使用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文件:
[plain]  view plain  copy
 
  1. TEMPLATE = app  
  2. QT  += axserver  
  3.   
  4. RC_FILE  = qaxserver.rc  
當作為進程內dll的時候,需要這樣寫.pro文件
[plain]  view plain  copy
 
  1. TEMPLATE = lib  
  2. QT += axserver  
  3. CONFIG  += dll  
  4.   
  5. DEF_FILE = qaxserver.def  
  6. RC_FILE  = qaxserver.rc  
  7. ...  
但是我們不用去操心這些內容,因為一般我們是用向導來生成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變量即可:
[plain]  view plain  copy
 
  1. TEMPLATE = lib  
  2. VERSION = 2.5  
  3. ...  

  • COM進程外和進程內使用方式的比較
進程外的使用方式是將COM寫成一個exe可執行文件,當它運行的時候其他程序可以調用它提供的接口來開發,這樣做如果某個調用它的程序出了bug,那么只有該程序會崩潰,其他調用不收影響,提供了更好的隔離性。缺點是需要更長的啟動時間與跨進程通信的一些額外負擔。
進程內的使用方式就很簡單了,調用過程僅僅是通過類似虛函數這樣的調用,需要加載的時間短、效率比較高。

  • 開發Server的過程
為了使用Qt實現COM對象,我們必須使用一個QObject的子類,如果該子類是QWidget的子類,那么這個COM對象就是一個ActiveX控件。代碼如下:
[cpp]  view plain  copy
 
  1. #include <QWidget>  
  2.   
  3. class MyActiveX : public QWidget  
  4. {  
  5.     Q_OBJECT  
只里面Q_OBJECT宏不能少,它提供了關於類MyActiveX的一些元數據信息, 接下來繼續添加ActiveX的一些信息
[cpp]  view plain  copy
 
  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
[cpp]  view plain  copy
 
  1. Q_PROPERTY(int value READ value WRITE setValue)  
之后可以像寫Qt程序那樣來完成。
[cpp]  view plain  copy
 
  1. public:  
  2.     MyActiveX(QWidget *parent = 0)  
  3.     ...  
  4.   
  5.     int value() const;  
  6.   
  7. public slots:  
  8.     void setValue(int v);  
  9.     ...  
  10.   
  11. signals:  
  12.     void valueChange(int v);  
  13.     ...  
  14.   
  15. };  
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來完成,最簡單的方法是使用下面的宏:
[cpp]  view plain  copy
 
  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程序類似。
[cpp]  view plain  copy
 
  1. #include <QApplication>  
  2. #include <QAxFactory>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     QApplication app(argc, argv);  
  7.     if (!QAxFactory::isServer()) {  
  8.         // create and show main window  
  9.     }  
  10.     return app.exec();  
  11. }  

 

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM