E017DDD5-35BC-4AF5-A0FA-C55D4A43A03C"Width="184"Height="96"VIEWASTEXT>
然后再在IE中打开这个网页,我们发现又可显示,这时就是真正的Active控件了.
接上回,现在的Active是真正意义上的Active了,然后我们在Demo中加入ShowMessage方法:
publicvoidShowMessage(stringmsg)
{
if(msg!
=null)
{
MessageBox.Show(msg);
}
}
如下图示:
再重新编译.然后我们修改test.html的代码如下,新增一按钮用于调用刚加的ShowMessage时事,现在再在IE里打开网址,我们就可以做交互了.但结果却让人失望,我们发现IE跳出了拦截对话框,说Active控件不安全,不能与JS进行交互,如下图示:
根据提示,我们判断可以通过修改IE的设置使控件运行。
打开IE的工具->Internet选项->安全->本地Intranet->自定义级别->对没有标记为安全的ActiveX控件进行初始化和运行,将其值设为启用。
我们刷新页面,现在终于可以正确运行了,点击按钮会弹出"helloworld"的对话框.
当然,我们不能指望我们的客户和我们一样修改这个值。
毕竟,一是操作麻烦,二是给电脑带来了很大的安全风险。
在互联网上搜索之后,发现必须要实现IObjectSafety接口,把ActiveX控件标记为安全的ActiveX控件。
在搜索MSDN之后,我找到了IObjectSafety接口的定义。
这就好办了。
首先我们自己用C#实现这个接口:
实现IObjectSafety的方法如下:
先在命名空间里定义好该接口及实现方法,代码如下:
[Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
publicinterfaceIObjectSafety
{
voidGetInterfacceSafyOptions(System.Int32riid,outSystem.Int32pdwSupportedOptions,outSystem.Int32pdwEnabledOptions);
voidSetInterfaceSafetyOptions(System.Int32riid,System.Int32dwOptionsSetMask,System.Int32dwEnabledOptions);
}
图示:
然后,将我们的Demo继承此接口,用如下语句:
publicclassUserControl1:
System.Windows.Forms.UserControl,IObjectSafety,图示如上.
然后我们在类UserControl1中实现IObjectSafety的方法,代码如下(不需要修改):
publicvoidGetInterfacceSafyOptions(System.Int32riid,outSystem.Int32pdwSupportedOptions,outSystem.Int32pdwEnabledOptions)
{
pdwSupportedOptions=1; //不要修改该代码
pdwEnabledOptions=2; //不要修改该代码
//return;
}
publicvoidSetInterfaceSafetyOptions(System.Int32riid,System.Int32dwOptionsSetMask,System.Int32dwEnabledOptions)
{
//return;
}
如下图示:
重新编译,然后我们再次在IE里运行测试页面test.html,些时我们发现一切功能都正常了.图示如下:
至此,我们的Active基本成功,可以在此基础上根据自已的需要再加相关的功能.
下面将说明Active控件的发布.
上一篇记录了OCX的开发。
相对于开发工作,部署显得不是那么重要,但却是最困难的一步,也是最后一步。
控件的部门总的说来要完成以下几件事,或者说实现以下几个功能:
1、客户端第一次访问页面时自动安装(自动提示用户下载).
2、有新版本发布时自动下载最新版并安装。
经测试还发现,我的2.0框架下开发的控件,不能直接在客户机上进行注册(regAsm.exe:
C#开发的ActiveX控件只能用此程序注册,而不能使用RegSvr32),不知道是不是框架版本的问题,只能做成安装文件。
打包成安装文件具体的就不说了,和打包其他项目一样的步骤,但需要注意以下几点:
1、针对控件生成的DLL中有一个Register属性,该属性一定要设置为vsdraCOM,否则安装包只会将DLL文件简单拷到你的目标目录下,设置该值后,安装包才会进行注册表的写入,相当于进行regasm注册。
2、还有一个属性Permanent,看该属性的描述说明是说指定该DLL文件在卸载程序时是否移除,一般性的惯性思维都是设置为true时表示在卸载程序时要移除该dll文件,但经过测试发现设置为flase时才会进行移除操作。
如果不移除老版本的文件,新版本安装时不会覆盖原有的文件。
所以此属性显得很重要了。
一般说来发布OCX最常用的就是打成CAB包,该方式最简单但也最麻烦,需要搞证书。
所以我就自己实现了一种不需要证书的发布方式(前提是要生成安装包:
XXXX.msi)。
为了实现自动下载并安装,我把MSI包又制作为自解压包,之后文件名就变成了XXXXXXX.EXE了(这个包的制作就不多说了,用RAR直接可以进行压缩,网上也有很多介绍),这样当用户通过IE下载到本地后,IE会针对EXE类型的文件提示用户是否要现在运行,如果用户选择运行,就会进行自动安装了。
整个实现原理就是如此的简单,但在简单的过程中却有比较麻烦的步骤,让页面通知客房端IE下载控件的步骤就较麻烦了。
在控件所在的页面(我用的是JSP页面)只能使用JS脚本进行判断是否需要安装控件或者开载新版本进行安装。
下面我说说通过JS来判断控件的问题。
需要判断以下2个内容:
1、判断是否需要新安装。
2、是否发布了新版本的判断。
需要通过JS读取注册表(有这个东东注定该项目不能移植到非WINDOWS操作系统上了)下的控件的相关项或者键值。
如果没找到相应的键则表示没有进行安装,需要新安装。
具体判断逻辑直接上代码:
主函数:
varlastModiDatetime="<%=lastModiDatetime%>";//读取的服务器上的控件文件的最后修改日期
//判断当前本地计算机上是否已注册了控件。
//如果已注册了控件,再次判断是否是最新版本(JAVA现在没有直接提供对文件版本的获取,通过获取文件最后修改日期来变相对比文件新旧)
//要实现此功能,要求每一次发布新版本控件时必须在应用服务器上安装并注册最新的控件。
//并把安装路径下的最新RuntimeData.dll及RunData.dll和最新的安装包:
//wellalarmruntimedata.exe(根据RuntimeDataSetup.msi制作的自解压RAR安装包)拷到WEB应用程序下的petrol/cab下,拷RuntimeData.dll及RunData.dll是为了让JAVA能够获取控件的最新修改日期,拷wellalarmruntimedata.exe是为了让客户端IE在服务器上能找到下载路径及文件。
window.onload=function()
{
//在系统提示用户是否允许交互前判断当前控件是否加载,通过此属性判断当前是否注册了COM组件
varisReg=true;
//通过获取注册表属性得到安装路径
varhkey_root,hkey_path,hkey_key,path;
hkey_root="HKEY_CLASSES_ROOT"
hkey_file:
//\\CLSID\\{80C07FCA-841C-43C9-BD34-E9F3A6E87A85}\\InprocServer32\\">\\CLSID\\{80C07FCA-841C-43C9-BD34-E9F3A6E87A85}\\InprocServer32\\"; //80C07FCA-841C-43C9-BD34-E9F3A6E87A85就是开发篇强调的控件的GUID
try
{
varRegWsh=newActiveXObject("WScript.Shell");
hkey_key="CodeBase";
path=RegWsh.RegRead(hkey_root+hkey_path+hkey_key);
//获取本地机器上的组件版本号
varassembly=RegWsh.RegRead(hkey_root+hkey_path+"Assembly");
if(assembly!
=null)
{
//获取版本号
varversionNos=assembly.split("Version=")[1];
varversionNo=versionNos.substring(0,versionNos.indexOf(","));
//查找是否注册此版本号.找到最新版本的就采用此版本的安装路径
try
{
varpath=RegWsh.RegRead(hkey_root+hkey_path+versionNo+"\\"+hkey_key);
}
catch(ex)
{
isReg=false;
}
}
}
catch(e){}
//注意,当用户不允许脚本运行时path也是为NULL值,但只要安装注册了控件,不放管用户是否同意脚本运行,
//MWellAlm控件都会被创建成object
if((path==null&&document.getElementById("MWellAlm")==null)||!
isReg)
{
//没有注册此控件或者此控件已被卸载
msg.style.display='';
ctlload.style.display='none';
msg.innerHTML='本地计算机上未安装“井场预警及实时数据显示控件”或此控件已被卸载,不能正确显示此页面,需要下载最新的控件!
';
msg.innerHTML+="请[运行]自动下载的文件 或者 /petrol/cab/wellalarmruntimedata.exe'>点击下载最新显示控件";
document.getElementById("loadctl").click();
return;
}
if(path!
=null&&document.getElementById("MWellAlm")==null)
{
alert("当前控件已安装注册但不能正确显示,请关闭所有的IE浏览器后重试!
");
return;
}
document.getElementById("MWellAlm").stop();//调用控件的方法之一
//以下就是判断是否发布了新版本
if(path!
=null&&path!
="")
{
varfso=newActiveXObject("Scripting.FileSystemObject");
varf1=fso.GetFile(path);
//FSO获取的文件最后修改日期是UTC格式的,JAVASCRIPT还没有对此日期格式操作对象.所以只有转换成字符来处理
//而且此UTC日期串与从北京时间转换来的UTC串格式不相同
varlastDate=""+f1.DateLastModified;//转换成字符串
varv=newDate();
v.setTime(lastModiDatetime);
//直接通过toUTCString()的格式:
Thu,18Dec200806:
46:
04UTC
//获取的文件最后修改时间的UTC格式:
ThuDec1814:
46:
04UTC+08002008
//经测试,这2个UTC时间之前除格式不同外,在小时上还不相同。
前者比后者少了8小时
//在小时上+上8小时,因为这个UTC时间是从北京时间转换来的
v.setHours(v.getHours()+8);
//把从北京时间转换来的UTC时间转换成数组,在后者中逐个匹配,都匹配上就说明时间相等
vards=v.toUTCString().replace(",","").split("");
//为了保证不出现左右滑动匹配的情况(18不能与118、181匹配),在匹配项与被匹配项左右都加上一个空格
varbaseUtc=""+lastDate.replace("+0800","")+"";
varisEqual=true;
for(vari=0;i {
vartemp=""+ds+"";
if(baseUtc.indexOf(temp)==-1)
{
isEqual=false;
break;
}
}
if(!
isEqual)
{
document.getElementById("MWellAlm").click();
document.getElementById("MWellAlm").clearAll();
msg.style.display='';
ctlload.style.display='none';
msg.innerHTML='本地计算机上安装的“井场预警及实时数据显示控件”不是最新版本,不能正确显示此页面,需要下载最新的控件!
';
msg.innerHTML+="请[运行]自动下载的文件 或者 /petrol/cab/wellalarmruntimedata.exe'>点击下载最新显示控件";
msg.innerHTML+='
注意:
在安装最新控件之前请保证已通过[控制面板->添加或者删除程序]卸载了旧版本的控件!
';
document.getElementById("loadctl").click();
return;
}
msg.style.display='none';
ctlload.style.display='';
document.getElementById("MWellAlm").click();
document.getElementById("MWellAlm").start();
}
}
以上就是我实现的控件发布的全部内容。
为了保证能顺利发布,要求客户端IE最好把控件所在的站点加为信任站点,并调低安全