劉 剛,徐麗群
(上海電力學院電力與自動化工程學院,上海 200090)
隨著計算機技術和軟件科學的不斷發展,新的應用系統越來越復雜,用戶對應用軟件也有了更高的要求,既要保證軟件在升級應用時不破壞其功能,又要保證升級后該軟件能與以前的版本相互兼容,傳統的面向對象的思想已經難以適應這種分布式軟件模型.因此,基于組件的程序設計方法越來越受到重視.組件化程序設計思想是將復雜的應用設計成小的、功能單一的組件模塊,不依賴特定的操作系統就可以擴展系統服務.一個新的軟件,就好像搭積木一樣,可以將不同的組件結合在一起組成一個新的應用軟件[1].
組件化程序設計方法在實施過程中必須遵守一定的標準,組建對象模型 (Component Object Module,COM)則為組件程序之間提供了規范.引入COM的組件化程序設計只要遵守COM標準,就可以選擇適合的編程語言進行編程,不用擔心因語言不同而出現調用和管理差錯,而且還可以通過增加新接口來提供新服務.利用COM提供的包容和聚合,可以很好地使組件得到擴展[2].
本文對計算機COM技術的基礎和原理進行了分析,深入研究COM標準的底層實現細節和數據通報機制,并通過在Visual C++中調用MATLAB繪制隨機曲線和在基于COM技術的力控組態軟件模擬了火電廠鍋爐汽包水位數據變化通知機制的應用,以深入了解COM技術.
COM為Windows提供了一個統一的、可擴充的、面向對象的通信機制.開發COM的目的是為了使軟件更易于定制、擴充更靈活.它可以將原來一個整體的應用程序拆分成幾個組件(components),從而實現像搭“積木”一樣,將組件“搭建”起來完成新的應用程序的開發.
COM對象,就是在程序與程序之間進行通信的雙方.對于客戶來說,COM對象本身是不可見的,只能通過接口來獲得對象服務.接口是對象或者組件的通信協議,實現對象或者組件的內部細節[1].
一個組件程序可以包含多個COM對象,一個COM對象也可實現多個接口.一個COM組件可以是一個動態連接庫(Dynamic Linking Library,DLL)文件,也可以是一個EXE文件.COM組件和COM對象與COM接口之間的關系如圖1所示.

圖1 COM組件和COM對象與COM接口之間的關系
對于客戶來說,對象本身是不可見的,而且客戶也無需知道對象內部的實現細節,客戶通過對象提供給外界的COM接口就可以獲得對象的服務,因此接口對于組件與組件之間的交互顯得十分重要.COM接口是一組邏輯上相關的函數集合,每個接口由一個128位的全球唯一標識符(Globally Unique Identifier,GUID)來標識.與接口類似,COM對象也有一個128位的全球唯一標識符,稱為類標識符(Class Identifer,CLSID)[2].所有的COM接口都必須從IUnknown繼承過來,IUnknown包含3個成員函數:QueryInterface(),AddRef(),Release(),其中 QueryInterface()為接口查詢函數.AddRef()和Release()實現的是一種名為引用計數的內存管理技術.通過它們,組件可以很好地進行自我刪除[3].
COM具備面向對象的繼承、封裝、多態等性能,并在此基礎上發展了一些其他特性.
(1)語言無關性 COM對象是建立在二進制可執行代碼級的基礎上,這一特性使得用不同編程語言開發的組件對象之間進行交互成為可能.
(2)進程透明性 COM所提供的服務組件對象在實現時有進程內對象和進程外對象兩種進程模型.但客戶程序在使用組件對象時可以無視這種區別的存在,只要遵照COM規范就可以了.
(3)可重用性 客戶程序可通過接口使用COM對象的服務,但并不知道其內部是如何實現的,因此這種重用是建立在行為方式而不是具體實現上,這是COM重用的關鍵所在.COM提供了包容(containment)和聚合(aggregation)兩種機制來實現對象的重用[4].
COM本身除了規范之外,也有實現部分,其中包括一些核心的系統級代碼,也正是這部分核心代碼,才使得對象和客戶之間可以通過接口在二進制代碼級進行交互.在編寫COM應用時,可以直接利用COM庫提供的API進行編程,而不是編寫大量基礎代碼,從而加快了開發速度.類廠是COM對象的生產基地,COM庫通過類廠創建COM對象,對應每一個COM類,有一個類廠專門用于該COM類的對象創建操作.
COM在功能實現之間提供了一種有效的數據交換機制,稱為統一數據傳輸機制(Uniform Data Transfer,UDT).它以數據對象作為數據實體,而數據對象則通過IDataObject接口暴露其內部信息.客戶程序只要通過某種傳輸協議就可以得到數據對象的IDataObject接口,以后客戶程序就可以直接訪問數據對象了.
COM提供了連接點機制以實現雙向通訊.在UDT中,客戶程序只需實現IAdviseSink接口,其中OnDataChange成員用于數據對象的通知過程.IDataObject::DAdvise函數建立了客戶程序接收器,在對象與數據對象之間通報連接,一旦連接建立起來,數據發生變化時,就會調用IAdviseSink::OnDataChange函數.
Matlab支持組件自動化(COM Automation),即一個COM協議,該協議允許一個程序或組件去控制另一個程序或組件.
通過分析如何在Visual C++中調用Matlab COM Builder生成的組件,用于實現在Matlab中繪制隨機曲線.在這里,Matlab生成的文件即為組件程序,而C++程序則為客戶程序.
圖2所示的myrandplottest對象有5個接口,其中的IUnknown接口是所有COM對象都支持的接口.接口Imyrandplottest則是自定義的接口,用于實現Matlab的圖形繪制功能.

圖2 myrandplottest對象
每個接口都有對應128位的IID加以標識,保證了全局唯一性,例如自定義的接口Imyrandplottest:

對象myrandplottest有自身128位的CLSID進行標識:


所創建的myrandplottest對象實現過程如下.
首先通過CoCreateInstance()函數來創建對象.其內部實際上也調用了CoGetClassObject函數,通過該函數,在注冊表中找到通過Matlab生成的的DLL文件,將它裝載在C++進程中,再調用DLL模塊的DLLGetClassObject引出函數創建類廠,并將類廠對象接口指針返回給CoGetClassObject函數,再把類廠對象接口指針返回給CoCreateInstance()函數,然后就可以調用類廠的對象創建函數,類廠就創建我們想要的COM對象,并將其返回給CoCreateInstance()函數,這樣就可以直接調用COM對象了.
CoCreateInstance()函數是將通過類廠創建對象的過程封裝起來了,所以只要指定所要創建的myrandplot對象類的CLSID和待輸出的Imyrandplottest接口指針及接口ID,就可以通過調用得到對象接口指針,而不需要與類廠打交道.這樣就省去了很多的編程煩惱[5].
在COM對象創建過程中,客戶程序、COM庫和進程內組件程序3者之間的順序關系見圖3.

圖3 客戶程序調用COM庫創建組件對象的順序
該工程的運行結果見圖4.
通過實例可以清楚地看到COM對象是如何創建和使用的,通過自定義的Imyrandplottest接口指針來獲得Matlab繪制曲線的功能.

圖4 運行結果
下面以一個電廠鍋爐汽包水位變化通知系統為例,分析C++是如何與力控組態軟件實現雙向通信的.首先通過力控組態軟件創建一個組態工程,繪制了鍋爐汽包的簡單循環系統,在數據庫中新建了一個注冊點temp,作為與C++中的數據通信點.DbCommOcxEf控件就是一個已經封裝好的COM組件,在運行程序之前,要保證DbCom.ocx已注冊成功,確保控件可以被實現,它就相當于一個.dll文件被裝入進程中.同樣創建的CDbCommOcxEf對象類也有其128位的CLSID進行標識.
CDbCommOcxEf對象類 :static CLSID const clsid={0x3310fa25,0xa027,0x47b3,{0x8c,0x49,0x10,0x91,0x07,0x73,0x17,0xe9}}
由于客戶程序只要知道數據對象的IDataObject接口指針就可以直接訪問數據對象了.有時候客戶程序希望能夠及時知道數據對象發生變化的信息,以便得到最新數據.比如鍋爐水位變化通知系統,客戶希望及時了解水位的變化情況,而不是一遍遍地去判斷水位是否發生變化.
通過聲明一個對象來獲取所要的注冊點的信息,把這些信息通過IDataObject接口指針存儲在通報連接接收器對象內部,實現數據連通[1].當注冊點信息發生變化時,消息映射機制找到對應的事件控制函數IAdviseSink::OnDataChange,通過它可以更新注冊點在C++工程中的信息.這樣就實現了數據對象的變化通知機制,該過程如圖5所示.C++工程的運行結果如圖6所示.

圖5 客戶程序與數據對象之間的通信過程

圖6 C++工程運行界面
通過在力控界面內將注冊點temp值改為1時,其運行界面如圖7所示.此時會發現C++界面上的數據已發生相應的變化.

圖7 力控運行界面
通過對兩個應用實例的分析,我們對COM底層操作的實現和數據變化通知機制有了深入的了解.此外,COM技術不限制語言,不要求操作系統,更可以跨進程相互調用.客戶程序可以很輕松地通過接口來調用對象提供的服務,而不必知道對象內部的實現細節,這就是COM技術給我們帶來的好處.在未來軟件發展過程中,COM技術將起到越來越大的作用.
[1]潘愛民.COM原理與應用[M].北京:清華大學出版社,2003:9-38.
[2]ROGERSON Dale.COM 技術內幕[M].楊秀章,譯.北京:清華大學出版社,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的數據訪問組件的實現[J].計算機應用與軟件,2005,22(9):17-20.
[5]曹曉陽,劉錦德.COM 及其應用向對象的組件集成技術[J].計算機應用1999,19(1):1-4.