一、關於普通DLL插件的實現VC知識庫裡已有文章介紹,但在很多大型的軟件中(如ArcGis、Office)中都不是采用這種方法,基於COM的插件在當今的大型軟件中應用的更廣泛。
二、實現插件離不開三個要素
插件管理器(即:要使用這些插件的主程)
插件基本接口(即:插件與管理器都認可的接口標准),在DLL插件中這個要素通常是一個標准的C++頭文件,在COM插件中我們常用一個包含基類的接口COM,在這個COM中它只定義接口,不作任何實現。
插件COM,在這些COM中,它們只實現基類COM庫的接口,沒有自己的接口。
本文的例子中TestSvr是第一個要素,是插件管理器,plugin目錄下的兩個工程是第三個要素是插件COM,Interface code目錄下的工程是提供基本類的COM,是我們上面提到的第二個要素。
三、在DLL插件中我們搜索插件的方法是將插件存放在與主程序相對固定的目錄下,主程序通過搜索目錄文件而搜索插件,在COM插件裡我們不能用這種方法搜索插件,這裡我們采用的方法是每個COM插件都注到一下固定的COM分組中,主程序在COM分組中搜索出插件 。
以下的代碼是將一個COM注冊、反注冊到某一個COM組:
void SpecialReg(GUID ID_SubItem,BOOL bRegister)
{
ICatRegister* pICatRegister = NULL ;
HRESULT hr = ::CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,
CLSCTX_ALL,
IID_ICatRegister,
(void**)&pICatRegister) ;
if (FAILED(hr))
{
ErrorMessage("Could not create the ComCat component.", hr);
return ;
}
CATEGORYINFO CatInfo ;
CatInfo.catid = CATID_MyCategory ;
CatInfo.lcid = LOCALE_SYSTEM_DEFAULT ;
wcscpy(CatInfo.szDescription,sCateGoryName) ;
if (bRegister)
{
hr = pICatRegister->RegisterCategories(1, &CatInfo) ;
hr = pICatRegister->RegisterClassImplCategories(ID_SubItem,
1,
&CATID_MyCategory) ;
}
else
{
hr = pICatRegister->UnRegisterClassImplCategories(ID_SubItem,
1,
&CATID_MyCategory);
ICatInformation* pICatInformation = NULL ;
hr = pICatRegister->QueryInterface(IID_ICatInformation,
(void**)&pICatInformation) ;
IEnumCLSID* pIEnumCLSID = NULL ;
hr = pICatInformation->EnumClassesOfCategories(1,
&CATID_MyCategory,
0,
NULL,
&pIEnumCLSID) ;
CLSID clsid ;
hr = pIEnumCLSID->Next(1, &clsid, NULL) ;
if (hr == S_FALSE)
{
hr = pICatRegister->UnRegisterCategories(1, &CATID_MyCategory) ;
}
pIEnumCLSID->Release() ;
pICatInformation->Release() ;
}
if (pICatRegister)
{
pICatRegister->Release() ;
}
}
以下的代碼是從COM組中列舉各個COM組件
ICatInformationPtr pICatInformation(CLSID_StdComponentCategoriesMgr);
IEnumCLSID* pIEnumCLSID = NULL ;
HRESULT hr = pICatInformation->EnumClassesOfCategories(1,
&CATID_RdExcelExportCategory,
0,
NULL,
&pIEnumCLSID) ;
ASSERT(SUCCEEDED(hr)) ;
long nComandID = _BASE_COMMAND_ID_;
CLSID clsid ;
while ((hr = pIEnumCLSID->Next(1, &clsid, NULL)) == S_OK)
{
RDINTERFACELib::ICommandPtr pCommand(clsid);
}
本文配套源碼