Delphi编程技术简介
Delphi支持COM接口規(guī)范,Object Pascal語言增加了對象接口的方法。用Delphi創(chuàng)建的COM對象還可以工作在MTS(Microsoft Transaction Server)環(huán)境中。
軟件重用是業(yè)界追求的目標(biāo),人們一直希望能夠像搭積木一樣隨意“裝配”應(yīng)用程序,組件對象就充當(dāng)了積木的角色。所謂組件對象,實(shí)際上就是預(yù)定義好的、能完成一定功能的服務(wù)或接口。問題是,這些組件對象如何與應(yīng)用程序、如何與其他組件對象共存并相互通信和交互?這就需要制定?個規(guī)范,讓這些組件對象按統(tǒng)一的標(biāo)準(zhǔn)方式工作。
COM是個二進(jìn)制規(guī)范,它與源代碼無關(guān)。這樣,即使COM對象由不同的編程語言創(chuàng)建,運(yùn)行在不同的進(jìn)程空間和不同的操作系統(tǒng)平臺,這些對象也能相互通信。COM既是規(guī)范,也是實(shí)現(xiàn),它以COM庫(OLE32.dll和貼OLEAut32.dll)的形式提供了訪問COM對象核心功能的標(biāo)準(zhǔn)接口以及一組API函數(shù),這些API函數(shù)用于創(chuàng)建和管理COM對象。COM本質(zhì)上仍然是客戶服務(wù)器模式。客戶(通常是應(yīng)用程序)請求創(chuàng)建COM對象并通過COM對象的接口操縱COM對象。服務(wù)器根據(jù)客戶的請求創(chuàng)建并管理COM對象。客戶和服務(wù)器這兩種角色并不是絕對的。
組件對象與一般意義上的對象既相似也有區(qū)別。一般意義上的對象是一種把數(shù)據(jù)和操縱數(shù)據(jù)的方法封裝在一起的數(shù)據(jù)類型的實(shí)例,而組件對象則使用接口(Interface)而不是方法來描述自己并提供服務(wù)。所謂接口,其精確定義是“基于對象的一組語義上相關(guān)的功能”,實(shí)際上是一個純虛類,真正實(shí)現(xiàn)接口的是接口對象)(Interface Object)。一個COM對象可以只有一個接口,例如Wndows 95/98外殼擴(kuò)展;也可以有許多接口,例如Ac咖ex控件一般就有多個接口,客戶可以從很多方面來操縱ActiveX控件。接口是客戶與服務(wù)器通信的唯一途徑。如果一個組件對象有多個接口,則通過一個接口不能直接訪問其他接口。但是,COM允許客戶調(diào)用COM庫中的QueryInterface()去查詢組件對象所支持的其他接口。從這個意義上講,組件對象有點(diǎn)像接口對象的經(jīng)紀(jì)人。
在調(diào)用QueryInterface()后,如果組件對象正好支持要查詢的接口,則QueryInterface()將返回該接口的指針。如果組件對象不支持該接口,則QueryInterface()將返回一個出錯信息。
所以,QueryInterface()是很有用的,它可以動態(tài)了解組件對象所支持的接口。接口是團(tuán)向?qū)ο缶幊趟枷氲囊环N體現(xiàn),它隱藏了COM對象實(shí)現(xiàn)服務(wù)的細(xì)節(jié)。COM對象可以完全獨(dú)立于訪問它的客戶,只要接口本身保持不變即可。如果需要更新接口,則可以重新定義一個新的接口,對于使用老接口的客戶來說,代碼得到了最大程度的保護(hù)。
在一個復(fù)雜的系統(tǒng)中,可能充斥著大量的組件對象.每個組件對象可能又有大量的樓cJ為了保證這些接口彼此不會沖突,Microsoft規(guī)定用GUID來標(biāo)識組件對象和接口。
引用計數(shù)
引用計數(shù)是一種機(jī)制,使組件對象具有?定的“智能性”。它的工作原理是這樣的:當(dāng)接口對象第一次創(chuàng)建時,引用計數(shù)的初始值為1。當(dāng)有?-個客戶請求獲得接口對象的指針時,就調(diào)用AddRef()使該計數(shù)加1.當(dāng)一個客戶不再需要組件對象的服務(wù)時.它應(yīng)當(dāng)調(diào)用Release()。注意,Release()并不真正釋放接口對象,因為可能還有其他客戶正在使用接口;Release()只是使引用計數(shù)減1。只有當(dāng)引用汁數(shù)正好減為零時.接口對象才被刪除。下面舉例說明引用計數(shù)的作用。假設(shè)客戶A向服務(wù)器請求IMalloc接口,服務(wù)器收到請求后.首先看該接口對象是否存在。如果沒有.就創(chuàng)建?個接口對象,并凋用AddRef()使引用計數(shù)變?yōu)?,同時把該接口對象的指針傳遞給客戶A。假設(shè)這時候客戶B也加入進(jìn)來,并且也是請求IMalloc接口。由于此時IMalloc接口對象己存在,所以服務(wù)器只是簡單地返回一個指針,并且調(diào)用AddRef()使引用計數(shù)變?yōu)?,當(dāng)客戶A不再需要IMalloc接口時,它就調(diào)用Release()試圖釋放這個接口。顯然,這時候不能刪除Imalloc接口對象,因為客尸B還正用著呢。可見,引用計數(shù)這種機(jī)制使服務(wù)區(qū)知道如何管理自己的接口。
引用汁數(shù)這種機(jī)制也帶來?個問題,就是調(diào)用AddRef()和Release()不能出現(xiàn)混亂。一旦出現(xiàn)混亂,可能導(dǎo)致接口對象水遠(yuǎn)不被刪除或者過早地被刪除。
虛擬方法表
COM是個二進(jìn)制規(guī)范,任何開發(fā)環(huán)境只要遵守這個規(guī)范都可以生產(chǎn)出COM對象。COM采用一種稱為虛擬方法表的文法來解決方法調(diào)用。不過,COM接口與Objetc Pascal的類還是行-?些區(qū)別的:COM接口中凡是要表露給客戶的方法必須聲明為純虛的,客戶得到的只是指向虛擬方法表的指針,具體實(shí)現(xiàn)接口的是接口對象。
如果建立了同一個COM對象的多個實(shí)例,則虛擬方法表是共享的.但每個實(shí)例的數(shù)據(jù)是私有的。在DELPHI種,用abstract指示字來聲明純虛方法。例如:
TMyPureVirtualClass=class
public
procedure MyMethod;virtual;abstract;
…
end;
IUnknOwn接口
正如TObjetc是所有類的祖先一樣,IUnknown是所有接口的祖先。這樣,凡是取得了接口對象指針的客戶總是能訪問COM對象的核心服務(wù),諸如AddRef(),Release()和QueryInterface(),這三個核心服務(wù)管理著接口對象的生存期。AddRef()和Release()比較簡單.都沒有參數(shù)。而QueryInterface()則比較復(fù)雜,它有兩個參數(shù):一個是IID參數(shù),用于指定要查詢的接口;另一個是Obj參數(shù),用于返回找到的接口對象的指釘;如果COM對象不支持所查詢的接口,則Obj參數(shù)將返回nil。
AddRef()和Release()前均加了下劃線前綴,這是為了更加醒目。過去,COM對象必須自己維護(hù)引用計數(shù),也就是說,必須調(diào)用AddRef()和Release()來把引用計數(shù)加1或減1。COM的另一個核心服務(wù)QueryInterface()也是不可缺少的,客戶只有調(diào)用QueryInterface()才能申請到另一個接口指針。由于采用了ActiveX框架,所以引用計數(shù)是有TComObject對象自動維護(hù)的,應(yīng)用程序不再需要直接與IUnknown接口打交道。
這里順便介紹一下COM模型中稱為Interface Aggregation的概念。面向?qū)ο蟮木幊趟枷朐试S通過繼承(Inheritance)來實(shí)現(xiàn)軟件重用。在COM模型中沒有繼承的概念,而是通過Interface Aggregation技術(shù)把多個接口聚合起來,共同完成某一復(fù)雜的功能。
In-Process COM服務(wù)器的形式是DLL,它可以輸出COM對象,并映射到客戶的進(jìn)程地址空間中運(yùn)行。In-Process服務(wù)器的優(yōu)勢在于,客戶可以直接調(diào)用COM對象的接口。
要創(chuàng)建一個In-Process COM服務(wù)器,先要建立一個ActiveX庫作為COM對象的容器。
為此,可以使用“File”菜單上的“New”命令,翻到“ActiveX”頁。雙擊“Activex NbrW”圖標(biāo),就會自動創(chuàng)建一個Ac6vex庫。
一個In-Precess類型的COM服務(wù)器必須引出下面4個例程:
function DllRegisterServer:HResult;stdcall;
function DllUnRegisterServer:HResult;stdcall;
function DllGetClaasObject(const CLSID,II:TGUID;var obj):HResult;stdcall
function DllCanUnloadNow:HResult;stdcall;
ComServ單元已經(jīng)實(shí)現(xiàn)了這幾個例程。因此,只要在項目文件中引出它們即可。DllRegisterServer()用于注冊COM服務(wù)器以及服務(wù)器中的所有COM對象。每個COM對象在注冊表的HKEY_CLASSES_ROOT\CLSID\{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx}下都有各自的鍵。其中,(x……)代表COM對象的CLSID。對于In-Process類型的COM服務(wù)器來說,還有一個鍵叫InProcServer32,這個鍵的默認(rèn)值是服務(wù)器文件在磁盤上的路徑。 DllUnregisterServer()用于撤消DllRegisterServer()所做的工作,即從注冊表中取消COM服務(wù)器以及COM對象的注冊。
DllGetClassObjetc()用于獲取一個COM對象的類工廠。CLSID參數(shù)用于指定COM對象的CLSID,HD參數(shù)用丁指定要獲取的類工廠的接口IID(通常設(shè)為IClassFactory的IID)。如果這個函數(shù)調(diào)用成功,obj參數(shù)將返回一個指向類工廠的指針。
DllCanUnloadNow()用于判斷COM服務(wù)器是否應(yīng)當(dāng)從內(nèi)存中卸載。只要服務(wù)器中有一個COM對象被引用,這個函數(shù)就應(yīng)當(dāng)返回S_PALSE,表明DLL不應(yīng)當(dāng)卸載。如果服務(wù)器中沒有一個COM對象被引用,這個函數(shù)應(yīng)當(dāng)返回S_TRUE。 要在服務(wù)器中加入COM對象,可以使用“File”菜單上的“New”命令,翻到“ActiveX"頁,然后雙擊“COM Object”圖標(biāo),Delphi 5將啟動COM對象向?qū)?#xff0e;這里說的COM對象是非常簡單的。
1、在“Class Name”框內(nèi)輸入C0M對象的類名,不必以T打頭。
2、在“Instancing”框內(nèi)指定COM對象的實(shí)例模式。對于In?Process類型的服務(wù)器來說不必指定實(shí)例模式。
3、在“Threading Model",柜內(nèi)選擇一種線程模式,可以設(shè)為以下值:
Single:整個COM服務(wù)器都是單線程的
Apartment:每個COM對象的實(shí)例有單獨(dú)的線程。這樣,凡是需要共享的數(shù)據(jù)(諸如全局變量)必須用線程同步對象保護(hù);
Free:一個COM對象的多個實(shí)例可以同時運(yùn)行,這意味著COM對象必須保護(hù)自己的實(shí)例數(shù)據(jù),以避免多個實(shí)例相沖突:
Both:同時支持Aartment和Free兩種線程模式。
在“Implementd Interfaces”框內(nèi)輸入讓COM對象實(shí)現(xiàn)的接口名稱(可選)。默認(rèn)情況下向?qū)鶆?chuàng)建的C0M對象只實(shí)現(xiàn)IUnknown接口。如果選中“Include Type Library”復(fù)選柜,向?qū)⑸梢粋€類型庫。
如果選中“Mark interface OleAutormation”復(fù)選框,將使接口支持Ole Autormation。不過,類型庫中的數(shù)據(jù)類型必須是與Ole Autormation兼容的類型。單擊擊“OK”按鈕,向?qū)?chuàng)建一個COM對象。如果選中丁“Include Type Library”復(fù)選柜,向?qū)?chuàng)建?個類型庫。同時,向?qū)⑸蒀OM對象的單元文件。
一個COM對象的單元:
| Unit Unit2; Interface uses windows,ActiveX,Classes,Comobj,Project2_TLB,StdVcl; type TXXH=class(TTypedComObjetc,IXXH) Protectd {Declare IXXH methods here) end; implementation uses ComServ; |
initialization
TTypedComObjetcFactory.Create(ComServer,TXXH,Class_XXH,ciMultiInstance,tmApartment);可以看出,用Delphi 5創(chuàng)建的COM對象,代碼非常簡潔,這主要是因為Object Pascal語言引入了對象接口的語法以及采用了ActiveX框架。接口對象是一個類,但保留字class后列山了兩個祖先:第一個祖先必須是TObject的派生類,這里是TTypedComObjetc;第二個祖先是要實(shí)現(xiàn)的接口,這里是IXXH。第一個祖先可以是其他已聲明過的接口對象,表示正在聲明的接口對象同時支持多個接口。接口的第一個成貝必須是CLSID。在某些需要傳遞CLSID常量的場合.可以直接用接口名稱來代替CLSID常量。當(dāng)然,目前IXXH接口中還沒有其他成員。
COM對象的實(shí)例是通過類工廠來建立的。每個COM對象都有一個類工廠。類工廠本身的實(shí)例是在單元的initialization部分建立的。這樣,一旦COM服務(wù)器調(diào)入內(nèi)存運(yùn)行,就會創(chuàng)建類工廠的實(shí)例,也就隨時可以府客戶的請求創(chuàng)建COM對象的實(shí)例。
要讓W(xué)indows能找到COM服務(wù)器,COM服務(wù)器必須在Windows的注冊表中登記注冊。這需要借助于一個叫服REGSVR32.EXE的命令行程序。
如果沒有REGSVR32.EX,則可以用一個文本編輯器建立一個“注冊表項目”文件,其擴(kuò)展名是.REG。“注冊表項目”文件應(yīng)當(dāng)遵循一定的格式。請參考下面的例子:
| REGEDIT4 [HKEY_CLASSES_ROOT\CLSID\{0AA1740-310E-11D0-A45E-444553540000}] @="MyCOMServer" [HKEY_CLASSES_ROOT\CLSID\{0AA1740-310E-11D0-A45E-444553540000}\InProcServer32] @="C:\\DELPHI\\COMServer\\MyComServer.DLL" |
建立了注冊表項目文件后,只要在資源管理器中雙擊這個文件,Windows就會把“注冊表項目”文件中的信息加到注冊表中。注冊了COM服務(wù)器后,就可以打開Windows的注冊表,查看COM服務(wù)器的注冊情況。
?
總結(jié)
以上是生活随笔為你收集整理的Delphi编程技术简介的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大家整齐的qipaifans朗读声忽然乱
- 下一篇: Com实现过程