1 題外話
這幾天沒有什麼太多的任務,回顧一下DirectShow的東西,發現MSDN上有一篇文章不錯,翻譯一下,順便提高一下英文。
題目:How to Create aDirectShow Filter DLL
出處:http://msdn.microsoft.com/en-us/library/dd389096(v=VS.85).aspx
2 翻譯內容
本文描述如何在微軟的DirectShow中實現一個動態鏈接庫(DLL)的組件。本文是How to ImplementIUnknown的繼續,其描述瞭如何讓你的組件通過繼承CUnknown基類來實現一個IUnknown接口。
本文包括以下幾個章節。
· 類工廠和工廠模板(ClassFactories and Factory Templates)
· 工廠模板數組(FactoryTemplate Array)
· 動態鏈接庫函數(DLLFunctions)
註冊一個DirectShow filter需要以下附件的步驟,這些步驟並沒有在本文中描述。關於註冊filter可以參考How to RegisterDirectShow Filters。
2.1 類工廠和工廠模板
在客戶端創建一個COM對象實例之前,它會調用CoGetClassObject函數創建一個對象的類工廠。然後客戶端再調用類工廠的IClassFactory::CreateInstance方法。實際上是這個類工廠創建了這個組件並返回客戶端請求的接口的指針。(CoCreateInsatance函數合併了這些步驟,在它的內部調用了上面的那些過程。)
下面的圖示說明了方法調用的過程。
CoGetClassObject調用定義在DLL中的DllGetClassObject函數。DllGetClassObject創建一個類工廠並且返回一個類工廠接口的指針。DirectShow已經爲你實現了DllGetClassObject,但是這個方法以一種指定的方式依賴於你的代碼。爲了理解它是如何工作的,你必須理解DirectShow如何實現類工廠的。
一個類工廠就是用來創建其他COM對象的COM對象。一個類工廠創建一種類型的對象。在DirectShow中,每個類工廠就是一個類似於CClassFactory的類。類工廠通過其他的一個類來實現,CFactoryTemplate,也叫工廠模板。每一個類工廠持有一個工廠模板的指針。工廠模板包含一個特定組件的一下信息,例如組件標識符(CLSID),一個創建組件的函數指針。
DLL中聲明一個全局的工廠模板數組,一個數組對應一個組件。當DllGetClassObject創建一個新的類工廠時,它依據CLSID查找模板數組。假設找到匹配的一個元素,它創建一個持有對應模板指針的類工廠。當客戶端調用IClassFactory::CreateInstance,類工廠調用定義在模板中的實例化函數創建出相應的組件。
下面的示意圖說明了方法的調用過程。
你從這種架構中得到的好處是,針對於你的組件你只需要做很少的事情,例如實現實例化函數,而不必實現整個的類工廠。
2.2 工廠模板數組
工廠模板包含下面這些公有成員變量:
const WCHAR * m_Name; // Name
const CLSID * m_ClsID; // CLSID
LPFNNewCOMObject m_lpfnNew; // Function to create an instance
// of the component
LPFNInitRoutine m_lpfnInit; // Initialization function (optional)
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter; // Set-upinformation (for filters)
其中兩個函數指針,m_lpfunNew和m_lpfnInit定義爲下面的形式:
typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWNpUnkOuter, HRESULT *phr);
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, constCLSID *rclsid);
第一個是組件實例化的函數。第二個是可選的初始化函數。如果你提供一個初始化的函數,它將從內部在DLL的入口點(entry-point)被調用。(關於DLL的入口函數將在本文的後面進行討論。)
假設你已經創建了一個包含繼承至CUnknown的CMyCompone的DLL。在你的DLL中你必須提供下面的一些元素:
· 初始化函數,返回一個新的CMyComponet實例的公有方法。
· 一個命名爲g_Templates的全局的工廠模板數組。這個數組包含CMyComponet的工廠模板。
· 一個指定數組大小的全局變量g_cTemplates。
下面的例子說明如何聲明這些元素:
// Public method that returns a new instance.
CUnknown * WINAPI CMyComponent::CreateInstance(LPUNKNOWN pUnk,HRESULT *pHr)
{
CMyComponent *pNewObject= new CMyComponent(NAME("My Component"), pUnk, pHr );
if (pNewObject == NULL){
*pHr =E_OUTOFMEMORY;
}
return pNewObject;
}
CFactoryTemplate g_Templates[1] =
{
{
L"My Component", // Name
&CLSID_MyComponent, // CLSID
CMyComponent::CreateInstance, //Method to create an instance of MyComponent
NULL, // Initializationfunction
NULL // Set-up information (forfilters)
}
};
int g_cTemplates = sizeof(g_Templates) /sizeof(g_Templates[0]);
CreateInstance方法調用類的構造函數返回一個新實例的指針。參數pUnk是IUnknown接口的指針,你可以把這個參數傳遞給類的構造函數。參數pHr指向一個HRESULT。構造函數給pHr賦一個適當值,但是如果構造失敗,需要把這個值賦爲E_OUTOFMEMORY。
NAME宏在Debug版本將生成一個字符串,但是在最終的Release版本被設置爲NULL。在這個例子中被用來在Debug log給組件一個名字,但是在Release版中不佔用內存。
CreateInstance方法可以是任何名字,因爲在類工廠中引用的是工廠模板中的函數指針。然而,g_Templates和g_cTemplates是全局的變量,爲了使其在類工廠中被找到必須使用這個名字。
2.3 動態鏈接庫函數
爲了使其被註冊,卸載和被載入內存一個DLL必須實現下面的函數:
· DllMain: DLL的入口點。DllMain是一個系統保留的函數名。DirectShow的實現使用DllEntryPoint。可以參考PlatformSDK獲取更多的信息。
· DllGetClassObject: 創建一個類工廠實例。在前面的段落介紹過。
· DllCanUnloadNow: 詢問這個DLL是否可以被安全卸載。
· DllRegisterServer: 爲DLL創建註冊表。
· DllUnRegisterServer: 移除DLL的註冊表項。
在這些當中,前三個已經被DirectShow實現了。如果你的工廠模板通過m_lpfnInit成員變量提供一個初始化函數,這個初始化化函數將在DLL的如果點從內部調用。瞭解更多的系統什麼使用調用DLL入口點函數,參考DllMain。
你必須實現DllRegisterServer和DllUnregisterServer,但是DirectShow提供了一個已經做了一些必要工作的函數AMovieDllRegisterServer2。你的組件可以簡單的調用這個函數,如下面的例子所示:
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2(TRUE );
}
STDAPI DllUnregisterServer()
{
returnAMovieDllRegisterServer2( FALSE );
}
然而,在DllRegisterServer和DllUnregisterServer的內部,你可以定製一下所需的註冊過程。如果你的DLL包含一個filter,你也許需要一些附件的工作。更多的信息可以參考How to Register DirectShow Filters。
在你的模塊定義文件(.def)中,要暴露出所有接入點所需的函數,下面是一個.def文件的例子:
EXPORTS
DllGetClassObjectPRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServerPRIVATE
DllUnregisterServerPRIVATE
你可以使用Regsvr32.exe工具註冊DLL。