第二章 模組間內部機制介紹
2.4 模組使用機制
2.4.2 模組使用流程
2.4.2.1 呼叫模組元件
透過解析這個步驟的原理,將幫助我們了解外部使用者如何使用 COM 模組的 問題,而且也可以了解 COM 模組如何自動連結 Library,而無須使用者手動連結 的機制,以下我們先從呼叫 COM 模組所要用到的一個指令開始介紹,接著才介紹 使用者下達這個指令時,這個指令內部是如何幫我們找出我們的模組並建立起 來。
當要呼叫 COM 模組時我們是調用 COM 函式庫內的 API CoCreateInstance() 來幫我們完成呼叫的動作,其 API 函式原型如圖 2-13,以下我們將分別解釋這 個 API 所帶參數的意義,了解這些參數後可以幫助我們知道如何去呼叫自己的 Filter 模組。
HRESULT CoCreateInstance ( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid,
LPVOID* ppv );
圖 2-13 CoCreateInstance()函式原型
z rclsid: COM 模組 CLSID, CLSID 即是 GUID(Globally unique identifier) 的意思,所謂 GUID 是一個 128 位數字的全球唯一標示符,因此每個模組都 有自己一組獨一無二的 CLSID 註冊在操作系統中,外部使用者知道此 ID 即 可呼叫 COM 模組。
z pUnkOuter: 負責 COM 模組的外部函式,可以利用此參數對 COM 模組添加外 部函式,NULL 表示不使用外部函式。
z dwClsContext: 表示 COM 模組所使用服務器種類,例如在 DirectShow 中使 用的是 in-process DLL,代表的意思就是在本機器操作並以 DLL 的方式進 行連接,所代表的參數值是 CLSCTX_INPROC_SERVER。
23
z riid: 所要求介面函式的 IID,例如可以設定 IID_ITest 來取得 ITest 的介 面指標。
z ppv: 介面指標的位址(address),通過此參數接收由 riid 所要求的介面函 式指標。
了 解 以 上 各 個 參 數 的 意 義 之 後 , 我 們 藉 由 撰 寫 一 個 外 部 使 用 者 透 過 CoCreateInstance 呼叫一個 COM 模組的範例來說明這個 API 如何使用,其程式 碼如圖 2-14 所示
IExample* pIEX;
hr = CoCreateInstance ( CLSID_EXAMPLE,
圖 2-14 CoCreateInstance 範例程式碼
以這個範例可以看到,首先呼叫了一個叫 CLSID_EXAMPLE 的 COM 模組,不使 用外部函式,使用 In-Process DLL 的服務器,並且向 COM 模組要求一個 IID_IExample 的介面,取得之介面函式指標傳送給 pIEX 接收。
而 CoCreateInstance 會有 HRESULT 的回傳值,回傳值代表之意義如表 2-6 所示。當 COM 模組使用完畢,可以透過 Release()來釋放 COM 模組,一但釋放 COM 模組將從記憶體刪除,因此也就無法在使用其介面,適當的釋放將能更有效的運 用記憶體,避免記憶體被不必要的 COM 模組所佔住空間。
24
返回值 代表意義
S_OK COM 元件呼叫成功
REGDB_E_CLASSNOTREG 此 COM 元件沒有註冊或找不到此註冊值而發生錯誤 CLASS_E_NOAGGREGATION COM 元件不能被創建,要求外部函式不支持 E_NOINTERFACE 介面錯誤,COM 元件無法取得介面
E_POINTER 位址錯誤,介面位址為 NULL 表 2-6 HRESULT 回傳值代表意義
CoCreateInstance 內部流程解說
以上知道 CoCreateInstance 這個 API 如何使用之後,接著我們要來深入探 討當我們使用這個指令來呼叫 Filter 的時候,這個指令的內部是透過怎樣的機 制來幫我們把 Filter 給建立起來,以及如何幫我們連接 Library。
在講解流程之前先介紹一個重要的介面,稱為 IClassFactory,這個介面對 於 CoCreateInstance 來說極為重要,因為 Filter 模組在還沒有呼叫之前是不知 道 Filter 類別(Class)的名稱,而不知道類別的名稱將無法取得類別裡面所實現 的功能函式。因此 COM 規範針對這個問題就規定了每個 Filter 模組都必須實現 一個與之相對應的類別工廠(Class Factory),而這個類別工廠(Class Factory) 的任務就是負責管理這個 Filter 模組的資訊,因此在開發 Filter 模組的過程必 須把我們所開發的 Filter 類別工廠新增到 Filter 資料庫中以方便管理,而 CoCreateInstance 就是透過 IClassFactory 介面來下達指令給 Filter 資料庫以 查找我們所需的 Filter 類別工廠。
了解這個介面之後,我們將透過下面圖 2-15 的流程圖分成六個步驟來說明 Filter 模組呼叫的整體過程。
25
CoCreateInstance() CoCreateInstance 呼叫 CoGetClassObject 啟動執行,此函式 Virtual code 如圖 2-16 所示,此函式便會透過使用者所給之 GUID 向 DLL 資料庫查找屬於 GetPrcoAddress(…DllGetClassObject);
}
圖 2-16 CoGetClassObject 函式原型
z Step 2:找到 DLL 路徑之後便把 DLL 路徑輸入 LoadLlibrary 函式,而此函式 便會將其 Filter 模組的 DLL 載入。
z Step 3:DLL 載入之後,接著透過 GetProcAddress 函式來取得 DLL 中的 DllGetClassObject 的功能函式指標。
26
z Step 4:透過 Step3 所取得之 DllGetClassObject 指標就可以幫我去 Filter 資料庫查找並透過 QueryInterFace 函式呼叫出其 Filter 的類別工廠介面 IClassFactory,DllGetClassObject 函式 Virtual code 如圖 2-17 所示。
DllGetClassObject(…) {
//透過 Filter GUID 查找其類別工廠 CFactory* pFactory = new CFactory;
//查找 IClassFactory 介面指標
pFactory->QueryInterFace(IID_IClassFactory,(void**)&pClassFactory);
pFactory->Release();
}
圖 2-17 DllGetClassObject 函式原型
z Step 5: 呼 叫 出 類 別 工 廠 後 , 接 著 利 用 類 別 工 廠 裡 面 的 成 員 函 式 CreateInstance 便可以把要求的 Filter 類別呼叫出來。
z Step 6:Filter 類別呼叫完成後就可以透過 QueryInterFace 就可以取得所 要求功能介面函式指標提供給使用者使用。