好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

浏览器插件之ActiveX开发

浏览器插件之ActiveX开发

一般的Web应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题。在有特殊需求(如涉及数据安全的金融业务数据交互、需插件才能实现的与本地设备的交互等)的情况下可以酌情慎用。

     浏览器插件总体可以划分为两大阵营,即IE支持的插件以及非IE支持的插件。本来在Netscape时代,对于浏览器插件是有公用的规范的(NPAPI),一开始所有浏览器都支持该规范,包括IE。后来出于商业原因,微软的IE不再支持NPAPI,改而自己开发了一套基于COM的ActiveX体系,但这个体系对于非IE浏览器是拒绝支持的。所以目前的状况基本是,IE浏览器仅支持ActiveX控件,而Firefox、Chrome等浏览器只支持另一类接口(XPCOM或NPAPI)。要想实现一个Web插件,至少需要同时考虑IE支持的AceiveX版以及非IE支持的Plugin版(Flash等插件对于IE与非IE浏览器都是不同的)。

     ActiveX的开发可以用C#、VB及C++等语言。用C++开发ActiveX既可以使用ATL,也可以使用MFC。ATL ActiveX输出文件较小,适合网络传输,但开发复杂度稍大;而MFC ActiveX输出文件稍大(附带必要的MFC dll),但易于上手。本文主要介绍基于MFC的ActiveX开发。

一、创建项目及添加接口

     在Vs.net 2008中,新建一个MFC ActiveX Control项目:

                           

    点击“OK”后将弹出如下对话框:

                           

    依次点击“Next”按钮直到“Control Settings”标签页:

                           

     由于本例子只演示仅提供函数接口不基于界面的ActiveX,故“Create control based on”选择“(none)”即可。点击"Finish”按钮,即完成了项目的创建,文件结构如下:

                            

    右击项目名称,选择“Properties”,在项目属性对话框中对“All Configurations”进行配置。在“Configurations Properties->General”标签页中,“Use of MFC”选择“Use MFC in a static Library”,以便编译时将MFC相关库自动和控件一起打包。对于“Character Set”的选择根据具体情况而定,须注意“Unicode Character Set”和“Mulity-Byte Character SEt”对字符处理是完全不一样的(字符编码不一样,需要进行MultiByteToWideChar或WideCharToMultiByte转换)。

 注意: 创建MFC ActiveX Control时已经自动给项目添加了.def文件并做好了相应关联。若对配置信息更改后导致编译的ocx注册不成功或提示找不到EntryPoint,可以检查一下Linker->Input的Module Definition File是否配置正确,正常情况下已经自动配置好了,如下图:

                   

   接下来就可以在ActiveX中添加我们需要与外部交互的接口方法和属性了。选择“Class View”,右击“MyTestActiveXLib->_DMyTestActiveX”,在弹出的菜单中可以选择Add Function或Add Property来添加接口方法或接口属性:

                   

   这里以定义一个 LONG AddFun(LONG num1,LONG num2)  的接口函数为例,添加Menthod如下图所示:

                   

    点击Finish后,即可在“MyTestActiveXCtrl.cpp”文件找到刚添加的接口函数代码:

                 

     在函数体中完成自定义的业务逻辑即可。

二、实现安全接口

       上述项目编译后即可生成ocx文件,该ocx即可嵌入html在IE中运行。但如果该ocx对应页面是放在真实的web服务器上,访问该页面执行ActiveX里对应接口时IE将会提示“无相关属性,需要设置其初始化和脚本运行的安全性”等信息。这是因为ActiveX要在远程IE上执行,需要实现安全接口。有关控件的初始化和脚本安全问题,《 再谈IObjectSafety 》一文及其引用的Microsoft文章做了较详致描述。

      对于ATL写的ActiveX,实现IObjectSafety即可, 这里 有ATL实现安全接口的详细的描述。

      对于MFC写的ActiveX,可以通过修改注册表的方式来实现控件的安全性,微软也提供的 详细的文档描述 。具体实现步骤如下:

      1、首先在项目中添加Cathelp.h和Cathelp.cpp两个文件,其内容如下所示。

       Cathelp.h

#include  "  comcat.h  " 

 //   Helper function to create a component category and associated
  //   description 
HRESULT CreateComponentCategory(CATID catid, WCHAR*  catDescription);

  //   Helper function to register a CLSID as belonging to a component
  //   category 
 HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid);

  //   HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry  
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid);

       Cathelp.cpp

#include  "  stdafx.h  "  
#include   "  comcat.h  "  
#include   "  strsafe.h  "  
#include   "  objsafe.h  " 


 //   HRESULT CreateComponentCategory - Used to register ActiveX control as safe  
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;
 
      //   Make sure the HKCR\Component Categories\{..catid...}
      //   key is registered. 
     CATEGORYINFO catinfo;
    catinfo.catid  =  catid;
    catinfo.lcid  =  0x0409  ;  //   english 
     size_t len;
      //   Make sure the provided description is not too long.
      //   Only copy the first 127 characters if it is.
      //   The second parameter of StringCchLength is the maximum
      //   number of characters that may be read into catDescription.
      //   There must be room for a NULL-terminator. The third parameter
      //   contains the number of characters excluding the NULL-terminator. 
    hr = StringCchLength(catDescription, STRSAFE_MAX_CCH, & len);
      if   (SUCCEEDED(hr))
        {
          if  (len> 127  )
          {
            len  =  127  ;
          }
        }   
      else  
        {
            //   TODO: Write an error handler; 
         }
      //   The second parameter of StringCchCopy is 128 because you need 
      //   room for a NULL-terminator. 
    hr = StringCchCopy(catinfo.szDescription, len +  1  , catDescription);
      //   Make sure the description is null terminated. 
    catinfo.szDescription[len +  1 ] =  '  \0  '  ;
 
    hr  = pcr->RegisterCategories( 1 , & catinfo);
    pcr -> Release();
 
      return   hr;
}
 
  //   HRESULT RegisterCLSIDInCategory -
  //        Register your component categories information  
 HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
  //   Register your component categories information. 
    ICatRegister *pcr =  NULL ;
    HRESULT hr  =  S_OK ;
    hr  =  CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (  void **)& pcr);
      if   (SUCCEEDED(hr))
    {
         //   Register this category as being "implemented" by the class. 
       CATID rgcatid[ 1  ] ;
       rgcatid[  0 ] =  catid;
       hr  = pcr->RegisterClassImplCategories(clsid,  1  , rgcatid);
    }
 
      if  (pcr !=  NULL)
        pcr -> Release();
            
      return   hr;
}
 
  //   HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry  
 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))
    {
         //   Unregister this category as being "implemented" by the class. 
       CATID rgcatid[ 1  ] ;
       rgcatid[  0 ] =  catid;
       hr  = pcr->UnRegisterClassImplCategories(clsid,  1  , rgcatid);
    }
 
      if  (pcr !=  NULL)
        pcr -> Release();
 
      return   hr;
} 

     注 :Cathelp.cpp中的代码是基于Unicode Character Set的。故项目配置时若改成Multi-Byte Character Set,需对Cathelp.cpp中代码做相应修改,否则编译不过;

     2、在MyTestActiveX.cpp文件中,添加 CLSID_SafeItem 的定义:

         

     CLSID_SafeItem的值是根据xxxCtrl.cpp(本例中是MyTestActiveXCtrl.cpp)文件中 IMPLEMENT_OLECREATE_EX 的定义而来的(实际上就是ActiveX的CLASSID)。本例中MyTestActiveXCtrl.cpp文件中 IMPLEMENT_OLECREATE_EX 的的值如下:

         

     将“ 0x1345c26b, 0xe979, 0x45a5, 0x99, 0x7d, 0x94, 0x27, 0xfb, 0x81, 0xe7, 0x7 ”简单的在适当位置添加“{”和“}”括弧即变成了CLSID_SafeItem的值“ 0x1345c26b, 0xe979, 0x45a5,  { 0x99, 0x7d, 0x94, 0x27, 0xfb, 0x81, 0xe7, 0x7 } ”。

      另外,MyTestActiveX.cpp文件起始处还需要引入如下两个文件方能正常编译:

             

   

    3、修改MyTestActiveX.cpp中 DllRegisterServer 和 DllUnregisterServer 函数,代码如下(照抄即可):

 //   DllRegisterServer - Adds entries to the system registry 
 
STDAPI DllRegisterServer(  void  )
{
    HRESULT hr;      //   HResult used by Safety Functions 
  
    AFX_MANAGE_STATE(_afxModuleAddrThis);
 
      if  (! AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
        return   ResultFromScode(SELFREG_E_TYPELIB);
 
      if  (! COleObjectFactoryEx::UpdateRegistryAll(TRUE))
        return   ResultFromScode(SELFREG_E_CLASS);
 
      //   Mark the control as safe for initializing. 
                                              
    hr  =  CreateComponentCategory(CATID_SafeForInitializing, 
         L  "  Controls safely initializable from persistent data!  "  );
      if   (FAILED(hr))
        return   hr;
 
    hr  =  RegisterCLSIDInCategory(CLSID_SafeItem, 
         CATID_SafeForInitializing);
      if   (FAILED(hr))
          return   hr;
 
      //   Mark the control as safe for scripting. 
  
    hr  =  CreateComponentCategory(CATID_SafeForScripting, 
                                 L  "  Controls safely  scriptable!  "  );
      if   (FAILED(hr))
          return   hr;
 
    hr  =  RegisterCLSIDInCategory(CLSID_SafeItem, 
                        CATID_SafeForScripting);
      if   (FAILED(hr))
          return   hr;
 
      return   NOERROR;
}



  //   DllUnregisterServer - Removes entries from the system registry 
 
STDAPI DllUnregisterServer(  void  )
{
    AFX_MANAGE_STATE(_afxModuleAddrThis);  

      //   删除控件初始化安全入口.    
    HRESULT hr= UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);  

      if   (FAILED(hr))  
          return   hr;  

      //   删除控件脚本安全入口    
    hr= UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);  

      if   (FAILED(hr))  
          return   hr;  

      if  (! AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))  
          return   ResultFromScode(SELFREG_E_TYPELIB);  

      if  (! COleObjectFactoryEx::UpdateRegistryAll(FALSE))  
          return   ResultFromScode(SELFREG_E_CLASS);  

      return   NOERROR;
} 

   注 : 很多例子里 DllUnregisterServer 的写法与本文的写法不一致,结果导致卸载控件时(regsvr32 /u xxxx.ocx)出现“ 调用某某ocx文件的DllUnregisterServer函数出错,错误代码:0x80070002 ”错误。究其根源,是 DllUnregisterServer 中删除注册表的顺序出了问题,“ waxgourd0的专栏 ”中 有篇文章 对此做了详尽描述。

      到目前为止,可以编译项目,输出的ocx控件是可以正常运行的了。~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MFC ActiveX开发参考资料:

A Complete ActiveX Web Control Tutorial  

ATL ActiveX开发参考资料:

洞庭散人: COM组件开发实践(二)

综合参考资料:

杨峰- COM组件与设计应用


=======================================================================
野文(Jasson Qian)
------------------------------------------------------
博客园: http://qguohog.cnblogs.com
CSDN: http://blog.csdn.net/sallay

 

分类:  Web开发 ,  C&C++

标签:  ActiveX ,  COM

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于浏览器插件之ActiveX开发的详细内容...

  阅读:49次