Developing COM Components using VC-ATL(2-2) (轉)

amyz發表於2007-08-17
Developing COM Components using VC-ATL(2-2) (轉)[@more@]

:namespace prefix = o ns = "urn:schemas--com::office" />

COM的真相

COM是一種體系結構,這種體系結構允許用不同的軟體商提供的來構造應用和。它是一套二進位制的和標準,允許任何軟體相互通訊而不管、OS和用於開發的語言。COM不是一種程式語言而是一套規範,它定義了元件怎麼樣可以相互通訊。

  每個COM元件被編寫成滿足由COM規定的二進位制標準。這一些標準是:

n  元件要求注意它們自己(即元件[3])的建立和銷燬

n  元件要求以標準方式提供它們的功能(即透過介面[4])

n  元件的位置要求對透明

  使用元件的優點在於具有將它們動態裝載或卸出應用程式系統的能力。為了達到這一目的,元件必須滿足兩個需求:

n  元件必須動態連結[5]。這允許它們在執行時改變。如果應用程式是由每次元件改變時靜態連結的元件組成,那麼應用程式就等同於一個單一的應用程式。

n  在實現時元件必須隱藏。每個元件具有唯一識別自己的ID作為標識。這些ID出現在系統登錄檔HKEY_CLASSES_目錄裡。

動態連結對於元件而言是一個至關重要的要求,而實現細節隱藏則是動態連結的一個必要條件。

  COM的工作方式

  到目前為止,顯然COM並不僅僅是書面形式的規範。它也包含系統級的程式碼,即它自身的實現。COM規則出現在COM執行庫裡。

  元件物件庫,即COM執行時間庫是一個系統元件,這個系統元件提供能夠在程式內、程式外或透過網路進行的COM。

  COM核心,簡單地講,是元件物件和客戶端使用二進位制標準如何互動的規範。COM在作業系統內的實現為COM執行時間庫。COM執行時間庫包括:

n  服務

n  允許應用程式在程式內控制分配的標準機制

n  …

  元件物件庫是透過3.1中的COMPOBJ.DLL以及和Windows 9X中的OLE32.DLL實現的。

  介面

n  概述

n  元件物件高度地封裝。元件物件的內部實現對使用者完全隱藏,根本無法知道元件物件在使用何種資料結構和這些資料是如何被函式處理的。每個元件有一個介面,這個介面是一種且唯一的訪問元件物件功能的方式。介面是由一組虛擬函式的宣告組成。它使得預期的行為和響應清淅化。接中並不具有這些函式的實現。函式是透過元件類(CoClass)實現的。元件類例項化時生成元件物件。

n  介面是用稱為虛擬函式表(VTable)的記憶體結構實現的。無論何時建立元件物件,元件物件也在記憶體中建立一張對應的虛擬函式表。虛擬函式表是由一系列指標組成,這些指標了由元件物件實現的成員函式的地址。

n  客戶端建立一個介面指標,介面指標指向一個虛指標,虛指標指向虛擬函式表。使用介面指標和虛指標,客戶端就可以訪問元件物件實現的成員函式。

VTable是一個函式指標陣列的記憶體結構。每一個陣列元素包含的是一個由元件所實現的函式地址。對於COM而言,介面也就是此記憶體結構,其他東西,均是COM不關心的實現細節。

n  特徵

n  介面是集合在同一個名稱(是一個系統唯一的ID值,稱IID)下的相關函式(/方法)的集合。這說明介面必須是全球唯一的。

n  如前所述,當元件類例項化時產生元件物件。介面是由元件物件實現的函式的集合。介面不可以被例項化,因為它沒有實現。

n  元件之間的通訊是基於介面的。介面是元件和其客戶之間嚴格型別化的契約。實現相同介面的兩個物件就被認為是多型的,這裡的多型不包含諸如基類指標指向派生類物件的意義,這裡是指同一個介面可以由多個元件物件以不同方法實現。

n  元件物件可以實現多個介面。例如,銀行交易元件(BankTransaction)支援兩個介面,bit和ICredit,即借和貸;又例如,Microsoft SERVER元件(SqlImplementation)支援兩個介面,一個是維護資料處理,另一個是維護資料定義。資料處理介面提供增加、和刪除資料方法,然而,資料定義介面提供建立資料庫、表和檢視的方法。

n  介面沒有版本化並且是不變的,如果功能必須改變以適應一個介面,則將建立一個有唯一標識的完全新介面。元件物件實現這兩個介面,因而解決了版本問題。為較早版本的介面製作的客戶端仍舊可以訪問原來的介面。

n  注意

n  原來的舊介面是可以改變的,但是介面中的函式說明不能改變,即介面中的函式的函式名、引數形式不能改變,因為這樣改變就需要使得客戶端呼叫服務的程式碼進行修改和進行重新編繹。但是介面中的函式的函式體是可以修改的。這就是介面不變性的本質,也是COM規範。

  COM識別符號

  因為全球都在開發和使用元件,因此需要標識唯一地標識每個元件。

  開發軟體基金會(O)研究出一種能產生唯一識別符號的演算法,稱之為全球唯一識別符號(UUID)。在COM中,UUID被稱之為全域性唯一識別符號(GUID)。GUID是能分配給介面、元件類和型別庫的128位或16位元組數。GUID唯一地標識元件。

  生成GUID的演算法根據以下幾個方面:

n  當前日期和時間。

n  網路介面卡卡地址。

n  時針序。

n  自動遞增計數器。

  COM使用的128位的介面識別符號使得我們可能建立大約340282366920900000000000000000000000000個獨立的介面,足夠為將來10782897524560000000年每秒建立一萬億個介面。

  地址是相互不同的,對沒有網路卡的機器,地址對使用中的機器保持唯一性。

  GUID可能透過UUIDGEN.EXE或GUIDGEN.EXE產生。GUIDGEN是裝載Microsoft 中的一個工具,它通常在C:Program FilesMicrosoft Visual StudioCommonToolsGUIDGEN.EXE。

  GUID分為三類,具體見下:

n  CLSID 是唯一地標識類或元件的GUID,傳統地,CLSID的一般形式為CLSID_,在本例中:

在MyProj_i.c中表示為

const CLSID CLSID_My= {0xFEB7BDEF,0xFB6F,0x446B,{0xBE,0x31,0xDF,0x0A,0x3A,0xD3,0x91,0xBA}};

在MyProj.idl中表示為

  [

  uuid(FEB7BDEF-FB6F-446B-BE31-DF0A3AD391BA),

  helpstring("MyCom Class")

  ]

  coclass MyCom

  {

  [default] interface IMyCom;

  };

在MyCom.rgs中表示為

HKCR

{

  MyProj.MyCom.1 = s 'MyCom Class'

  {

  CLSID = s '{FEB7BDEF-FB6F-446B-BE31-DF0A3AD391BA}'

  }

  MyProj.MyCom = s 'MyCom Class'

  {

  CLSID = s '{FEB7BDEF-FB6F-446B-BE31-DF0A3AD391BA}'

  CurVer = s 'MyProj.MyCom.1'

  }

  NoRemove CLSID

  {

  ForceRemove {FEB7BDEF-FB6F-446B-BE31-DF0A3AD391BA} = s 'MyCom Class'

  {

  ProgID = s 'MyProj.MyCom.1'

  VersionIndependentProgID = s 'MyProj.MyCom'

  ForceRemove 'Programmable'

  InprocServer32 = s '%MODULE%'

  {

  val ThreadingModel = s 'Apartment'

  }

  'TypeLib' = s '{FE651184-11DE-4D01-BD69-B07DDFA12D0C}'

  }

  }

}

n  ProgID 顯然,上面的CLSID難以記憶且難以使用。ProgID是分配給物件的使用者友好名。ProgID不可能是單一的。每個ProgID對映到CLSID。命名習慣可以是..。在本例中:

在MyCom.rgs中可以看到,有這麼兩句

  ProgID = s 'MyProj.MyCom.1'

  VersionIndependentProgID = s 'MyProj.MyCom'

  透過分別地呼叫函式ProgIDFromCLSID和CLSIDFromProgID可以將ProgID轉換為CLSID和將CLSID轉換為ProgID。

  如在本示例中的客戶端程式中

HRESULT hr=CoInitialize(NULL);

  CLSID  clsid;

  hr=CLSIDFromProgID(OLESTR("MyProj.MyCom"),&clsid);

  if(FAILED(hr))

  {

  AfxMessageBox("COM Failed");

  return;

  }

 呼叫函式CLSIDFromProgID把作為第一個引數的ProgID的CLSID存放到第二個引數中。

n  IID 是唯一標識介面的GUID。按照慣例,IID的一般形式為IID_,在本例中:

在MyProj_i.c中表示為

const IID IID_IMyCom = {0x65460F9C,0x3BAB,0x4055,{0x88,0x5A,0x8E,0xD5,0x9F,0x5F,0xA9,0xB0}};

在MyProj.idl中表示為

  [

  ,

  uuid(65460F9C-3BAB-4055-885A-8ED59F5FA9B0),

  dual,

  helpstring("IMyCom Interface"),

  pointer_default(unique)

  ]

  interface IMyCom : IDispatch

  {

  [id(1), helpstring("method MyF1")] HRESULT MyF1();

  [id(2), helpstring("method MyF2")] HRESULT MyF2([in] BSTR str,[out, retval] int* val);

  [id(3), helpstring("method MyF3")] HRESULT MyF3([in] BSTR str,[out, retval] BSTR* retstr);

  [id(4), helpstring("method MyF4")] HRESULT MyF4([in] int x,[out, retval] int* val);

  };

n  TypeLibID 是標識系統上的型別庫。按照慣例,TypeLibID的一般形式為LIBID_Lib,在本例中:

在MyProj_i.c中表示為

const IID LIBID_MYPROJLib = {0xFE651184,0x11DE,0x4D01,{0xBD,0x69,0xB0,0x7D,0xDF,0xA1,0x2D,0x0C}};

在MyProj.idl中表示為

[

  uuid(FE651184-11DE-4D01-BD69-B07DDFA12D0C),

  version(1.0),

  helpstring("MyProj 1.0 Type Library")

]

library MYPROJLib

{

  importlib("stdole32.tlb");

  importlib("stdole2.tlb");

};

在MyCom.rgs中也可以看到這麼一句

'TypeLib' = s '{FE651184-11DE-4D01-BD69-B07DDFA12D0C}'


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-963226/,如需轉載,請註明出處,否則將追究法律責任。

相關文章