浏览器插件之ActiveX开发.docx
- 文档编号:4885237
- 上传时间:2022-12-11
- 格式:DOCX
- 页数:34
- 大小:1.15MB
浏览器插件之ActiveX开发.docx
《浏览器插件之ActiveX开发.docx》由会员分享,可在线阅读,更多相关《浏览器插件之ActiveX开发.docx(34页珍藏版)》请在冰豆网上搜索。
浏览器插件之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。
ATLActiveX输出文件较小,适合网络传输,但开发复杂度稍大;而MFCActiveX输出文件稍大(附带必要的MFCdll),但易于上手。
本文主要介绍基于MFC的ActiveX开发。
一、创建项目及添加接口
在V2008中,新建一个MFCActiveXControl项目:
点击“OK”后将弹出如下对话框:
依次点击“Next”按钮直到“ControlSettings”标签页:
由于本例子只演示仅提供函数接口不基于界面的ActiveX,故“Createcontrolbasedon”选择“(none)”即可。
点击"Finish”按钮,即完成了项目的创建,文件结构如下:
右击项目名称,选择“Properties”,在项目属性对话框中对“AllConfigurations”进行配置。
在“ConfigurationsProperties->General”标签页中,“UseofMFC”选择“UseMFCinastaticLibrary”,以便编译时将MFC相关库自动和控件一起打包。
对于“CharacterSet”的选择根据具体情况而定,须注意“UnicodeCharacterSet”和“Mulity-ByteCharacterSEt”对字符处理是完全不一样的(字符编码不一样,需要进行MultiByteToWideChar或WideCharToMultiByte转换)。
注意:
创建MFCActiveXControl时已经自动给项目添加了.def文件并做好了相应关联。
若对配置信息更改后导致编译的ocx注册不成功或提示找不到EntryPoint,可以检查一下Linker->Input的ModuleDefinitionFile是否配置正确,正常情况下已经自动配置好了,如下图:
接下来就可以在ActiveX中添加我们需要与外部交互的接口方法和属性了。
选择“ClassView”,右击“MyTestActiveXLib->_DMyTestActiveX”,在弹出的菜单中可以选择AddFunction或AddProperty来添加接口方法或接口属性:
这里以定义一个LONGAddFun(LONGnum1,LONGnum2) 的接口函数为例,添加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"
//Helperfunctiontocreateacomponentcategoryandassociated
//description
HRESULTCreateComponentCategory(CATIDcatid,WCHAR*catDescription);
//HelperfunctiontoregisteraCLSIDasbelongingtoacomponent
//category
HRESULTRegisterCLSIDInCategory(REFCLSIDclsid,CATIDcatid);
//HRESULTUnRegisterCLSIDInCategory-Removeentriesfromtheregistry
HRESULTUnRegisterCLSIDInCategory(REFCLSIDclsid,CATIDcatid);
Cathelp.cpp
#include"stdafx.h"
#include"comcat.h"
#include"strsafe.h"
#include"objsafe.h"
//HRESULTCreateComponentCategory-UsedtoregisterActiveXcontrolassafe
HRESULTCreateComponentCategory(CATIDcatid,WCHAR*catDescription)
{
ICatRegister*pcr=NULL;
HRESULThr=S_OK;
hr=CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr);
if(FAILED(hr))
returnhr;
//MakesuretheHKCR\ComponentCategories\{..catid...}
//keyisregistered.
CATEGORYINFOcatinfo;
catinfo.catid=catid;
catinfo.lcid=0x0409;//english
size_tlen;
//Makesuretheprovideddescriptionisnottoolong.
//Onlycopythefirst127charactersifitis.
//ThesecondparameterofStringCchLengthisthemaximum
//numberofcharactersthatmaybereadintocatDescription.
//TheremustberoomforaNULL-terminator.Thethirdparameter
//containsthenumberofcharactersexcludingtheNULL-terminator.
hr=StringCchLength(catDescription,STRSAFE_MAX_CCH,&len);
if(SUCCEEDED(hr))
{
if(len>127)
{
len=127;
}
}
else
{
//TODO:
Writeanerrorhandler;
}
//ThesecondparameterofStringCchCopyis128becauseyouneed
//roomforaNULL-terminator.
hr=StringCchCopy(catinfo.szDescription,len+1,catDescription);
//Makesurethedescriptionisnullterminated.
catinfo.szDescription[len+1]='\0';
hr=pcr->RegisterCategories(1,&catinfo);
pcr->Release();
returnhr;
}
//HRESULTRegisterCLSIDInCategory-
//Registeryourcomponentcategoriesinformation
HRESULTRegisterCLSIDInCategory(REFCLSIDclsid,CATIDcatid)
{
//Registeryourcomponentcategoriesinformation.
ICatRegister*pcr=NULL;
HRESULThr=S_OK;
hr=CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr);
if(SUCCEEDED(hr))
{
//Registerthiscategoryasbeing"implemented"bytheclass.
CATIDrgcatid[1];
rgcatid[0]=catid;
hr=pcr->RegisterClassImplCategories(clsid,1,rgcatid);
}
if(pcr!
=NULL)
pcr->Release();
returnhr;
}
//HRESULTUnRegisterCLSIDInCategory-Removeentriesfromtheregistry
HRESULTUnRegisterCLSIDInCategory(REFCLSIDclsid,CATIDcatid)
{
ICatRegister*pcr=NULL;
HRESULThr=S_OK;
hr=CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL,CLSCTX_INPROC_SERVER,IID_ICatRegister,(void**)&pcr);
if(SUCCEEDED(hr))
{
//Unregisterthiscategoryasbeing"implemented"bytheclass.
CATIDrgcatid[1];
rgcatid[0]=catid;
hr=pcr->UnRegisterClassImplCategories(clsid,1,rgcatid);
}
if(pcr!
=NULL)
pcr->Release();
returnhr;
}
注:
Cathelp.cpp中的代码是基于UnicodeCharacterSet的。
故项目配置时若改成Multi-ByteCharacterSet,需对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-Addsentriestothesystemregistry
STDAPIDllRegisterServer(void)
{
HRESULThr;//HResultusedbySafetyFunctions
AFX_MANAGE_STATE(_afxModuleAddrThis);
if(!
AfxOleRegisterTypeLib(AfxGetInstanceHandle(),_tlid))
returnResultFromScode(SELFREG_E_TYPELIB);
if(!
COleObjectFactoryEx:
:
UpdateRegistryAll(TRUE))
returnResultFromScode(SELFREG_E_CLASS);
//Markthecontrolassafeforinitializing.
hr=CreateComponentCategory(CATID_SafeForInitializing,
L"Controlssafelyinitializablefrompersistentdata!
");
if(FAILED(hr))
returnhr;
hr=RegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForInitializing);
if(FAILED(hr))
returnhr;
//Markthecontrolassafeforscripting.
hr=CreateComponentCategory(CATID_SafeForScripting,
L"Controlssafelyscriptable!
");
if(FAILED(hr))
returnhr;
hr=RegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForScripting);
if(FAILED(hr))
returnhr;
returnNOERROR;
}
//DllUnregisterServer-Removesentriesfromthesystemregistry
STDAPIDllUnregisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);
//删除控件初始化安全入口.
HRESULThr=UnRegisterCLSIDInCategory(CLSID_SafeItem,CATID_SafeForInitializing);
if(FAILED(hr))
returnhr;
//删除控件脚本安全入口
hr=UnRegisterCLSIDInCategory(CLSID_SafeItem,CATID_SafeForScripting);
if(FAILED(hr))
returnhr;
if(!
AfxOleUnregisterTypeLib(_tlid,_wVerMajor,_wVerMinor))
returnResultFromScode(SELFREG_E_TYPELIB);
if(!
COleObjectFactoryEx:
:
UpdateRegistryAll(FALSE))
returnResultFromScode(SELFREG_E_CLASS);
returnNOERROR;
}
注:
很多例子里DllUnregisterServer的写法与本文的写法不一致,结果导致卸载控件时(regsvr32/uxxxx.ocx)出现“调用某某ocx文件的DllUnregisterServer函数出错,错误代码:
0x80070002”错误。
究其根源,是DllUnregisterServer中删除注册表的顺序出了问题,“waxgourd0的专栏”中有篇文章对此做了详尽描述。
4、在解决方案下点击资源文件(Resources->MyTestActiveX.rc),点击右键在弹出的菜单中选择“ViewCode”,编辑资源文件信息并确保以下几个项目的正确性:
a)BLOCK的值为“040904e4”
b) OLESelfRegister的值为“\0”
c)VarFileInfo中的Translation后对应为“0x0409,1252”
到目前为止,可以编译项目,输出的ocx控件是可以正常运行的了。
~~~
浏览器插件之ActiveX开发
(二)
按照上文《浏览器插件之ActiveX开发
(一)》的步骤,能开发一个基于MFC的简单的ActiveX控件。
不过在实际操作中还是会遇到一些问题。
由于对COM编程了解得很少很少,有些问题我也没有找到很好的解决方法。
一、ActiveX需要引用其他dll的问题
我们的ActiveX需要对IC卡设备进行读写,所以需要调用设备自带的接口。
设备厂商提供了“mwhrf_bj.lib”、“mwhrf_bj.dll”和“mwrf32.h”等接口文件。
将“mwhrf_bj.lib”和“mwrf32.h”添加到项目中,ActiveX的接口方法中就可以调用接口文件中的方法了。
但是在编译时会出现“Project:
errorPRJ0050:
未能注册输出。
请尝试启用“每个用户的重定向”,或用提升权限从命令提示窗口中注册该组件”或“Project:
errorPRJ0050:
Failedtoregisteroutput.Pleaseensureyouhavetheappropriatepermissionstomodifytheregistry”的错误。
实际上该错误不是出现在编辑阶段,而是出现在注册编译后的ocx文件时。
V2008默认在编译成功后会自动注册编译后的ocx文件。
右击项目名称,选择“Properties”,在弹出对话框的“ConfigurationsProperties->Linker->General”中的RegisterOutput就可以配置编译后是否自动注册ocx,如下图所示:
之所以注册ocx时出错,是因为注册时找不到被调用的“mwhrf_bj.dll”文件。
将被调用的“mwhrf_bj.dll”文件放在ocx文件相同目录下或者其他%PATH%路径下(如Windows文件夹或System32文件夹等),则注册ocx时不会报错。
在开发环境中可以直接将要被调用的外部dll文件copy到Debug或Release目录下即可,也可以在PreBuild脚本里将外部dll文件COPY到编译目标文件夹,如:
注:
可参考“
二、ActiveX的调试方法
在V2008下可以对ActiveX按如下方式进行调试:
1、准备好Demo.html文件并写好测试程序,该页面中需通过来引用需测试的ocx控件(关于如何在html页面中调用控件在后续文章将专门提及)。
2、在2008中右击项目名称,选择“Properties”,在弹出框中的Debugging配置页里配置好Command和CommandArgs参数:
Command:
本地IE浏览器的路径,如“C:
\ProgramFiles\InternetExplorer\IEXPLORE.EXE”
Command Args:
已经创建好的用于测试ocx的html文件路径(如上面提及的Demo.html文件路径)
3、在程序中需调试的地方设置断点。
按F5运行后将自动启动IE并打开对应的html测试文件,在断点处会中断运行进入调试状态。
三、ActiveX的接口实现out/ref参数及返回自定义结构体数据的问题
有时候ActiveX的接口方法只返回一个数据并不能满足我们的实际要求。
例如通过ActiveX的getPersons()方法返回一堆的人员信息,那必定是一个列表或数组,而且每个Person还包含姓名、性别等各种信息,这个时候返回值就相当复杂了。
为了简单起见,还是已通过ActiveX进行读卡号来举例。
一般情况下,只要该插件提供以下接口即可满足需
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 浏览器 插件 ActiveX 开发
![提示](https://static.bdocx.com/images/bang_tan.gif)