ActiveX控件的安全初始化和腳本操作 和 數字簽名SIGN


摘要:數字簽名SIGN保證控件在下載時候的安全性。如果你的代碼已經經過數字簽名,即使用戶IE的安全設置很高也能下載,安裝並登記。但是在頁面上初始化,或者用腳本運行這個控件,為了保證安全性,還需要進行MARK。   
    
  數字簽名SIGN   
  曹曉峰   
    
  摘要:數字簽名保證控件的安全性。數字簽名使用證書。證書一般有個人證書和授信公司證書。個人證書是對個人的信任,由個人承擔責任,控件每次下載時需要進行確認。公司證書是由第三方公司發布的,保證控件的安全性,公司證書需要付費。Windows授信的證書公司有VeriSign,SecureSign等等。由這些公司證書簽名的控件在下載的時候不需要確認。   
    
  一.工具   
  工具包括以下幾個軟件:   
  makecert.exe     制作cer格式的證書,即X.509證書,同時可以創建私鑰(防止抵賴)   
  cert2spc.exe     將cer格式證書轉換成spc格式證書,即PKCS   #7證書   
  signcode.exe     將證書簽署到ocx上去   
  chktrust.exe     檢查簽署證書后的ocx是否正確   
  certmgr.exe,是管理證書用的,可以從這里面導出root.cer來,不過沒有私鑰用不上。   
    
  二.步驟   
  下面是具體的步驟:   
  1、創建一個自己的證書文件:   
  makecert   /sv   "Record.PVK"   /n   "CN=SinoWave"   dream.cer   
  這里,Record.PVK表示新創建的私人密鑰保存文件名   
              SinoWave是你想顯示的公司名   
              dream.cer是你創建最后的證書文件名   
  這些根據你自己的要求填寫,最后得到Record.PVK和dream.cer兩個文件。其中,運行過程中需要輸入私人密鑰的保護密碼(sw),一定要輸入一致,不要出錯。   
    
  2、轉換cer格式為spc格式(可以省略),得到dream.spc文件。   
    
  cert2spc   dream.cer   dream.spc   
    
  3、用VS6工具中的   cabarc生成internet分發的CAB包,   
  cabarc.exe   N   DataTransfer.cab   DataTransfer.ocx   
    
  4、同時制作分發代碼(.htm,其中包含使IE可以自動下載安裝包的代碼)。   
  現在得到了2個文件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/",真正發行時最好使用url。   
    
  5、給CAB文件簽名   
  運行signcode,命令行的我沒有試驗通過,我是通過界面實現的。signcode運行后會出現數字簽名向導,首先選擇DataTransfer.CAB,下一步后會出現簽名選項,一種是典型,一種是自定義。選擇自定義,這樣才能從文件選擇證書,選擇前面制作的dream.spc,再下一步是選擇私鑰文件,選擇Record.PVK,輸入私人密鑰的保護密碼,選擇散列算法,一般用md5就可以了,下一步是選擇其他證書,直接下一步,填寫一下這個控件的聲明,用戶用ie瀏覽的時候,會彈出證書說明,再下一步是加蓋時間戳,例如http://timestamp.sheca.com/timestamp   
      
  6、用chktrust檢查是否正確   
  chktrust   -v   DataTransfer.CAB   
    
  7、將簽名后的DataTransfer.CAB和DataTransfer.htm復制到IIS的某個目錄下。並在IE中打開DataTransfer.htm文件進行測試。  

 

 



ActiveX控件的安全初始化和腳本操作MARK   
  曹曉峰   
    
  簡介   
  很多微軟的ActiveX控件(本地/遠程)都需要使用持久性數據進行初始化,而且它們大多數都是可以通過腳本進行操作的   (支持一個方法,事件和屬性的集合提供腳本語言操作)。初始化(使用持久性數據)和腳本操作都需要一個確定的安全性機制保證其安全性不被違背。   
    
  初始化安全性   
  當一個控件初始化時,可以從一個   IPersist*   接口獲得數據   (來自一個本地/遠端的URL)提供初始化狀態。這是一個潛在的安全隱患,因為數據可能來自一個不可信的數據源。不提供安全性保證的控件將無視數據源的安全性。   
  有兩種方法可以檢測控件的初始化安全性。第一種使用組件分組管理器(Component   Categories   Manager)創建一個正確的入口到系統注冊表。IE檢測注冊表之后才調用你的控件決定是否這些入口存在。第二種方法實現一個名稱為IObjectSafe的接口到你的控件。如果IE發現你的控件支持IObjectSafety,它調用   IObjectSafety::SetInterfaceSafetyOptions   方法然后才載入你的控件。   
  注意,第一種方法是基於組件的安全性,也就是如果設置了注冊表,那么整個組件的所有的接口都被標注為安全的;而第二種方法(實現IObjectSafety)是基於接口的,也就是它的作用范圍是接口,必須一個接口一個接口地標注。第二種方法的運行性能要優於第一種。在保證安全的情況下,兩種方法同時使用更好。   
    
  腳本操作安全性   
  代碼簽字可以保證用戶其代碼的可信度。但是運行一個   ActiveX   控件可以被腳本訪問將帶來幾個新的安全性問題。即使控件被認為是可靠的,如果使用不可信腳本代碼訪問也不能保證其安全性。比如,微軟的   Word   被認為是一個安全的程序,但是一個宏可以使用自動化模型的腳本刪除用戶計算機上的文件,載入宏病毒或者蠕蟲病毒。   
  有兩種方法提供你的控件的腳本操作安全性保證。第一種是使用組件分組管理器   (Component   Categories   Manager)   ——在組件導入以后在注冊表上面創建正確的入口。IE在腳本操作之前檢查注冊表確認安全性。第二種是實現一個   IObjectSafety   接口到你的控件。如果IE發現你的控件支持   IObjectSafety,就在導入控件之前調用   IObjectSafety::SetInterfaceSafetyOptions   方法來確保安全性腳本操作。   
  注意,第一種方法是基於組件的安全性,也就是如果設置了注冊表,那么整個組件的所有的接口都被標注為安全的;而第二種方法是基於接口的,也就是它的作用范圍是接口,必須一個接口一個接口地標注。第二種方法的運行性能要優於第一種。在保證安全的情況下,兩種方法同時使用更好。   
    
  方法一:IObjectSafety方法:   
  MFC:實現接口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)加入接口類的實現   
  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   
  在控件類加一個繼承接口即可   
  public   IObjectSafetyImpl<CAtlCtrl,   INTERFACESAFE_FOR_UNTRUSTED_CALLER     
                                                      |   INTERFACESAFE_FOR_UNTRUSTED_DATA>  
方法二:使用組件分組管理器   
  前面提到,IE通過檢測注冊表決定一個控件是否是可以安全性初始化和腳本操作的。IE通過調用   ICatInformation::IsClassOfCategories   方法決定是否控件支持給出的安全性分組。   
  如果一個控件使用組件分組管理器將自己注冊為安全的,該控件的注冊表入口就包含一個實現的分組關鍵字,該關鍵字含有一個或者兩個子鍵。一個子鍵設置控件支持安全性初始化,另一個設置支持安全性腳本操作。安全性初始化子鍵依賴於   CATID_SafeForInitializing;   安全性腳本操作子鍵依賴於   CATID_SafeForScripting。(其他組件分組子鍵定義在   Comcat.h   文件,而安全性初始化和腳本操作子鍵定義在   Objsafe.h   文件。)     
  下列演示顯示了一個注冊表入口(Tabular   Data   Control),該ActiveX控件同IE綁定支持創建數據驅動的網頁。因為控件是可以安全性腳本操作和初始化的,注冊表中將其標記為安全性腳本操作   
  (7DD95801-9882-11CF-9FA9-00AA006C42C4)   和安全性初始化   
  (7DD95802-9882-11CF-9FA9-00AA006C42C4)。   
  注意,這兩個GUID值是固定的。   
  將一個控件注冊為安全的   
  系統注冊表含有一個組件分組鍵來羅列每一個安裝在系統中的組件的功能性分組。下面演示了一個組件分組鍵。假設   CATID_SafeForScripting   (7DD95801-9882-11CF-9FA9-00AA006C42C4)   和   CATID_SafeForInitializing   (7DD95802-9882-11CF-9FA9-00AA006C42C4)   在列表之中。     
  要創建一個組件分組的子鍵,你的控件必須包含以下步驟:   
  1.創建一個組件分組管理器(Component   Categories   Manager)實例來接收   ICatRegister   接口的地址。   
  2.設置正確的   CATEGORYINFO   結構分量。   
  3.調用   ICatRegister::RegisterCategories   方法,將初始化的   CATEGORYINFO   結構變量傳遞給這個方法。   
      
  下面的例子演示如何使用這些步驟來完成一個名稱為   CreateComponentCategory全局函數,生成組件分組。   
  #include   "comcat.h"   
  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   作為參數傳遞給函數。   
        
  下面的例子演示如何將一個名稱為   RegisterCLSIDInCategory   加入實例控件。   
        
  #include   "comcat.h"   
  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;   
  }   
        
  一個控件應該在調用   DLLRegisterServer   函數是注冊安全性初始化和腳本操作。(DLLRegisterServer   由組件對象模型   [COM]   調用創建注冊表入口)   在實例組件中   DLLRegisterServer   函數調用了   CreateComponentCategory   和   RegisterCLSIDInCategory   函數   (它們保證控件的安全性初始化和腳本操作)。下面的就是   DLLRegisterServer   的實現。   
        
  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)   
  {   
  //   注冊控件是安全性初始化的   
  /////////////////////////////////////////////////   
  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;   
  //   注冊控件是安全性腳本操作的   
    
  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;   
  }   
  作為一個創建所有安全性分組入口到注冊表的控件,它也應該負責卸載所有的分組信息。COM   調用控件的   DLLUnRegisterServer   函數刪除相應的注冊表入口然后卸載該控件。   
        
  要卸載一個安全性初始化和腳本操作控件,控件應該完成以下任務:   
  1   創建一個組件分類管理器實例接收   ICatRegister   接口地址。   
  2   調用   ICatRegister::UnRegisterClassImplCategories   方法,將控件的   CLSID   和必要的   category   ID   作為參數傳遞   
        
  下面的例子演示如何完成一個   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;   
  }   
        
  我們前面提過,一個控件負責刪除安全性初始化和腳本操作入口,下面演示如何完成這兩個步驟:     
        
  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   
  以上函數形成一個文件,   
  #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;   
    
  //創建一個組件管理器實例(進程內)   
  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;   
  }   
    
  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;   
  }   
    
  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;   
  }   
    
  //下面來兩個函數分別在DllRegisterServer和DllUnregisterServer中使用,參數均為控件的GUID。   
  HRESULT   RegisterTwo(REFCLSID   clsid)//   注意clsid就是控件的GUID   
  {   
  //   注冊控件是安全性初始化的   
  /////////////////////////////////////////////////   
  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;   
  //   注冊控件是安全性腳本操作的   
    
  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   
  {   
  //   刪除注冊表入口   
  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;   
  }   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM