摘要:數(shù)字簽名SIGN保證控件在下載時(shí)候的安全性。如果你的代碼已經(jīng)經(jīng)過(guò)數(shù)字簽名,即使用戶IE的安全設(shè)置很高也能下載,安裝并登記。但是在頁(yè)面上初始化,或者用腳本運(yùn)行這個(gè)控件,為了保證安全性,還需要進(jìn)行MARK。
數(shù)字簽名SIGN
曹曉峰
摘要:數(shù)字簽名保證控件的安全性。數(shù)字簽名使用證書。證書一般有個(gè)人證書和授信公司證書。個(gè)人證書是對(duì)個(gè)人的信任,由個(gè)人承擔(dān)責(zé)任,控件每次下載時(shí)需要進(jìn)行確認(rèn)。公司證書是由第三方公司發(fā)布的,保證控件的安全性,公司證書需要付費(fèi)。Windows授信的證書公司有VeriSign,SecureSign等等。由這些公司證書簽名的控件在下載的時(shí)候不需要確認(rèn)。
一.工具
工具包括以下幾個(gè)軟件:
makecert.exe 制作cer格式的證書,即X.509證書,同時(shí)可以創(chuàng)建私鑰(防止抵賴)
cert2spc.exe 將cer格式證書轉(zhuǎn)換成spc格式證書,即PKCS #7證書
signcode.exe 將證書簽署到ocx上去
chktrust.exe 檢查簽署證書后的ocx是否正確
certmgr.exe,是管理證書用的,可以從這里面導(dǎo)出root.cer來(lái),不過(guò)沒(méi)有私鑰用不上。
二.步驟
下面是具體的步驟:
1、創(chuàng)建一個(gè)自己的證書文件:
makecert /sv "Record.PVK" /n "CN=SinoWave" dream.cer
這里,Record.PVK表示新創(chuàng)建的私人密鑰保存文件名
SinoWave是你想顯示的公司名
dream.cer是你創(chuàng)建最后的證書文件名
這些根據(jù)你自己的要求填寫,最后得到Record.PVK和dream.cer兩個(gè)文件。其中,運(yùn)行過(guò)程中需要輸入私人密鑰的保護(hù)密碼(sw),一定要輸入一致,不要出錯(cuò)。
2、轉(zhuǎn)換cer格式為spc格式(可以省略),得到dream.spc文件。
cert2spc dream.cer dream.spc
3、用VS6工具中的 cabarc生成internet分發(fā)的CAB包,
cabarc.exe N DataTransfer.cab DataTransfer.ocx
4、同時(shí)制作分發(fā)代碼(.htm,其中包含使IE可以自動(dòng)下載安裝包的代碼)。
現(xiàn)在得到了2個(gè)文件DataTransfer.CAB和DataTransfer.htm。
.htm中包含類似如下的代碼:
<OBJECT ID=" DataTransfer " CLASSID="CLSID: CA466D54-0684-49D2-B0C3-DD7E09EA76D3" CODEBASE="http://192.9.200.8/DataTransfer.CAB#version=1,0,0,0"></OBJECT>
注意:一定要寫上"http:// 192.9.200.8/",真正發(fā)行時(shí)最好使用url。
5、給CAB文件簽名
運(yùn)行signcode,命令行的我沒(méi)有試驗(yàn)通過(guò),我是通過(guò)界面實(shí)現(xiàn)的。signcode運(yùn)行后會(huì)出現(xiàn)數(shù)字簽名向?qū)?,首先選擇DataTransfer.CAB,下一步后會(huì)出現(xiàn)簽名選項(xiàng),一種是典型,一種是自定義。選擇自定義,這樣才能從文件選擇證書,選擇前面制作的dream.spc,再下一步是選擇私鑰文件,選擇Record.PVK,輸入私人密鑰的保護(hù)密碼,選擇散列算法,一般用md5就可以了,下一步是選擇其他證書,直接下一步,填寫一下這個(gè)控件的聲明,用戶用ie瀏覽的時(shí)候,會(huì)彈出證書說(shuō)明,再下一步是加蓋時(shí)間戳,例如http://timestamp.sheca.com/timestamp
6、用chktrust檢查是否正確
chktrust -v DataTransfer.CAB
7、將簽名后的DataTransfer.CAB和DataTransfer.htm復(fù)制到IIS的某個(gè)目錄下。并在IE中打開(kāi)DataTransfer.htm文件進(jìn)行測(cè)試。
ActiveX控件的安全初始化和腳本操作MARK
曹曉峰
簡(jiǎn)介
很多微軟的ActiveX控件(本地/遠(yuǎn)程)都需要使用持久性數(shù)據(jù)進(jìn)行初始化,而且它們大多數(shù)都是可以通過(guò)腳本進(jìn)行操作的 (支持一個(gè)方法,事件和屬性的集合提供腳本語(yǔ)言操作)。初始化(使用持久性數(shù)據(jù))和腳本操作都需要一個(gè)確定的安全性機(jī)制保證其安全性不被違背。
初始化安全性
當(dāng)一個(gè)控件初始化時(shí),可以從一個(gè) IPersist* 接口獲得數(shù)據(jù) (來(lái)自一個(gè)本地/遠(yuǎn)端的URL)提供初始化狀態(tài)。這是一個(gè)潛在的安全隱患,因?yàn)閿?shù)據(jù)可能來(lái)自一個(gè)不可信的數(shù)據(jù)源。不提供安全性保證的控件將無(wú)視數(shù)據(jù)源的安全性。
有兩種方法可以檢測(cè)控件的初始化安全性。第一種使用組件分組管理器(Component Categories Manager)創(chuàng)建一個(gè)正確的入口到系統(tǒng)注冊(cè)表。IE檢測(cè)注冊(cè)表之后才調(diào)用你的控件決定是否這些入口存在。第二種方法實(shí)現(xiàn)一個(gè)名稱為IObjectSafe的接口到你的控件。如果IE發(fā)現(xiàn)你的控件支持IObjectSafety,它調(diào)用 IObjectSafety::SetInterfaceSafetyOptions 方法然后才載入你的控件。
注意,第一種方法是基于組件的安全性,也就是如果設(shè)置了注冊(cè)表,那么整個(gè)組件的所有的接口都被標(biāo)注為安全的;而第二種方法(實(shí)現(xiàn)IObjectSafety)是基于接口的,也就是它的作用范圍是接口,必須一個(gè)接口一個(gè)接口地標(biāo)注。第二種方法的運(yùn)行性能要優(yōu)于第一種。在保證安全的情況下,兩種方法同時(shí)使用更好。
腳本操作安全性
代碼簽字可以保證用戶其代碼的可信度。但是運(yùn)行一個(gè) ActiveX 控件可以被腳本訪問(wèn)將帶來(lái)幾個(gè)新的安全性問(wèn)題。即使控件被認(rèn)為是可靠的,如果使用不可信腳本代碼訪問(wèn)也不能保證其安全性。比如,微軟的 Word 被認(rèn)為是一個(gè)安全的程序,但是一個(gè)宏可以使用自動(dòng)化模型的腳本刪除用戶計(jì)算機(jī)上的文件,載入宏病毒或者蠕蟲(chóng)病毒。
有兩種方法提供你的控件的腳本操作安全性保證。第一種是使用組件分組管理器 (Component Categories Manager) ——在組件導(dǎo)入以后在注冊(cè)表上面創(chuàng)建正確的入口。IE在腳本操作之前檢查注冊(cè)表確認(rèn)安全性。第二種是實(shí)現(xiàn)一個(gè) IObjectSafety 接口到你的控件。如果IE發(fā)現(xiàn)你的控件支持 IObjectSafety,就在導(dǎo)入控件之前調(diào)用 IObjectSafety::SetInterfaceSafetyOptions 方法來(lái)確保安全性腳本操作。
注意,第一種方法是基于組件的安全性,也就是如果設(shè)置了注冊(cè)表,那么整個(gè)組件的所有的接口都被標(biāo)注為安全的;而第二種方法是基于接口的,也就是它的作用范圍是接口,必須一個(gè)接口一個(gè)接口地標(biāo)注。第二種方法的運(yùn)行性能要優(yōu)于第一種。在保證安全的情況下,兩種方法同時(shí)使用更好。
方法一:IObjectSafety方法:
MFC:實(shí)現(xiàn)接口IObjectSafety
ATL:繼承接口IObjectSafetyImpl<CPolyCtl, INTERFACESAFE_FOR_UNTRUSTED_CALLER| INTERFACESAFE_FOR_UNTRUSTED_DATA >
具體方法:
一. MFC
1. 在控件類的頭文件里
(1)加入文件#include <objsafe.h>
(2)在類的定義里,聲明接口映射表,并加入接口定義(以嵌套類的形式)
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(ObjSafe, IObjectSafety)
STDMETHOD_(HRESULT, GetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions
);
STDMETHOD_(HRESULT, SetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions
);
END_INTERFACE_PART(ObjSafe);
2. 在控件類的CPP文件里
(1)建立接口映射表
BEGIN_INTERFACE_MAP( CMyCtrl, COleControl )
INTERFACE_PART(CMyCtrl, IID_IObjectSafety, ObjSafe)
END_INTERFACE_MAP()
(2)加入接口類的實(shí)現(xiàn)
ULONG FAR EXPORT CMyCtrl::XObjSafe::AddRef()
{
METHOD_PROLOGUE(CMyCtrl, ObjSafe)
return pThis->ExternalAddRef();
}
ULONG FAR EXPORT CMyCtrl::XObjSafe::Release()
{
METHOD_PROLOGUE(CMyCtrl, ObjSafe)
return pThis->ExternalRelease();
}
HRESULT FAR EXPORT CMyCtrl::XObjSafe::QueryInterface(
REFIID iid, void FAR* FAR* ppvObj)
{
METHOD_PROLOGUE(CMyCtrl, ObjSafe)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
const DWORD dwSupportedBits =
INTERFACESAFE_FOR_UNTRUSTED_CALLER |
INTERFACESAFE_FOR_UNTRUSTED_DATA;
const DWORD dwNotSupportedBits = ~ dwSupportedBits;
/////////////////////////////////////////////////////////////////////////////
// CStopLiteCtrl::XObjSafe::GetInterfaceSafetyOptions
// Allows container to query what interfaces are safe for what. We're
// optimizing significantly by ignoring which interface the caller is
// asking for.
HRESULT STDMETHODCALLTYPE
CMyCtrl::XObjSafe::GetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions)
{
METHOD_PROLOGUE(CMyCtrl, ObjSafe)
HRESULT retval = ResultFromScode(S_OK);
// does interface exist?
IUnknown FAR* punkInterface;
retval = pThis->ExternalQueryInterface(&riid,
(void * *)&punkInterface);
if (retval != E_NOINTERFACE) { // interface exists
punkInterface->Release(); // release it--just checking!
}
// we support both kinds of safety and have always both set,
// regardless of interface
*pdwSupportedOptions = *pdwEnabledOptions = dwSupportedBits;
return retval; // E_NOINTERFACE if QI failed
}
/////////////////////////////////////////////////////////////////////////////
// CStopLiteCtrl::XObjSafe::SetInterfaceSafetyOptions
// Since we're always safe, this is a no-brainer--but we do check to make
// sure the interface requested exists and that the options we're asked to
// set exist and are set on (we don't support unsafe mode).
HRESULT STDMETHODCALLTYPE
CMyCtrl::XObjSafe::SetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions)
{
METHOD_PROLOGUE(CMyCtrl, ObjSafe)
// does interface exist?
IUnknown FAR* punkInterface;
pThis->ExternalQueryInterface(&riid, (void * *)&punkInterface);
if (punkInterface) { // interface exists
punkInterface->Release(); // release it--just checking!
}
else { // interface doesn't exist
return ResultFromScode(E_NOINTERFACE);
}
// can't set bits we don't support
if (dwOptionSetMask & dwNotSupportedBits) {
return ResultFromScode(E_FAIL);
}
// can't set bits we do support to zero
dwEnabledOptions &= dwSupportedBits;
// (we already know there are no extra bits in mask )
if ((dwOptionSetMask & dwEnabledOptions) !=
dwOptionSetMask) {
return ResultFromScode(E_FAIL);
}
// don't need to change anything since we're always safe
return ResultFromScode(S_OK);
}
二. ATL
在控件類加一個(gè)繼承接口即可
public IObjectSafetyImpl<CAtlCtrl, INTERFACESAFE_FOR_UNTRUSTED_CALLER
| INTERFACESAFE_FOR_UNTRUSTED_DATA>
方法二:使用組件分組管理器
前面提到,IE通過(guò)檢測(cè)注冊(cè)表決定一個(gè)控件是否是可以安全性初始化和腳本操作的。IE通過(guò)調(diào)用 ICatInformation::IsClassOfCategories 方法決定是否控件支持給出的安全性分組。
如果一個(gè)控件使用組件分組管理器將自己注冊(cè)為安全的,該控件的注冊(cè)表入口就包含一個(gè)實(shí)現(xiàn)的分組關(guān)鍵字,該關(guān)鍵字含有一個(gè)或者兩個(gè)子鍵。一個(gè)子鍵設(shè)置控件支持安全性初始化,另一個(gè)設(shè)置支持安全性腳本操作。安全性初始化子鍵依賴于 CATID_SafeForInitializing; 安全性腳本操作子鍵依賴于 CATID_SafeForScripting。(其他組件分組子鍵定義在 Comcat.h 文件,而安全性初始化和腳本操作子鍵定義在 Objsafe.h 文件。)
下列演示顯示了一個(gè)注冊(cè)表入口(Tabular Data Control),該ActiveX控件同IE綁定支持創(chuàng)建數(shù)據(jù)驅(qū)動(dòng)的網(wǎng)頁(yè)。因?yàn)榭丶强梢园踩阅_本操作和初始化的,注冊(cè)表中將其標(biāo)記為安全性腳本操作
(7DD95801-9882-11CF-9FA9-00AA006C42C4) 和安全性初始化
(7DD95802-9882-11CF-9FA9-00AA006C42C4)。
注意,這兩個(gè)GUID值是固定的。
將一個(gè)控件注冊(cè)為安全的
系統(tǒng)注冊(cè)表含有一個(gè)組件分組鍵來(lái)羅列每一個(gè)安裝在系統(tǒng)中的組件的功能性分組。下面演示了一個(gè)組件分組鍵。假設(shè) CATID_SafeForScripting (7DD95801-9882-11CF-9FA9-00AA006C42C4) 和 CATID_SafeForInitializing (7DD95802-9882-11CF-9FA9-00AA006C42C4) 在列表之中。
要?jiǎng)?chuàng)建一個(gè)組件分組的子鍵,你的控件必須包含以下步驟:
1.創(chuàng)建一個(gè)組件分組管理器(Component Categories Manager)實(shí)例來(lái)接收 ICatRegister 接口的地址。
2.設(shè)置正確的 CATEGORYINFO 結(jié)構(gòu)分量。
3.調(diào)用 ICatRegister::RegisterCategories 方法,將初始化的 CATEGORYINFO 結(jié)構(gòu)變量傳遞給這個(gè)方法。
下面的例子演示如何使用這些步驟來(lái)完成一個(gè)名稱為 CreateComponentCategory全局函數(shù),生成組件分組。
#include "comcat.h"
HRESULT CreateComponentCategory(CATID catid,WCHAR* catDescription)
{
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
//創(chuàng)建一個(gè)組件管理器實(shí)例(進(jìn)程內(nèi))
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr);
if (FAILED(hr))
return hr;
// 確信 HKCR\Component Categories\{..catid...} 鍵已經(jīng)被注冊(cè)
CATEGORYINFO catinfo;
catinfo.catid = catid;
catinfo.lcid = 0x0409 ; // 英語(yǔ)
// 確信提供的描述在127個(gè)字符以內(nèi)
int len = wcslen(catDescription);
if (len>127)
len = 127;
wcsncpy(catinfo.szDescription,catDescription,len);
// 確信描述使用'\0'結(jié)束
catinfo.szDescription[len] = '\0';
hr = pcr->RegisterCategories(1,&catinfo);
pcr->Release();
return hr;
}
當(dāng)一個(gè)子鍵被創(chuàng)建到需要的分組,控件應(yīng)該注冊(cè)到該分組,需要以下步驟:
1.創(chuàng)建一個(gè)組件分組管理器實(shí)例接收 ICatRegister 接口地址。
2.調(diào)用 ICatRegister::RegisterClassImplCategories 方法,將控件的 CLSID 和需要的 category ID 作為參數(shù)傳遞給函數(shù)。
下面的例子演示如何將一個(gè)名稱為 RegisterCLSIDInCategory 加入實(shí)例控件。
#include "comcat.h"
HRESULT RegisterCLSIDInCategory(REFCLSID clsid,CATID catid)
{
// 注冊(cè)你的組件分組信息
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr);
if (SUCCEEDED(hr))
{
// 注冊(cè)已實(shí)現(xiàn)的類到分組
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->RegisterClassImplCategories(clsid,1,rgcatid);
}
if (pcr != NULL)
pcr->Release();
return hr;
}
一個(gè)控件應(yīng)該在調(diào)用 DLLRegisterServer 函數(shù)是注冊(cè)安全性初始化和腳本操作。(DLLRegisterServer 由組件對(duì)象模型 [COM] 調(diào)用創(chuàng)建注冊(cè)表入口) 在實(shí)例組件中 DLLRegisterServer 函數(shù)調(diào)用了 CreateComponentCategory 和 RegisterCLSIDInCategory 函數(shù) (它們保證控件的安全性初始化和腳本操作)。下面的就是 DLLRegisterServer 的實(shí)現(xiàn)。
STDAPI DllRegisterServer(void)
{
HRESULT hr; // return for safety functions
AFX_MANAGE_STATE(_afxModuleAddrThis);
if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(),_tlid))
return ResultFromScode(SELFREG_E_TYPELIB);
if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
return ResultFromScode(SELFREG_E_CLASS);
RegisterTwo(CLSID_SafeItem);
return NOERROR;
}
HRESULT RegisterTwo(REFCLSID clsid)
{
// 注冊(cè)控件是安全性初始化的
/////////////////////////////////////////////////
HRESULT hr;
hr = CreateComponentCategory(CATID_SafeForInitializing, L"Controls safely initializable from persistent data!");
if (FAILED(hr))
return hr;
hr = RegisterCLSIDInCategory(clsid, CATID_SafeForInitializing);
if (FAILED(hr))
return hr;
// 注冊(cè)控件是安全性腳本操作的
hr = CreateComponentCategory(CATID_SafeForScripting, L"Controls safely scriptable!");
if (FAILED(hr))
return hr;
hr = RegisterCLSIDInCategory(clsid, CATID_SafeForScripting);
if (FAILED(hr))
return hr;
return S_OK;
}
作為一個(gè)創(chuàng)建所有安全性分組入口到注冊(cè)表的控件,它也應(yīng)該負(fù)責(zé)卸載所有的分組信息。COM 調(diào)用控件的 DLLUnRegisterServer 函數(shù)刪除相應(yīng)的注冊(cè)表入口然后卸載該控件。
要卸載一個(gè)安全性初始化和腳本操作控件,控件應(yīng)該完成以下任務(wù):
1 創(chuàng)建一個(gè)組件分類管理器實(shí)例接收 ICatRegister 接口地址。
2 調(diào)用 ICatRegister::UnRegisterClassImplCategories 方法,將控件的 CLSID 和必要的 category ID 作為參數(shù)傳遞
下面的例子演示如何完成一個(gè) UnRegisterCLSIDInCategory 。
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;
}
我們前面提過(guò),一個(gè)控件負(fù)責(zé)刪除安全性初始化和腳本操作入口,下面演示如何完成這兩個(gè)步驟:
STDAPI DllUnregisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);
if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
return ResultFromScode(SELFREG_E_TYPELIB);
if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
return ResultFromScode(SELFREG_E_CLASS);
UnRegisterTwo(CLSID_SafeItem);
return NOERROR;
}
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;
}
附件, CXFMARK.h
以上函數(shù)形成一個(gè)文件,
#include "comcat.h"
const GUID CATID_SafeForInitializing =
{0x7DD95802,0x9882,0x11CF,{0x9F,0xA9,0x00,0xAA,0x00,0x6C,0x42,0xC4}};
const GUID CATID_SafeForScripting =
{0x7DD95801,0x9882,0x11CF,{0x9F,0xA9,0x00,0xAA,0x00,0x6C,0x42,0xC4}};
HRESULT CreateComponentCategory(CATID catid,WCHAR* catDescription)
{
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK;
//創(chuàng)建一個(gè)組件管理器實(shí)例(進(jìn)程內(nèi))
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr);
if (FAILED(hr))
return hr;
// 確信 HKCR\Component Categories\{..catid...} 鍵已經(jīng)被注冊(cè)
CATEGORYINFO catinfo;
catinfo.catid = catid;
catinfo.lcid = 0x0409 ; // 英語(yǔ)
// 確信提供的描述在127個(gè)字符以內(nèi)
int len = wcslen(catDescription);
if (len>127)
len = 127;
wcsncpy(catinfo.szDescription,catDescription,len);
// 確信描述使用'\0'結(jié)束
catinfo.szDescription[len] = '\0';
hr = pcr->RegisterCategories(1,&catinfo);
pcr->Release();
return hr;
}
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// 注冊(cè)你的組件分組信息
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr);
if (SUCCEEDED(hr))
{
// 注冊(cè)已實(shí)現(xiàn)的類到分組
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->RegisterClassImplCategories(clsid,1,rgcatid);
}
if (pcr != NULL)
pcr->Release();
return hr;
}
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;
}
//下面來(lái)兩個(gè)函數(shù)分別在DllRegisterServer和DllUnregisterServer中使用,參數(shù)均為控件的GUID。
HRESULT RegisterTwo(REFCLSID clsid)// 注意clsid就是控件的GUID
{
// 注冊(cè)控件是安全性初始化的
/////////////////////////////////////////////////
HRESULT hr;
hr = CreateComponentCategory(CATID_SafeForInitializing, L"Controls safely initializable from persistent data!");
if (FAILED(hr))
return hr;
hr = RegisterCLSIDInCategory(clsid, CATID_SafeForInitializing);
if (FAILED(hr))
return hr;
// 注冊(cè)控件是安全性腳本操作的
hr = CreateComponentCategory(CATID_SafeForScripting, L"Controls safely scriptable!");
if (FAILED(hr))
return hr;
hr = RegisterCLSIDInCategory(clsid, CATID_SafeForScripting);
if (FAILED(hr))
return hr;
return S_OK;
}
HRESULT UnRegisterTwo(REFCLSID clsid) // 注意clsid就是控件的GUID
{
// 刪除注冊(cè)表入口
HRESULT hr;
hr = UnRegisterCLSIDInCategory(clsid, CATID_SafeForInitializing);
if (FAILED(hr))
return hr;
//REFCLSID
hr = UnRegisterCLSIDInCategory(clsid, CATID_SafeForScripting);
if (FAILED(hr))
return hr;
return S_OK;
}
from:http://topic.csdn.net/t/20030704/00/1988641.html
posted on 2007-07-04 10:01
我風(fēng) 閱讀(1308)
評(píng)論(0) 編輯 收藏 引用