2.4 使用组件分组管理器确保控件初始化和脚本操作安全性:
如果一个控件使用组件分组管理器将自己注册为安全,则该控件的注册表入口就包含一个分组关键字,该关键字含一个或者两个子键,其中一个设置控件支持安全性初始化,另一个设置支持安全性脚本操作。安全性初始化子键对应CATID_SafeForInitializing,安全性脚本操作子键对应CATID_SafeForScripting(组件分组子键定义在 Comcat.h 文件,而安全性初始化和脚本操作子键定义在 Objsafe.h 文件)。
要创建一个组件分组的子键,控件必须实现以下步骤:
(1)创建一个组件分组管理器实例来接收 ICatRegister 接口的地址;
(2)设置CATEGORYINFO结构变量;
(3)调用ICatRegister::RegisterCategories方法,将以上设定的CATEGORYINFO结构变量作为参数传递。 在控件编写时就需添加如下的全局函数:
HRESULT CreateComponentCategory(CATID catid, WCHAR *catDescription) { ICatRegister* pcr = NULL; HRESULT hr = S_OK; //创建一个组件管理器实例(进程内) hr=CoCreateInstance(CLSID_StdComponentCategoriesMgr,NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr); if (FAILED(hr)) return hr; //确信HKCR\Component Categories\{..catid...}键已经被注册 CATEGORYINFO catinfo; catinfo.catid = catid; catinfo.lcid = 0x0409;//英语 //确信提供的描述在127个字符以内 int len = wcslen(catDescription); if (len>127) len = 127; wcsncpy(catinfo.szDescription,catDescription,len); //确信描述使用'\0'结束 catinfo.szDescription[len] = '\0'; hr = pcr->RegisterCategories(1,&catinfo); pcr->Release(); return hr; } |
当一个子键被创建到需要的分组,控件应该按以下步骤注册到该分组:
(1)创建一个组件分组管理器实例接收ICatRegister接口地址;
(2)调用ICatRegister::RegisterClassImplCategories方法,将控件的CLSID和需要的category ID 作为参数传递。
对应的全局函数为:
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid) { //注册组件分组信息 ICatRegister* pcr = NULL; HRESULT hr = S_OK; hr=CoCreateInstance(CLSID_StdComponentCategoriesMgr,NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr); if(SUCCEEDED(hr)) { //注册已实现的类到分组 CATID rgcatid[1]; rgcatid[0] = catid; hr = pcr->RegisterClassImplCategories(clsid,1,rgcatid); } if(pcr != NULL) pcr->Release(); return hr; }
ActiveX的注册是在函数DLLRegisterServer中进行的,在组件中 DLLRegisterServer 函数调用了CreateComponentCategory和RegisterCLSIDInCategory函数来保证控件的安全性初始化和脚本操作。所以DLLRegisterServer应添加如下代码:
//注册控件是安全性初始化的 hr=CreateComponentCategory(CATID_SafeForInitializing,L"Controls safely initializable from persistent data!"); if(FAILED(hr))return hr; CLSID m_clsid;//本控件的的clsid CLSIDFromString(L"{0F968806-D214-11D5-9022-5254AB123A61}",&m_clsid); hr=RegisterCLSIDInCategory(m_clsid,CATID_SafeForInitializing); if(FAILED(hr))return hr; //注册控件是安全性脚本操作的 hr=CreateComponentCategory(CATID_SafeForScripting,L"Controls safely scriptable!"); if(FAILED(hr))return hr; hr=RegisterCLSIDInCategory(m_clsid,CATID_SafeForScripting); if(FAILED(hr))return hr; |
注意,一个创建安全性分组入口到注册表的控件,也应该负责卸载所有分组信息。要卸载一个已经安全性初始化和脚本操作的控件,应该按以下步骤:
(1)创建一个组件分类管理器实例接收ICatRegister接口地址;
(2)调用ICatRegister::UnRegisterClassImplCategories方法,将控件的CLSID和必要的category ID作为参数传递。
对应全局函数如下:
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid,CATID catid) { ICatRegister* pcr=NULL; HRESULT hr=S_OK; hr=CoCreateInstance(CLSID_StdComponentCategoriesMgr,NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr); if(SUCCEEDED(hr)){// 从分组卸载组件 CATID rgcatid[1]; rgcatid[0]=catid; hr=pcr->UnRegisterClassImplCategories(clsid,1,rgcatid); } if(pcr!=NULL)pcr->Release(); return hr; } |
控件卸载时调用的是DLLUnRegisterServer函数,故需在该函数中添加如下代码:
CLSID m_clsid; //本控件的clsid CLSIDFromString(L"{0F968806-D214-11D5-9022-5254AB123A61}",&m_clsid); hr=UnRegisterCLSIDInCategory(m_clsid,CATID_SafeForInitializing); if(FAILED(hr))return hr; hr=UnRegisterCLSIDInCategory(m_clsid,CATID_SafeForScripting); if(FAILED(hr))return hr; |
|