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控件库项目:
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用户控件:
按照开发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,如下图所示:
建立一个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项目查看结果):
综上所述,在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. 出现如下错误怎么解决?