COM 技術概觀

本主題提供Microsoft元件物件模型的概觀(COM):

COM 簡介

Microsoft元件物件模型 (COM) 會定義二進位互作性標準,以建立可在運行時間互動的可重複使用軟體連結庫。 您可以使用 COM 連結庫,而不需要將它們編譯至您的應用程式。 COM 是許多Microsoft產品和技術的基礎,例如 Windows Media Player 和 Windows Server。

COM 定義適用於許多作系統和硬體平臺的二進位標準。 針對網路運算,COM 會定義標準有線格式和通訊協定,以便在不同硬體平臺上執行的對象之間進行互動。 COM 與實作語言無關,這表示您可以使用不同的程式設計語言來建立 COM 連結庫,例如C++和 .NET Framework 中的程式設計語言。

COM 規格提供啟用跨平臺軟體重複使用的所有基本概念:

  • 元件之間函數調用的二進位標準。
  • 提供一種將函式依據強型別分組至介面的方式。
  • 基底介面,提供多型、特徵探索和物件存留期追蹤。
  • 可唯一識別元件及其介面的機制。
  • 從部署建立元件實例的元件載入器。

COM 有許多元件可以一起運作,以建立從可重複使用元件建置的應用程式:

  • 主機系統,提供符合 COM 規格的運行時間環境。
  • 定義功能合約的介面,以及實作這些介面的 元件
  • 提供元件的伺服器,以及使用元件所提供的功能 用戶端。
  • 註冊表,用於追蹤元件在本機和遠端主機上的部署位置。
  • Service Control Manager,可找出本機和遠端主機上的元件,並將伺服器連線至用戶端。
  • 結構化記憶體 通訊協定,定義如何在主機文件系統上巡覽檔案的內容。

跨主機和平臺啟用程式代碼重複使用,是 COM 的核心。 可重複使用的介面實作會命名為 元件元件物件,或 COM 物件。 元件會實作一或多個 COM 介面。

您可以藉由設計程式庫所實作的介面來定義自訂的 COM 程式庫。 您連結庫的取用者可以在不了解連結庫的部署和實作詳細數據的情況下探索及使用其功能。

對象和介面

COM 物件會透過 介面公開其功能,這是成員函式的集合。 COM 介面定義了元件的預期行為和責任,並指定了一個嚴格類型的契約,以提供一組小型且相關的操作。 COM 元件之間的所有通訊都是透過介面進行,而元件所提供的所有服務都會透過其介面公開。 呼叫端只能存取介面成員函式。 除非在介面中公開內部狀態,否則呼叫端無法使用內部狀態。

介面是強烈類型的。 每個介面都有自己的唯一介面標識符,名為 IID,可消除與人類可讀取名稱可能發生的衝突。 IID 是全域唯一標識符 (GUID),與 Open Software Foundation (OSF) 分散式運算環境 (DCE) 定義的通用唯一標識符 (UUID) 相同。 當您建立新的介面時,您必須為該介面建立新的標識碼。 當呼叫端使用 介面時,它必須使用唯一標識符。 此明確識別可藉由消除會導致運行時間失敗的命名衝突來改善健全性。

當您定義新的介面時,您可以使用介面定義語言 (IDL) 來建立介面定義。 從這個介面定義中,Microsoft IDL 編譯程式會產生頭檔,以供使用 介面的應用程式使用,以及原始程式碼來處理遠端過程調用。 Microsoft提供的 IDL 是以 DCE IDL 的簡單擴充為基礎,這是遠端過程調用 (RPC) 分散式運算的業界標準。 IDL 是介面設計師方便使用的工具,而不是 COM 互操作性的核心。 使用IDL時,您不需要針對每個程式設計環境手動建立頭檔。 如需詳細資訊,請參閱 定義 COM 介面

繼承在 COM 介面中偶爾使用。 COM 僅支援介面繼承,以重複使用與基底介面相關聯的合約。 COM 不支援選擇性繼承;因此,如果某個介面繼承自另一個介面,它就會包含基底介面所定義的所有函式。 此外,介面只會使用單一繼承,而不是多個繼承,從基底介面取得函式。

介面實作

您無法自行建立 COM 介面的實例。 相反地,您會建立實作 介面之類別的實例。 在 C++ 中,COM 介面會模型化為 抽象基類,這表示介面是只包含純虛擬成員函式的C++類別。 C++程式庫通過繼承一個或多個介面的成員函式簽名,覆寫每個成員函式,並為每個函式提供實作,來實作 COM 物件。

您可以使用任何支援函式指標概念的程式設計語言來實作 COM 介面。 例如,在 C 語言中,介面是一個結構,其中包含指向函式指標表的指標,每個方法在介面中都有一個對應的函式指標。

當您實作介面時,類別必須針對介面中的每個函式提供實作。 如果類別在介面函式中沒有作用,則實作可能是單一 return 語句。

COM 類別是使用唯一的 128 位元類別識別碼(CLSID)來識別,該標識符將類別與文件系統中的特定部署產生關聯。在 Windows 系統中,這通常是指使用 DLL 或 EXE 檔案進行部署。 CLSID 是 GUID,這表示沒有其他類別具有相同的 CLSID。 使用唯一類別標識碼可防止類別之間的名稱衝突。 例如,兩個不同的廠商可以撰寫名為 CStack 的類別,但兩個類別都有唯一的 CLSID,因此可以避免任何衝突的可能性。

您可以使用 CoCreateGuid 函式,或使用 Visual Studio 等 COM 撰寫工具,在內部呼叫此函式,來取得新的 CLSID。

IUnknown 介面

所有 COM 介面都繼承自 IUnknown 介面。 IUnknown 介面包含用於多型和實例生命周期管理的基本 COM 操作。 IUnknown 介面有三個成員函式,名為 QueryInterfaceAddRefRelease。 所有 COM 物件都必須實作 IUnknown 介面。

queryInterface成員函式提供 COM 的多型。 呼叫 QueryInterface,以判斷 COM 物件是否支援特定介面。 如果 COM 物件實作要求的介面,則 COM 物件會在 ppvObject 參數中傳回介面指標,否則會傳回 NULLQueryInterface 成員函式可讓您流覽 COM 物件支援的所有介面。

COM 物件實例的存留期是由其 參考計數所控制。 IUnknown 成員函式 AddRefRelease 控制計數。 AddRef 遞增計數,Release 遞減計數。 當參考計數達到零時,Release 成員函式可能會釋放 實例,因為沒有任何呼叫端使用它。

用戶端/伺服器模型

COM 類別會實作一些 COM 介面。 實作是由呼叫端與 COM 類別實例互動時執行的二進位檔所組成。 COM 可讓您在不同的應用程式中使用類別,包括那些在撰寫時不需要了解特定類別的應用程式。 在 Windows 平臺上,類別存在於動態連結庫 (DLL) 或另一個應用程式 (EXE) 中。

在其主機系統上,COM 會維護系統上所安裝 COM 物件之所有CLSID的註冊資料庫。 註冊資料庫是每個 CLSID 與裝載對應類別之 DLL 或 EXE 位置之間的對應。 每當呼叫端想要建立 COM 類別的實例時,COM 就會查詢此資料庫。 呼叫者只需要知道 CLSID 就能請求該類別的新實例。

COM 物件與其呼叫端之間的互動會模型化為用戶端/伺服器關聯性。 用戶端是向系統要求 COM 物件的呼叫端,而伺服器是裝載提供用戶端服務的 COM 物件的模組。

COM 客戶端是任何將 CLSID 傳遞給系統的呼叫者,來要求 COM 物件的實例。 建立實例最簡單的方式是呼叫 COM 函式,CoCreateInstance

CoCreateInstance 函式會建立指定 CLSID 的一個實例,並傳回用戶端所要求類型的介面指標。 用戶端負責管理實例的存留期,方法是在用戶端完成使用時呼叫其 Release 函式。 若要根據單一 CLSID 建立多個物件,請呼叫 CoGetClassObject 函式。 若要連接到已建立並執行的物件,請呼叫 getActiveObject函式

COM 伺服器會提供 COM 實作給系統。 伺服器會將 CLSID 與 COM 類別產生關聯,承載類別的實作,實作類別工廠以建立類別的實例,並提供卸載伺服器的功能。

注意

COM 伺服器與提供給系統的 COM 物件不同。

 

若要啟用建立 COM 物件,COM 伺服器必須提供 IClassFactory 介面的實作。 用戶端可以呼叫 CreateInstance 方法來要求 COM 物件的新實例,但這類要求通常會封裝在 CoCreateInstance 函式中。

您可以將 COM 伺服器部署為在客戶端進程運行時載入的共享函式庫(Windows 平臺上的 DLL),或部署為可執行模組(Windows 平臺上的 EXE)。 如需詳細資訊,請參閱 註冊 COM 應用程式

服務控制管理員

服務控制管理員 (SCM) 會處理 COM 物件實例的用戶端要求。 下列清單顯示事件的順序:

  • 用戶端會使用 COM 物件的 CLSID 透過呼叫 CoCreateInstance 等函式,向 COM 程式庫要求 COM 物件的介面指標。
  • COM 連結庫會查詢 SCM,以尋找與所要求 CLSID 對應的伺服器。
  • SCM 會找出伺服器,並從伺服器所提供的類別處理站要求建立 COM 物件。
  • 如果成功,COM 連結庫會傳回用戶端的介面指標。

COM 系統將伺服器物件連線到客戶端之後,客戶端和物件會直接通訊。 透過中繼運行時間呼叫不會增加額外負荷。

當您向主機系統註冊 COM 伺服器時,您可以指定啟用伺服器的不同方式。 下列清單顯示 SCM 可以啟用 COM 伺服器的三種方式:

  • 進程內:SCM 會傳回包含物件伺服器實作之 DLL 的檔案路徑。 COM 連結庫會載入 DLL,並查詢其類別處理站介面指標。
  • 本機:SCM 會啟動在啟動時註冊類別工廠的本地可執行檔,並且其介面指標可供系統和用戶端使用。
  • 遠端:本機的服務控制管理員從遠端計算機執行的服務控制管理員中取得類別工廠介面指標。

當用戶端要求 COM 物件時,COM 連結庫會連絡本機主機上的 SCM。 SCM 會定位出適當的 COM 伺服器,不論是本機或遠端,然後伺服器會傳回伺服器的類別工廠的介面指標。 當類別處理站可供使用時,COM 連結庫或用戶端可以使用類別處理站來建立要求的物件。 如需詳細資訊,請參閱 實作 IClassFactory

可重用

COM 支援 黑箱可重複使用性,這表示可重複使用元件的實作詳細數據不會公開給用戶端。 為了達到黑箱可重複使用性,COM 支援兩種機制,透過這些機制,一個物件可以重複使用另一個物件。 這兩種重複使用形式的名稱是 包含聚合。 依照慣例,重複使用的物件會命名為 內部物件,而使用內部物件的物件會命名為 外部物件

在內含專案中,外部對象的行為會當做內部物件的用戶端。 外部對象是內部對象的邏輯容器,當外部物件使用內部對象的服務時,外部物件會將實作委派給內部對象的介面。 這表示外部物件是以內部對象的服務來實作。 外部物件可能不支援與內部物件相同的介面,而外部物件可能會使用內部物件的介面來協助在外部對象上實作不同介面的元件。

在匯總中,外部物件會從內部對象公開介面,就像是在外部對象上實作一樣。 當外部物件一律將其中一個介面上的每個呼叫委派給內部物件的相同介面時,這會很有用。 聚合是一種便利性,可讓外部物件避免額外的實作負擔。

如需詳細資訊,請參閱 重複使用物件

記憶體和串流物件

COM 物件會使用 結構化記憶體將狀態儲存至檔案,這是一種持續性記憶體形式,可使用文件系統語意來瀏覽檔案的內容。 以這種方式處理檔案的內容,可讓進程之間有累加式存取、交易和共用等功能。

COM 永續性儲存規格提供兩種類型的儲存元素:儲存對象和數據流物件。 這些物件是由 COM 連結庫實作,而使用者應用程式很少實作這些儲存元素。 記憶體物件會實作 IStorage 介面,而數據流物件會實作 IStream 介面。

數據流物件包含數據,在概念上類似於文件系統中的單一檔案。 每個數據流都有訪問許可權和單一搜尋指標。 透過 IStream 介面,您可以讀取、寫入、搜尋,以及在數據流的基礎數據上執行其他作業。 數據流會使用文字字串來命名。 它可以包含任何內部結構,因為它是平坦的位元組流。 此外,IStream 介面中的函式類似於標準檔案句柄型函式,例如 ANSI C 執行時間連結庫中的函式。

記憶體物件在概念上類似於文件系統中的目錄。 每個記憶體可以包含任意數目的子記憶體物件和任意數目的數據流。 每個記憶體都有自己的訪問許可權。 透過 IStorage 介面,您可以執行列舉、移動、複製、重新命名、建立和刪除元素等作業。 記憶體物件不會儲存應用程式定義的數據,但它會隱含儲存它所包含的專案名稱(儲存和數據流)。

當它們根據主機平臺上的 COM 規格實作時,記憶體和數據流物件可在進程之間共用。 這可讓執行同進程或跨進程的物件具有其檔案記憶體的相等累加存取權。 因為 COM 會個別載入每個進程,所以它會使用作系統支援的共用記憶體機制來傳達已開啟元素的狀態,以及進程之間的存取模式。

結構化檔案中的每個記憶體和數據流物件都有一個名稱來識別它。 名稱是遵循特定慣例的字串。 如需詳細資訊,請參閱 記憶體物件命名慣例。 名稱會傳遞至 IStorage 函式,以指定要在儲存區中操作的元素。 根記憶體物件的名稱與基礎文件系統中的檔名相同,而且這些名稱必須遵循文件系統的慣例和限制。 傳遞至記憶體相關函式的字串,其名稱檔案會傳遞至文件系統,而不需要解譯或變更。

儲存物件中包含的項目名稱是由特定儲存物件的實作所管理。 儲存物件的所有實作都必須支援長度為 32 個字元的項目名稱,而且某些實作可能支援較長的名稱。 名稱是以保留大小寫的方式儲存,但在比較時不會考慮大小寫。 定義儲存專案名稱的應用程式必須選擇任一情況下運作的名稱。

您可以使用 COM 所實作的函式和介面,存取結構化記憶體檔案中的每個專案。 這表示其他應用程式可以使用 IStorage 介面函式,以提供類似目錄的服務來瀏覽檔案。 此外,其他應用程式也可以使用檔案的數據,而不需要執行寫入檔案的應用程式。 當 COM 應用程式存取另一個應用程式的結構化記憶體檔案時,會套用標準 Windows 訪問許可權,而且應用程式必須具有足夠的許可權。

COM 物件可以讀取和寫入永續性記憶體。 用戶端會根據作業的內容,查詢 COM 物件上其中一個持續性相關介面。 COM 物件可以實作下列介面的任何組合:

  • IPersistStorage:COM 物件會讀取並寫入其永續性狀態至記憶體物件。 客戶端會透過這個介面為物件提供 IStorage 指標。 這是唯一包含增量存取語意的持續性介面。
  • IPersistStream:COM 物件會讀取並寫入其永續性狀態至數據流物件。 用戶端透過這個介面,提供物件一個 IStream 指標。
  • IPersistFile:COM 物件會直接讀取和寫入其持續性狀態至基礎系統上的檔案。 除非透過這些介面存取基礎檔案,否則此介面不包含 IStorageIStream,但 IPersistFile 介面沒有記憶體和數據流的語意。 用戶端會以檔名提供 物件,並呼叫 SaveLoad 函式。

數據傳輸

結構化存儲提供 COM 物件與程序之間資料交換的基礎,稱為 統一資料傳輸。 在 OLE 2 中實作 COM 之前,Windows 上的數據傳輸是由 傳輸通訊協定指定,例如剪貼簿和拖放通訊協定。 每個傳輸通訊協定都有自己的一組函式,將通訊協定系結至查詢,而且需要特定的程式代碼來處理每個不同的通訊協定和交換程式。 統一數據傳輸代表所有數據傳輸,方法是使用 IDataObject 介面來分隔一般數據交換作業與傳輸通訊協定。

IDataObject 介面將標準的資料取得和設定操作、查詢和列舉功能,以及偵測物件中資料變化的通知進行封裝。 統一數據傳輸可提供數據格式的豐富描述,以及針對數據傳輸使用不同的儲存媒體。

在統一數據傳輸期間,所有通訊協定都會交換 IDataObject 介面的指標。 伺服器是數據源,並實作一個數據物件,可在任何數據交換通訊協定中使用。 用戶端從任何通訊協定收到 IDataObject 指標時,會取用數據,並從數據物件要求數據。 指標交換發生之後,雙方會透過 IDataObject 介面,以統一的方式處理數據交換。

COM 定義兩個啟用統一數據傳輸的數據結構。 FORMATETC 結構代表一般化剪貼簿格式,而 STGMEDIUM 結構代表傳輸媒體做為記憶體句柄。

用戶端會建立 FORMATETC 結構,以指出它向數據源要求的數據類型,而且數據源會使用它來描述其提供的格式。 用戶端會透過查詢其 IEnumFORMATETC 介面,來獲取數據源的可用格式。 如需詳細資訊,請參閱 FORMATETC 結構

用戶端會建立 STGMEDIUM 結構,並將它傳遞給 GetData 方法,而數據物件會傳回所提供 之 STGMEDIUM 結構中的數據。

STGMEDIUM 結構可讓客戶端和數據源選擇最有效率的交換媒體。 例如,如果要交換的數據非常大,數據源可以指出以磁碟為基礎的媒體做為其慣用格式,而不是主要記憶體。 此彈性可讓有效率的數據交換能夠像將指標傳遞至 IStorageIStream一樣快。 如需詳細資訊,請參閱 STGMEDIUM 結構

數據源的客戶端在數據變更時可能需要通知。 COM 會使用 通知接收器 對象來處理數據變更通知,該對象會實作 IAdviseSink 介面。 用戶端會實作通知接收物件和 IAdviseSink 介面,然後將 IAdviseSink 指標傳遞給數據源。 當數據源偵測到基礎數據的變更時,它會呼叫 IAdviseSink 方法來通知用戶端。 如需詳細資訊,請參閱 資料通知

遠端通訊

COM 可啟用遠端和分散式計算。 介面遠端處理 可讓成員函式傳回位於不同進程或不同主電腦上的 COM 物件的介面指標。 執行介面遠端協作的基礎結構對於客戶端和物件伺服器而言都是透明的。 用戶端和伺服器都不需要彼此的部署詳細數據,才能透過遠端介面進行通訊。 用戶端會呼叫相同介面上的成員函式,以與本機主機上或遠端電腦上的同進程、跨進程 COM 物件通訊。 相同介面上的本機和遠端呼叫與客戶端無法區分。

若要與 COM 物件通訊,客戶端通常會呼叫程序內實作。 如果 COM 物件正在處理中,則呼叫是直接的。 如果 COM 物件是跨進程或遠端,COM 會提供 Proxy 實作,以使用遠端過程調用 (RPC) 通訊協定將呼叫轉送至物件。

COM 物件一律會透過進程內實作接收來自用戶端的呼叫。 如果呼叫端為進程內,則呼叫是直接的。 如果呼叫端是進程外或遠端,COM 會提供 存根 實作,以從客戶端進程中的代理接收遠端程序呼叫。

封送處理 是包裝呼叫堆疊的過程,供從 Proxy 至存根傳輸之用。 反序列化 是在接收端發生的資料解包過程。 傳回值會被封送處理,從存根反序列化至代理。 這種通訊也稱為透過線路傳送 的通話

每種不同的數據類型都有序列化的規則。 介面指標也有封送處理協定,該協定會封裝在 CoMarshalInterface 函式中。 在大部分情況下,由系統提供的標準介面封送處理已足夠,但 COM 物件可以選擇性地實作 自定義介面封送處理 來控制遠端物件 Proxy 的建立。 如需詳細資訊,請參閱 Inter-Object 通訊

安全

COM 提供兩種形式的應用程式安全性。 其中一個是 啟用安全性,指定如何建立新的物件、用戶端如何連線到新的和現有的物件,以及特定公共服務,例如 Class Table 和 Running Object Table 的安全。 另一個是 呼叫安全性,這會指定安全性在用戶端與 COM 物件之間建立的連線中運作的方式。

啟用安全性是由服務控制管理員 (SCM) 自動套用的。 當 SCM 收到擷取 COM 物件的要求時,它會根據登錄中儲存的安全性資訊檢查要求。

SCM 實作通常會提供登錄驅動組態來管理已部署的類別,以及主機上的特定用戶帳戶。 如需詳細資訊,請參閱 啟用安全性

呼叫安全性會自動套用,或由應用程式強制執行。 如果應用程式提供設定資訊,COM會執行必要的檢查來保護應用程式。

自動機制會檢查程式的安全性,但不會檢查個別物件或方法的安全性。 如果應用程式需要更精細的安全性,COM 會提供應用程式可以使用的功能來執行自己的安全性檢查。

自動和自定義機制可以一起使用,因此應用程式可能會要求 COM 執行自動安全性檢查,然後執行自己的安全性檢查。

COM 呼叫安全性服務分為下列類別:

通常,客戶端會查詢 COM 物件以獲取 IClientSecurity 介面,這個介面由遠端層在本地實作。 用戶端會使用此介面來控制 COM 物件上個別介面 Proxy 的安全性,然後再對其中一個介面進行呼叫。

當呼叫到達伺服器時,伺服器可能會呼叫 CoGetCallContext 函式,以擷取 IServerSecurity 介面,以允許伺服器檢查客戶端的驗證,並視需要模擬用戶端。 IServerSecurity 物件在呼叫期間有效。

呼叫 CoInitializeSecurity 函式來初始化安全性層,並將指定的值設定為安全性預設值。 如果進程未呼叫 CoInitializeSecurity,COM 會在第一次封送處理或取消封送處理介面時自動呼叫它,並註冊系統預設安全性。 CoInitializeSecurity 函式可讓用戶端建立程式的預設呼叫安全性,以避免在個別 Proxy 上使用 IClientSecurityCoInitializeSecurity 函式可讓伺服器註冊進程的自動驗證服務。 如需詳細資訊,請參閱 使用 CoInitializeSecurity 設定 Process-Wide Security

COM 用戶端和伺服器

定義 COM 介面

註冊 COM 應用程式

COM 中的 安全性

進程、線程和單元