用C#编写ActiveX控件的基础练习--经过测试的完整代码

前言

  activex控件以前也叫做ole控件或ocx控件,它是一些软件组件或对象,可以将其插入到web网页或其它应用程序中。使用activex插件,可以轻松方便的在 web页中插入多媒体效果、交互式对象以及复杂程序等等。

  通常使用c++或vb开发activex控件,本文探讨一下在visual studio 2005环境中使用c#开发activex控件的技术实现。

1. 问题场景

  在c/s架构的系统中,客户端要实现某些业务功能,可以通过安装相关的应用程序集来方便的实现。同样的需求,在b/s架构的系统里实现起来却比较困难。因为所有的程序都放在服务器端,客户端只是采用浏览器,通过http协议来访问服务器端。比较成熟的解决办法是开发activex控件安装到客户端,这样客户端的浏览器就可以访问本地的activex控件来执行相关的本地操作。本文将要谈论的,就是使用c#开发一个activex控件实现读取并显示客户端的系统时间。

2. 开发环境

  windows xp

  visual studio 2005

  .net framework 2.0(c#)

3. 实现过程

3.1.activex控件开发

  在visual studio 2005开发环境中,可以使用windows控件库项目实现activex控件的开发,但是需要对项目做一些必要的设置。下面就来看看如何使用 windows控件库项目开发一个activex控件。首先创建一个应用程序解决方案,并添加一个windows控件库项目:

        更改“项目属性-应用程序-程序集信息”设置,勾选“使程序集 com 可见”:

用C#编写ActiveX控件的基础练习--经过测试的完整代码_第1张图片

       更改“项目属性-生成”设置,勾选“为 com interop 注册”(注意,此处如果实在debug状态下修改的,那在调到release状态下还需要再设置一次):
 
修改assemblyinfo.cs文件,添加[assembly: allowpartiallytrustedcal lers()]项(需要引用system.security名称空间): 

using system.reflection;  
using system.runtime.compilerservices;  
using system.runtime.interopservices;  
using system.security;  
 
[assembly: assemblytitle("yilin.preresearch.csharpactivex")]  
[assembly: assemblydescription("")]  
[assembly: assemblyconfiguration("")]  
[assembly: assemblycompany("10bar")]  
[assembly: assemblyproduct("yilin.preresearch.csharpactivex")]  
[assembly: assemblycopyright("copyright ? 10bar 2009")]  
[assembly: assemblytrademark("")]  
[assembly: assemblyculture("")]  
[assembly: allowpartiallytrustedcallers()]  
[assembly: comvisible(true)]  
[assembly: guid("114d1f0c-43b8-40ac-ae7c-5adccc19aef3")]  
[assembly: assemblyversion("1.0.0.0")]  
[assembly: assemblyfileversion("1.0.0.0")]

右击面板右侧的项目名称“testActiveX”,选择“添加”一个windows用户控件:

用C#编写ActiveX控件的基础练习--经过测试的完整代码_第2张图片

按照开发windows用户控件一样的思路完成该控件的开发,本例中主要实现了两个业务功能,一个是提供一个公共方法,用于读取usbkey中保存的签名证书,保存到本地c盘根目录下,并返回操作信息;另一个业务功能提供ui界面,包括一个button控件和一个label控件,button控件的 click事件调用前面提供的那个方法,并将返回信息显示到label控件上。这样做可以达到两个目的,其一,activex控件提供公共方法供B/S程序直接调用,从后实现业务功能;其二,activex控件可以提供B/S程序ui界面,通过响应B/S程序中对ui的操作事件实现业务功能。

  完成控件开发后,为了使该用户控件作为一个activex控件进行使用,还需要做以下修改:

  首先,为控件类添加guid,这个编号将用于B/S系统的客户端调用时使用(可以使用 工具-创建guid 菜单创建一个guid): 

guid("4a44cf4e-f859-4328-aa22-3e9d7afff1ab")]  
public partial class hello : usercontrol  
{  

  其次,为了让activex控件获得客户端的信任,控件类还需要实现一个名为“iobjectsafety”的接口。先创建该接口(注意,不能修改该接口的guid值): 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices; 

    [ComImport, GuidAttribute("67E30C07-CF4B-49c0-B835-ED2125A6EC2E")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface iobjectsafety    

    {
        [PreserveSig]
        int getinterfacesafetyoptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int PdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int PdwEnabledOptions);

        [PreserveSig()]
        int setinterfacesafetyoptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int DwOptionSetmask, [MarshalAs(UnmanagedType.U4)] int DwEnabledOptions);
    } 

  然后在控件类中继承并实现该接口: 

        #region iobjectsafety 成员

        private const string _iid_idispatch = "{00020400-0000-0000-c000-000000000046}";
        private const string _iid_idispatchex = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
        private const string _iid_ipersiststorage = "{0000010a-0000-0000-c000-000000000046}";
        private const string _iid_ipersiststream = "{00000109-0000-0000-c000-000000000046}";
        private const string _iid_ipersistpropertybag = "{37d84f60-42cb-11ce-8135-00aa004bb851}";

        private const int interfacesafe_for_untrusted_caller = 0x00000001;
        private const int interfacesafe_for_untrusted_data = 0x00000002;
        private const int s_ok = 0;
        private const int e_fail = unchecked((int)0x80004005);
        private const int e_nointerface = unchecked((int)0x80004002);

        private bool _fsafeforscripting = true;
        private bool _fsafeforinitializing = true;

        public int getinterfacesafetyoptions(ref Guid riid, ref int pdwsupportedoptions, ref int pdwenabledoptions)
        {
            int rslt = e_fail;

            string strguid = riid.ToString("b");
            pdwsupportedoptions = interfacesafe_for_untrusted_caller | interfacesafe_for_untrusted_data;
            switch (strguid)
            {
                case _iid_idispatch:
                case _iid_idispatchex:
                    rslt = s_ok;
                    pdwenabledoptions = 0;
                    if (_fsafeforscripting == true)
                        pdwenabledoptions = interfacesafe_for_untrusted_caller;
                    break;
                case _iid_ipersiststorage:
                case _iid_ipersiststream:
                case _iid_ipersistpropertybag:
                    rslt = s_ok;
                    pdwenabledoptions = 0;
                    if (_fsafeforinitializing == true)
                        pdwenabledoptions = interfacesafe_for_untrusted_data;
                    break;
                default:
                    rslt = e_nointerface;
                    break;
            }

            return rslt;
        }

        public int setinterfacesafetyoptions(ref Guid riid, int dwoptionsetmask, int dwenabledoptions)
        {
            int rslt = e_fail;
            string strguid = riid.ToString("b");
            switch (strguid)
            {
                case _iid_idispatch:
                case _iid_idispatchex:
                  if (((dwenabledoptions & dwoptionsetmask) == interfacesafe_for_untrusted_caller) && (_fsafeforscripting == true))
                        rslt = s_ok;
                    break;
                case _iid_ipersiststorage:
                case _iid_ipersiststream:
                case _iid_ipersistpropertybag:
                  if (((dwenabledoptions & dwoptionsetmask) == interfacesafe_for_untrusted_data) && (_fsafeforinitializing == true))
                        rslt = s_ok;
                    break;
                default:
                    rslt = e_nointerface;
                    break;
            }

            return rslt;
        }

        #endregion

  这样,一个activex控件就开发完成了。

3.2.Activex控件部署

  activex控件可以使用visual studio 2005的安装项目进行部署。这与普通的windows form应用程序的部署几乎一样,只有一个地方需要注意,将前面创建的用户控件项目作为主输出项目,并设置其register属性为vsdrpcom,如下图所示:

 

3.3.测试

  建立一个web应用程序项目,在测试页面的html代码中添加对activex控件的引用,并且可以通过javascript调用控件的公共成员(注意这里clsid后面的值即为前面为用户控件类设置的guid): 

<object id="csharpactivex" classid="clsid:e5e0446c-8680-4444-9fc2-f837bc617ed9"></object> 
<input type="button" onclick="alert(csharpactivex.sayhello());" value="显示当前时间" /> 

  将该web应用程序项目发布到iis。另外找一台电脑作为客户端测试环境,确保它与服务器端网络连通,安装.net framework 2.0和该activex控件。安装完成后,就可以用浏览器访问服务器,进行测试了(你也可以在开发环境的系统中安装该activex控件,并直接在vs 2005中运行webapp项目查看结果):

 
4. 总结

  综上所述,在visual studio 2005环境中使用c#开发activex控件,技术实现上没有什么难度,唯一的问题就是客户端需要安装.net framework。鉴于activex控件一般都是实现一些简单单一的功能,.net framework 2.0已经完全可以应付,所以建议在.net framework 2.0下开发。因为相对于.net framework 3.5两百多兆的安装包,.net framework 2.0安装包只有20多兆,用户相对容易接受一些。

5. 出现如下错误怎么解决?

 用C#编写ActiveX控件的基础练习--经过测试的完整代码_第3张图片

    经在网上查阅,该问题是visual studio 2005的一个bug,并不是每次都发生。我的解决办法是从visual studio 2008的安装目录里拷贝regcap.exe覆盖visual studio 2005的对应文件,文件目录一般为“~\microsoft visual studio 8\common7\tools\deployment\regcap.exe”。压缩包中提供了该文件的visual studio 2008版本。
 
 
 
文章来源: http://blog.sina.com.cn/s/blog_6ff1afa90100qpb1.html

你可能感兴趣的:(windows,.net,String,C#,测试,assembly)