劉 剛,徐麗群
(上海電力學(xué)院電力與自動化工程學(xué)院,上海 200090)
隨著計(jì)算機(jī)技術(shù)和軟件科學(xué)的不斷發(fā)展,新的應(yīng)用系統(tǒng)越來越復(fù)雜,用戶對應(yīng)用軟件也有了更高的要求,既要保證軟件在升級應(yīng)用時不破壞其功能,又要保證升級后該軟件能與以前的版本相互兼容,傳統(tǒng)的面向?qū)ο蟮乃枷胍呀?jīng)難以適應(yīng)這種分布式軟件模型.因此,基于組件的程序設(shè)計(jì)方法越來越受到重視.組件化程序設(shè)計(jì)思想是將復(fù)雜的應(yīng)用設(shè)計(jì)成小的、功能單一的組件模塊,不依賴特定的操作系統(tǒng)就可以擴(kuò)展系統(tǒng)服務(wù).一個新的軟件,就好像搭積木一樣,可以將不同的組件結(jié)合在一起組成一個新的應(yīng)用軟件[1].
組件化程序設(shè)計(jì)方法在實(shí)施過程中必須遵守一定的標(biāo)準(zhǔn),組建對象模型 (Component Object Module,COM)則為組件程序之間提供了規(guī)范.引入COM的組件化程序設(shè)計(jì)只要遵守COM標(biāo)準(zhǔn),就可以選擇適合的編程語言進(jìn)行編程,不用擔(dān)心因語言不同而出現(xiàn)調(diào)用和管理差錯,而且還可以通過增加新接口來提供新服務(wù).利用COM提供的包容和聚合,可以很好地使組件得到擴(kuò)展[2].
本文對計(jì)算機(jī)COM技術(shù)的基礎(chǔ)和原理進(jìn)行了分析,深入研究COM標(biāo)準(zhǔn)的底層實(shí)現(xiàn)細(xì)節(jié)和數(shù)據(jù)通報(bào)機(jī)制,并通過在Visual C++中調(diào)用MATLAB繪制隨機(jī)曲線和在基于COM技術(shù)的力控組態(tài)軟件模擬了火電廠鍋爐汽包水位數(shù)據(jù)變化通知機(jī)制的應(yīng)用,以深入了解COM技術(shù).
COM為Windows提供了一個統(tǒng)一的、可擴(kuò)充的、面向?qū)ο蟮耐ㄐ艡C(jī)制.開發(fā)COM的目的是為了使軟件更易于定制、擴(kuò)充更靈活.它可以將原來一個整體的應(yīng)用程序拆分成幾個組件(components),從而實(shí)現(xiàn)像搭“積木”一樣,將組件“搭建”起來完成新的應(yīng)用程序的開發(fā).
COM對象,就是在程序與程序之間進(jìn)行通信的雙方.對于客戶來說,COM對象本身是不可見的,只能通過接口來獲得對象服務(wù).接口是對象或者組件的通信協(xié)議,實(shí)現(xiàn)對象或者組件的內(nèi)部細(xì)節(jié)[1].
一個組件程序可以包含多個COM對象,一個COM對象也可實(shí)現(xiàn)多個接口.一個COM組件可以是一個動態(tài)連接庫(Dynamic Linking Library,DLL)文件,也可以是一個EXE文件.COM組件和COM對象與COM接口之間的關(guān)系如圖1所示.
圖1 COM組件和COM對象與COM接口之間的關(guān)系
對于客戶來說,對象本身是不可見的,而且客戶也無需知道對象內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),客戶通過對象提供給外界的COM接口就可以獲得對象的服務(wù),因此接口對于組件與組件之間的交互顯得十分重要.COM接口是一組邏輯上相關(guān)的函數(shù)集合,每個接口由一個128位的全球唯一標(biāo)識符(Globally Unique Identifier,GUID)來標(biāo)識.與接口類似,COM對象也有一個128位的全球唯一標(biāo)識符,稱為類標(biāo)識符(Class Identifer,CLSID)[2].所有的COM接口都必須從IUnknown繼承過來,IUnknown包含3個成員函數(shù):QueryInterface(),AddRef(),Release(),其中 QueryInterface()為接口查詢函數(shù).AddRef()和Release()實(shí)現(xiàn)的是一種名為引用計(jì)數(shù)的內(nèi)存管理技術(shù).通過它們,組件可以很好地進(jìn)行自我刪除[3].
COM具備面向?qū)ο蟮睦^承、封裝、多態(tài)等性能,并在此基礎(chǔ)上發(fā)展了一些其他特性.
(1)語言無關(guān)性 COM對象是建立在二進(jìn)制可執(zhí)行代碼級的基礎(chǔ)上,這一特性使得用不同編程語言開發(fā)的組件對象之間進(jìn)行交互成為可能.
(2)進(jìn)程透明性 COM所提供的服務(wù)組件對象在實(shí)現(xiàn)時有進(jìn)程內(nèi)對象和進(jìn)程外對象兩種進(jìn)程模型.但客戶程序在使用組件對象時可以無視這種區(qū)別的存在,只要遵照COM規(guī)范就可以了.
(3)可重用性 客戶程序可通過接口使用COM對象的服務(wù),但并不知道其內(nèi)部是如何實(shí)現(xiàn)的,因此這種重用是建立在行為方式而不是具體實(shí)現(xiàn)上,這是COM重用的關(guān)鍵所在.COM提供了包容(containment)和聚合(aggregation)兩種機(jī)制來實(shí)現(xiàn)對象的重用[4].
COM本身除了規(guī)范之外,也有實(shí)現(xiàn)部分,其中包括一些核心的系統(tǒng)級代碼,也正是這部分核心代碼,才使得對象和客戶之間可以通過接口在二進(jìn)制代碼級進(jìn)行交互.在編寫COM應(yīng)用時,可以直接利用COM庫提供的API進(jìn)行編程,而不是編寫大量基礎(chǔ)代碼,從而加快了開發(fā)速度.類廠是COM對象的生產(chǎn)基地,COM庫通過類廠創(chuàng)建COM對象,對應(yīng)每一個COM類,有一個類廠專門用于該COM類的對象創(chuàng)建操作.
COM在功能實(shí)現(xiàn)之間提供了一種有效的數(shù)據(jù)交換機(jī)制,稱為統(tǒng)一數(shù)據(jù)傳輸機(jī)制(Uniform Data Transfer,UDT).它以數(shù)據(jù)對象作為數(shù)據(jù)實(shí)體,而數(shù)據(jù)對象則通過IDataObject接口暴露其內(nèi)部信息.客戶程序只要通過某種傳輸協(xié)議就可以得到數(shù)據(jù)對象的IDataObject接口,以后客戶程序就可以直接訪問數(shù)據(jù)對象了.
COM提供了連接點(diǎn)機(jī)制以實(shí)現(xiàn)雙向通訊.在UDT中,客戶程序只需實(shí)現(xiàn)IAdviseSink接口,其中OnDataChange成員用于數(shù)據(jù)對象的通知過程.IDataObject::DAdvise函數(shù)建立了客戶程序接收器,在對象與數(shù)據(jù)對象之間通報(bào)連接,一旦連接建立起來,數(shù)據(jù)發(fā)生變化時,就會調(diào)用IAdviseSink::OnDataChange函數(shù).
Matlab支持組件自動化(COM Automation),即一個COM協(xié)議,該協(xié)議允許一個程序或組件去控制另一個程序或組件.
通過分析如何在Visual C++中調(diào)用Matlab COM Builder生成的組件,用于實(shí)現(xiàn)在Matlab中繪制隨機(jī)曲線.在這里,Matlab生成的文件即為組件程序,而C++程序則為客戶程序.
圖2所示的myrandplottest對象有5個接口,其中的IUnknown接口是所有COM對象都支持的接口.接口Imyrandplottest則是自定義的接口,用于實(shí)現(xiàn)Matlab的圖形繪制功能.
圖2 myrandplottest對象
每個接口都有對應(yīng)128位的IID加以標(biāo)識,保證了全局唯一性,例如自定義的接口Imyrandplottest:
對象myrandplottest有自身128位的CLSID進(jìn)行標(biāo)識:
所創(chuàng)建的myrandplottest對象實(shí)現(xiàn)過程如下.
首先通過CoCreateInstance()函數(shù)來創(chuàng)建對象.其內(nèi)部實(shí)際上也調(diào)用了CoGetClassObject函數(shù),通過該函數(shù),在注冊表中找到通過Matlab生成的的DLL文件,將它裝載在C++進(jìn)程中,再調(diào)用DLL模塊的DLLGetClassObject引出函數(shù)創(chuàng)建類廠,并將類廠對象接口指針返回給CoGetClassObject函數(shù),再把類廠對象接口指針返回給CoCreateInstance()函數(shù),然后就可以調(diào)用類廠的對象創(chuàng)建函數(shù),類廠就創(chuàng)建我們想要的COM對象,并將其返回給CoCreateInstance()函數(shù),這樣就可以直接調(diào)用COM對象了.
CoCreateInstance()函數(shù)是將通過類廠創(chuàng)建對象的過程封裝起來了,所以只要指定所要創(chuàng)建的myrandplot對象類的CLSID和待輸出的Imyrandplottest接口指針及接口ID,就可以通過調(diào)用得到對象接口指針,而不需要與類廠打交道.這樣就省去了很多的編程煩惱[5].
在COM對象創(chuàng)建過程中,客戶程序、COM庫和進(jìn)程內(nèi)組件程序3者之間的順序關(guān)系見圖3.
圖3 客戶程序調(diào)用COM庫創(chuàng)建組件對象的順序
該工程的運(yùn)行結(jié)果見圖4.
通過實(shí)例可以清楚地看到COM對象是如何創(chuàng)建和使用的,通過自定義的Imyrandplottest接口指針來獲得Matlab繪制曲線的功能.
圖4 運(yùn)行結(jié)果
下面以一個電廠鍋爐汽包水位變化通知系統(tǒng)為例,分析C++是如何與力控組態(tài)軟件實(shí)現(xiàn)雙向通信的.首先通過力控組態(tài)軟件創(chuàng)建一個組態(tài)工程,繪制了鍋爐汽包的簡單循環(huán)系統(tǒng),在數(shù)據(jù)庫中新建了一個注冊點(diǎn)temp,作為與C++中的數(shù)據(jù)通信點(diǎn).DbCommOcxEf控件就是一個已經(jīng)封裝好的COM組件,在運(yùn)行程序之前,要保證DbCom.ocx已注冊成功,確保控件可以被實(shí)現(xiàn),它就相當(dāng)于一個.dll文件被裝入進(jìn)程中.同樣創(chuàng)建的CDbCommOcxEf對象類也有其128位的CLSID進(jìn)行標(biāo)識.
CDbCommOcxEf對象類 :static CLSID const clsid={0x3310fa25,0xa027,0x47b3,{0x8c,0x49,0x10,0x91,0x07,0x73,0x17,0xe9}}
由于客戶程序只要知道數(shù)據(jù)對象的IDataObject接口指針就可以直接訪問數(shù)據(jù)對象了.有時候客戶程序希望能夠及時知道數(shù)據(jù)對象發(fā)生變化的信息,以便得到最新數(shù)據(jù).比如鍋爐水位變化通知系統(tǒng),客戶希望及時了解水位的變化情況,而不是一遍遍地去判斷水位是否發(fā)生變化.
通過聲明一個對象來獲取所要的注冊點(diǎn)的信息,把這些信息通過IDataObject接口指針存儲在通報(bào)連接接收器對象內(nèi)部,實(shí)現(xiàn)數(shù)據(jù)連通[1].當(dāng)注冊點(diǎn)信息發(fā)生變化時,消息映射機(jī)制找到對應(yīng)的事件控制函數(shù)IAdviseSink::OnDataChange,通過它可以更新注冊點(diǎn)在C++工程中的信息.這樣就實(shí)現(xiàn)了數(shù)據(jù)對象的變化通知機(jī)制,該過程如圖5所示.C++工程的運(yùn)行結(jié)果如圖6所示.
圖5 客戶程序與數(shù)據(jù)對象之間的通信過程
圖6 C++工程運(yùn)行界面
通過在力控界面內(nèi)將注冊點(diǎn)temp值改為1時,其運(yùn)行界面如圖7所示.此時會發(fā)現(xiàn)C++界面上的數(shù)據(jù)已發(fā)生相應(yīng)的變化.
圖7 力控運(yùn)行界面
通過對兩個應(yīng)用實(shí)例的分析,我們對COM底層操作的實(shí)現(xiàn)和數(shù)據(jù)變化通知機(jī)制有了深入的了解.此外,COM技術(shù)不限制語言,不要求操作系統(tǒng),更可以跨進(jìn)程相互調(diào)用.客戶程序可以很輕松地通過接口來調(diào)用對象提供的服務(wù),而不必知道對象內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),這就是COM技術(shù)給我們帶來的好處.在未來軟件發(fā)展過程中,COM技術(shù)將起到越來越大的作用.
[1]潘愛民.COM原理與應(yīng)用[M].北京:清華大學(xué)出版社,2003:9-38.
[2]ROGERSON Dale.COM 技術(shù)內(nèi)幕[M].楊秀章,譯.北京:清華大學(xué)出版社,1999:12-37.
[3]SULLIVAN Kevin J,MARCHUKOV Mark ,SOCHA John.Analysis ofa conflictbetween aggregation and interface negotination in microsoft’s component object model[J].IEEE Transactions on Software Engineering,1999,25(4):584-599.
[4]張先剛,余金山.基于COM的數(shù)據(jù)訪問組件的實(shí)現(xiàn)[J].計(jì)算機(jī)應(yīng)用與軟件,2005,22(9):17-20.
[5]曹曉陽,劉錦德.COM 及其應(yīng)用向?qū)ο蟮慕M件集成技術(shù)[J].計(jì)算機(jī)應(yīng)用1999,19(1):1-4.