如何使用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

posted @ 2012-12-08 23:08  iamshf  阅读(1607)  评论(4编辑  收藏  举报