如何使用C#开发“类ActiveX组件”
首先解释一下“类ActiveX组件”:ActiveX在本质上是属于COM组件,是二进制组件,并且是属于非托管的;而.NET开发出来的东西都是托管的,不是真正意义上的二进制组件。因此从严格意义上来讲,C#开发出来的东西不是真正的ActiveX组件,因此才叫做“类ActiveX组件”。
这篇文章本来是想在上周写的,但是上周一直忙于公司的事情,就没有时间来写。本文主要是介绍一下项目中的经历,分享一下开发的心得。
前一段时间,公司在项目中要增加一个新的模块,大致的结构是这样子的:
1、这是一个C/S的应用;
2、在客户端安装一个一个启动的程序(这个程序很小,仅有几M);
3、所有的资源、数据都在服务器端存放,如果需要(比如数据、图片、声音等),客户端程序就判断本地是否有这些资源,如果没有就从服务器下载然后保存在本地,如果有就就直接在本地读取;
4、要在Web浏览器中启动这个应用程序。
这样的结果对我来讲是第一次遇到,并且对时间要求的很紧,所以当时简单的思考了一下,觉得这样主要是面临两个问题——1、如何从网页中启动一个C/S程序;2、浏览器如何才能知道这个C/S程序是否已经安装,因为如果没有安装就需要提示用户安装,如果已经安装就直接打开。
对于第一个问题,因为知道QQ能从网页中发起一个会话,所以研究了一下相关的内容,很容易就解决了,基本原理是这样的:在注册表里面添加一个自定义的超链接协议即可,然后就可以通过这种方式打开你的程序:
1 <a href="myProtocol://PragramName">打开我的协议</a>
就可以打开自己的客户端程序,但是在浏览器中会有一个提示,这个不太好,目前也没有什么好的办法解决,从后面的这个URL可以找到这种方式的相关介绍,在这里我就不多做赘述了:http://zhoumf1214.blog.163.com/blog/static/524194020118163572382/。
而对于第二个问题,就想到了通过ActiveX的方式进行解决,但是这样就会有个缺点,就是这样仅支持在IE浏览器中运行,所以对于其他的浏览器,就采用提示的方式解决,如果有哪位大侠有更好的方式的话,还望能够不吝指教,在此我先行谢过了,毕竟我这方式对用户来讲还是不太友好。
在.NET中,微软为我们提供了与COM的互操作接口,也就是说,如果我们使用C#开发的ActiveX插件如果要被浏览器加载和使用,就得把托管组件包装成非托管的COM组件才行。幸运的是,.NET提供了CCW(COM可调用包装)机制,通过这个,我们就可以把我们的ActiveX包装成一个ActiveX应用。
那么,一个ActiveX插件是如何被浏览器使用的呢?这个我们可以从熟悉的Flash入手来看,我们打开一个插入Flash的代码可以看到Flash是这样被加载的:
1 <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0">
这里有两个地方需要我们注意:classid和codebase,classid就是我们这个COM组件的GUID,codebase就是我们的组件包的下载位置,这个东西在稍后的开发中就会用到,在这里解释一下浏览器的加载方式:
1、浏览器加载HTML;
2、如果碰到object对象的时候就查找通过classid指定GUID的COM对象,并试图去加载;
3、如果在计算机中找到这个COM对象的话就将其加载;
4、如果没有找到的话就尝试从codebase指定的路径去下载安装。
当然这其中会涉及到安全性的问题,这个在后面会有遇到,因此就放在后面一起叙述了。
知道这个原理之后,下面就开始进入到开发阶段(这个是测试的代码,是用于分享ActiveX开发新的的,在项目中实际运行的不是这段代码):
1、在VS中新建一个VS类库;
2、右击应用程序——属性——应用程序——程序集信息,然后选中“使程序集COM可见”,如下图所示:
3、点击生成,然后选中”为COM互操作注册“,如下图所示:
注:这两步的目的是使我们的应用程序能够作为COM组件调用
4、新建一个IObjectSafety接口,在接口中输入以下代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.InteropServices; 6 7 namespace MyActiveX 8 { 9 //这个Guid是IObjectSafety接口的GUID,因为C#中没有直接实现IObjectsafety接口,因此要声明调用IObjectSafety接口 10 //InterfaceType声明COM接口的方式,IObjectSafety派生自IUnknown 11 [ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")] 12 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 13 public interface IObjectSafety 14 { 15 [PreserveSig] 16 int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions); 17 18 [PreserveSig()] 19 int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions); 20 } 21 }
5、新建一个HelloWord类,并实现IObjectSafety接口
至于为何要实现IObjectSafety接口和如何实现,这个可以参考这三篇篇文章:
1、http://msdn.microsoft.com/EN-US/library/aa751977.aspx
2、http://msdn.microsoft.com/EN-US/library/aa768181.aspx
3、http://msdn.microsoft.com/en-us/library/aa768225(v=vs.85).aspx
简单的解释一下:IE在碰到ActiveX组件的时候,首先要调用IObjectSafety接口,如果返回是S_OK,那么浏览器就认为这个ActiveX是安全的,否则,浏览器会给用户返回一个危险的提示。
代码如下所示:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 using System.Runtime.InteropServices; 7 8 namespace MyActiveX 9 { 10 [ProgId("MyTestActiveX")]//控件名称 11 [Guid("6E591306-0986-4C00-AE3E-E7E03371A41C")]//控件的GUID,用于COM注册和HTML中Object对象classid引用 12 public class HelloWord:IObjectSafety 13 { 14 public string SayHello() 15 { 16 return "Hello Word"; 17 } 18 #region IObjectSafety 成员 19 public void GetInterfacceSafyOptions(int riid, out int pdwSupportedOptions, out int pdwEnabledOptions) 20 { 21 pdwSupportedOptions = 1; 22 pdwEnabledOptions = 2; 23 } 24 public void SetInterfaceSafetyOptions(int riid, int dwOptionsSetMask, int dwEnabledOptions) 25 { 26 throw new NotImplementedException(); 27 } 28 #endregion 29 } 30 }
5、新建一个Windows安装程序程序,将MyActiveX打包成安装程序;
6、新建一个Web项目,添加一个HTML页,在网页中添加对ActiveX控件的引用,代码如下所示:
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <title></title> 5 <script type="text/javascript"> 6 //插件安装成功之后,就可以使用这种方式进行检测 7 function test() { 8 try { 9 var test = new ActiveXObject("MyTestActiveX"); 10 if (test == undefined) 11 { return false; } 12 else 13 { return true; } 14 } 15 catch (e) { 16 return false; 17 } 18 } 19 </script> 20 </head> 21 <body onload="test();"> 22 <object classid="CLSID:6E591306-0986-4C00-AE3E-E7E03371A41C" id="abc" width="0" height="0"></object> 23 <input type="button" value="测试" onclick="javascript:alert(abc.SayHello());" /><!--可是使用这种方式检测插件是否能够成功的调用--> 24 </body> 25 </html>
7、在IIS中发布网页;
8、在计算机中安装一下打包的安装程序;
这样,就可以在IE中使用刚刚开发的ActiveX控件了,但是在实际发布的时候,IE还会弹出不安全的警告提示,可以采取购买证书的方式解决,但是由于我们的项目使用的并不广泛,因此这种方式不合算,因此我们就采取了一种折衷的方式来解决,那就是在生成安装程序的时候,将我们的站点写进IE的可信任站点中,这样IE在安装我们的插件之后,就不会在弹出不安全的提示,只会在插件第一次运行的时候询问一下你是否允许插件运行。
添加IE可信任站点的方式为在注册表的HKEY_CURRENT_USER\Microsoft\Windows\CurrentVersion\Internet Setting\ZoneMap\Domains下面添加上一个站点比如test.com,然后新建一个http的DWORD值,将其Value设置为2就可以了。
Demo可以从这里下载:C#开发类ActiveX控件Demo