使用C#构建带事件的签名ActiveX组件(一)

 

 
 
                最近在忙微软Imagine Cup,需要在WEB中实现用户说话的自动检测,语音数据的传输,语音播放等功能,而浏览器出于安全性考虑实现上述功能非常困难,因此想到了 ActiveX,而ActiveX基于COM技术使用C#、VB.NET等语言也可以编写,不过笔者发现国内对C#编写ActiveX的文章并不是很多,因此写下此文希望能对想使用.NET语言编写ActiveX的人有所帮助。
关键词
                ActiveX   .NET   签名   Javascript   事件
涉及内容
·         使用C#编写ActiveX
·         为该ActiveX添加事件并在Javascript中注册
·         为该ActiveX制作安装包
·         为ActiveX安装包签名
开始
第一步 创建ActiveX项目
                使用.NET语言编写的ActiveX控件的主体就是一个类库,首先我们来创建这个类库项目。打开Visual Studio 2008,File->New->Project,选择Class Library,创建一个类库项目。
图1 创建ActiveX项目
第二步 编写ActiveX主体
ActiveX的主体包括方法定义接口、事件定义接口(可选)、实现这些接口的ActiveX主体类三个部分。下面是笔者原创的Demo。
首先,我们创建方法定义接口:
    /// <summary>
    /// 该接口定义了ActiveX的方法
    /// </summary>
    [
    Guid ( "F3BD342F-14E1-4347-BFBD-F449DD070DF9" ),
    InterfaceType ( ComInterfaceType .InterfaceIsDual),
    ComVisible ( true )]
    public interface IBosnMaActiveX
    {
        [ DispId (1)]
        void Start();
        [ DispId (2)]
        void Stop();
}
该接口内的成员会暴露给外部调用。这里我们提供两个简单方法 Start和Stop。使用工具(Visual Studio -> Tools -> Create GUID)生成自己的GUID。
 
接下来定义事件接口 :
    /// <summary>
    /// 该接口定义了ActiveX的事件
    /// </summary>
    [ ComVisible ( true )]
    [ Guid ( "C4F9F24F-B860-4e79-945D-B9A281950C82" )]
    [ InterfaceType ( ComInterfaceType .InterfaceIsIDispatch)]
    public interface BosnMaActiveXEvents
    {
        [ DispId (21)]
        void OnRecorderStarted();
        [ DispId (22)]
        void OnRecorderStopped();
        [ DispId (23)]
        void OnRecorderVolumeChanged( int value);
}
 
这里我们为 ActiveX定义三个事件,分别为 OnRecorderStarted OnRecorderStopped OnRecorderVolumeChanged (带一个int参数)。
 
最后我们编写集成方法接口和事件接口的 ActiveX主体类:
 
    [
       Guid ( "78E683CE-EC77-40b0-B0C3-4060FFC70A93" ),
       ProgId ( "ActiveXOfBosnMa.BosnMaActiveX" ),
       ClassInterface ( ClassInterfaceType .None),
       ComDefaultInterface ( typeof ( IBosnMaActiveX )),
       ComSourceInterfaces ( typeof ( BosnMaActiveXEvents )),
       ComVisible ( true )
   ]
    public class BosnAcX : IBosnMaActiveX , IObjectSafety
    {
        #region Events, Handlers, Instances
        public delegate void VolumeChangedHandler ( int value);
        public delegate void SimpleHandler ();
 
        public event VolumeChangedHandler OnRecorderVolumeChanged;
        public event SimpleHandler OnRecorderStarted;
        public event SimpleHandler OnRecorderStopped;
        #endregion
 
        #region Implementation of IBosnMaActiveX
 
        /// <summary>
        /// 调用该方法将引发OnRecorderStarted事件,并在3秒后引发OnRecorderVolumeChanged
        /// </summary>
        public void Start()
        {
            OnRecorderStarted();
            SimpleHandler d = Work;
            d.BeginInvoke( null , null );
        }
 
 
        public void Work()
        {
            Thread .Sleep(3000);
            OnRecorderVolumeChanged(53);
        }
 
 
        /// <summary>
        ///  调用该方法将引发OnRecorderStopped事件
        /// </summary>
        public void Stop()
        {
            OnRecorderStopped();
        }
 
        #endregion
    }
 
    这里要注意主体类的事件名称要与事件接口(在上例中为 BosnMaActiveXEvents )中的方法名相同。 BosnAcX 中我们实现了 IBosnMaActiveX 中的方法,当调用 Start()时引发一个OnRecorderStarted事件,并在3秒后引发一个OnRecorderVolumeChanged事件,在调用Stop()时引发一个OnRecorderStopped事件。
 
编译并注册ActiveX
编译整个项目将输出dll。
 
图2 编译ActiveX项目生成dll文件
然后我们启动命令行CMD(如果是Vista/Win7使用管理员方式打开),使用以下命令注册控件.
C:/>D:                       //转到.dll所在目录,笔者为了方便将.dll copy到了D盘根目录
D:/>regasm activexofbosnma.dll /codebase /tlb
*regasm 命令在 %systemroot%/Microsoft.NET/Framework/v2.x.xxxx/ 目录下,将该目录注册到用户环境变量中即可不使用完全限定名运行该命令。
* 使用 regasm activexofbosnma.dll /codebase /tlb /unregister 可以反注册 , ActiveX 代码变更时重编译后,需要先反注册再注册。
3 注册和反注册 ActiveX 控件
测试ActiveX
最后我们创建一个html页面来测试该ActiveX.
< html >
< head runat ="server">
    < title ></ title >
    < object id ="myAcX" name ="myAcX" classid ="clsid:78E683CE-EC77-40b0-B0C3-4060FFC70A93">
    </ object >
 
    < script language ="javascript" for ="myAcX" type ="text/javascript" event ="OnRecorderVolumeChanged(v);">
    MyDiv.innerHTML = 'In javascript: Get Volume:' +v;
    </ script >
 
    < script language ="javascript" for ="myAcX" type ="text/javascript" event ="OnRecorderStarted">
    MyDiv.innerHTML = 'In javascript: OnRecorderStarted' ;
    </ script >
 
    < script language ="javascript" for ="myAcX" type ="text/javascript" event ="OnRecorderStopped">
    MyDiv.innerHTML = 'In javascript: OnRecorderStopped' ;
    </ script >
 
</ head >
< body >
    < form >
        < script language ="javascript" type ="text/jscript">
            function Button1_onclick() {
                myAcX.Start();
            }
 
            function Button2_onclick() {
                myAcX.Stop();               
            }
 
            function RecorderVolumeChanged(v) {
                alert( 'volume:' + v);
            }
        </ script >
       
        < div id ="MyDiv"> Nothing happened </ div >
       
            < p >
        < input id ="Button1" type ="button" value ="Start" onclick ="Button1_onclick()" /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        < input id ="Button2" type ="button" value ="Stop" onclick ="Button2_onclick()" /></ p >
    </ form >
 
</ body >
</ html >
测试效果
首先使用IE打开测试页面
允许ActiveX交互后进入主界面,点击Start按钮会收到ActiveX返回的OnRecorderStarted事件。
三秒过后收到Volume事件
 
最后点击Stop按钮会收到OnRecorderStopped事件。


 
安全性
                为了标记ActiveX控件为安全的(避免弹出“该控件是不安全的”警告),需要实现IObjectSafety接口。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Text;
 
namespace ActiveXOfBosnMa
{
    [
        Serializable ,
        ComVisible ( true )
    ]
    public enum ObjectSafetyOptions
    {
        INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
        INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
        INTERFACE_USES_DISPEX = 0x00000004,
        INTERFACE_USES_SECURITY_MANAGER = 0x00000008
    };
 
    //
    // MS IObjectSafety Interface definition
    //
    [
        ComImport (),
        Guid ( "CB5BDC81-93C1-11CF-8F20-00805F2CD064" ),
        InterfaceType ( ComInterfaceType .InterfaceIsIUnknown)
    ]
    public interface IObjectSafety
    {
        [ PreserveSig ]
        long GetInterfaceSafetyOptions( ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions);
 
        [ PreserveSig ]
        long SetInterfaceSafetyOptions( ref Guid iid, int dwOptionSetMask, int dwEnabledOptions);
    };
 
    //
    // Provides a default Implementation for
    // safe scripting.
    // This basically means IE won't complain about the
    // ActiveX object not being safe
    //
    public class IObjectSafetyImpl : IObjectSafety
    {
        private ObjectSafetyOptions m_options =
            ObjectSafetyOptions .INTERFACESAFE_FOR_UNTRUSTED_CALLER |
            ObjectSafetyOptions .INTERFACESAFE_FOR_UNTRUSTED_DATA;
 
        #region [IObjectSafety implementation]
        public long GetInterfaceSafetyOptions( ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions)
        {
            pdwSupportedOptions = ( int )m_options;
            pdwEnabledOptions = ( int )m_options;
            return 0;
        }
 
        public long SetInterfaceSafetyOptions( ref Guid iid, int dwOptionSetMask, int dwEnabledOptions)
        {
            return 0;
        }
        #endregion
    };
}
 
 
并实现以下两个方法:
 
        #region Implementation of IObjectSafety
 
        private ObjectSafetyOptions m_options =
             ObjectSafetyOptions .INTERFACESAFE_FOR_UNTRUSTED_CALLER |
             ObjectSafetyOptions .INTERFACESAFE_FOR_UNTRUSTED_DATA;
 
 
        public long GetInterfaceSafetyOptions( ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions)
        {
            pdwSupportedOptions = ( int )m_options;
            pdwEnabledOptions = ( int )m_options;
            return 0;
        }
 
        public long SetInterfaceSafetyOptions( ref Guid iid, int dwOptionSetMask, int dwEnabledOptions)
        {
            return 0;
        }
 
 
        #endregion
源码下载
作者:Bosn Ma
E-mail: x@bosnma.com
说明: 本文章可以转载,但请保留原文。本文作者Bosn Ma,来源于 http://www.bosnma.com
如有问题欢迎到以上地址发表,获通过E-Mail与我联系。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值