您的位置:首页 > WinForm >

C#构建带事件的签名ActiveX组件

2012-04-28 21:33 来源:未知编辑:admin点击:

使用C#构建带事件的签名ActiveX组件具体办法:

   第一步 创建ActiveX项目

  使用.NET语言编写的ActiveX控件的主体就是一个类库,首先我们来创建这个类库项目。打开Visual Studio 2008,File->New->Project,选择Class Library,创建一个类库项目。

  创建ActiveX项目

  第二步 编写ActiveX主体

  ActiveX的主体包括方法定义接口、事件定义接口(可选)、实现这些接口的ActiveX主体类三个部分。下面是笔者原创的Demo。

  首先,我们创建方法定义接口:

  ///<summary>

  /// 该接口定义了ActiveX的方法

  ///</summary>

  [

  Guid("F3BD342F-14E1-4347-BFBD-F449DD070DF9"),

  InterfaceType(ComInterfaceType.InterfaceIsDual),

  ComVisible(true)]

  publicinterfaceIBosnMaActiveX

  {

  [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)]

  publicinterfaceBosnMaActiveXEvents

  {

  [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)

  ]

  publicclassBosnAcX : IBosnMaActiveX, IObjectSafety

  {

  #region Events, Handlers, Instances

  publicdelegatevoidVolumeChangedHandler(int value);

  publicdelegatevoidSimpleHandler();

  publiceventVolumeChangedHandler OnRecorderVolumeChanged;

  publiceventSimpleHandler OnRecorderStarted;

  publiceventSimpleHandler OnRecorderStopped;

  #endregion

  #region Implementation of IBosnMaActiveX

  ///<summary>

  /// 调用该方法将引发OnRecorderStarted事件,并在3秒后引发OnRecorderVolumeChanged

  ///</summary>

  publicvoid Start()

  {

  OnRecorderStarted();

  SimpleHandler d = Work;

  d.BeginInvoke(null, null);

  }

  publicvoid Work()

  {

  Thread.Sleep(3000);

  OnRecorderVolumeChanged(53);

  }

  ///<summary>

  /// 调用该方法将引发OnRecorderStopped事件

  ///</summary>

  publicvoid 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>

  <headrunat="server">

  <title></title>

  <objectid="myAcX"name="myAcX"classid="clsid:78E683CE-EC77-40b0-B0C3-4060FFC70A93">

  </object>

  <scriptlanguage="javascript"for="myAcX"type="text/javascript"event="OnRecorderVolumeChanged(v);">

  MyDiv.innerHTML = 'In javascript: Get Volume:'+v;

  </script>

  <scriptlanguage="javascript"for="myAcX"type="text/javascript"event="OnRecorderStarted">

  MyDiv.innerHTML = 'In javascript: OnRecorderStarted';

  </script>

  <scriptlanguage="javascript"for="myAcX"type="text/javascript"event="OnRecorderStopped">

  MyDiv.innerHTML = 'In javascript: OnRecorderStopped';

  </script>

  </head>
<body>

  <form>

 

  <scriptlanguage="javascript"type="text/jscript">

  function Button1_onclick() {

  myAcX.Start();

  }

  function Button2_onclick() {

  myAcX.Stop();

  }

  function RecorderVolumeChanged(v) {

  alert('volume:' + v);

  }

  </script>

  <divid="MyDiv">Nothing happened</div>

  <p>

  <inputid="Button1"type="button"value="Start"onclick="Button1_onclick()"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

  <inputid="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)

  ]

  publicenumObjectSafetyOptions

  {

  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)

  ]

  publicinterfaceIObjectSafety

  {

  [PreserveSig]

  long GetInterfaceSafetyOptions(refGuid iid, outint pdwSupportedOptions, outint pdwEnabledOptions);

  [PreserveSig]

  long SetInterfaceSafetyOptions(refGuid 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

  //

  publicclassIObjectSafetyImpl : IObjectSafety

  {

  privateObjectSafetyOptions m_options =

  ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER |

  ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;

  #region [IObjectSafety implementation]

  publiclong GetInterfaceSafetyOptions(refGuid iid, outint pdwSupportedOptions, outint pdwEnabledOptions)

  {

  pdwSupportedOptions = (int)m_options;

  pdwEnabledOptions = (int)m_options;

  return 0;

  }

  publiclong SetInterfaceSafetyOptions(refGuid iid, int dwOptionSetMask, int dwEnabledOptions)

  {

  return 0;

  }

  #endregion

  };

  }

  并实现以下两个方法:

  #region Implementation of IObjectSafety

  privateObjectSafetyOptions m_options =

  ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER |

  ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;

  publiclong GetInterfaceSafetyOptions(refGuid iid, outint pdwSupportedOptions, outint pdwEnabledOptions)

  {

  pdwSupportedOptions = (int)m_options;

  pdwEnabledOptions = (int)m_options;

  return 0;

  }

  publiclong SetInterfaceSafetyOptions(refGuid iid, int dwOptionSetMask, int dwEnabledOptions)

  {

  return 0;

  }

  #endregion

  使用C#构建带事件的签名ActiveX组件具体办法:

   第一步 创建ActiveX项目

  使用.NET语言编写的ActiveX控件的主体就是一个类库,首先我们来创建这个类库项目。打开Visual Studio 2008,File->New->Project,选择Class Library,创建一个类库项目。

  创建ActiveX项目

  第二步 编写ActiveX主体

  ActiveX的主体包括方法定义接口、事件定义接口(可选)、实现这些接口的ActiveX主体类三个部分。下面是笔者原创的Demo。

  首先,我们创建方法定义接口:

  ///<summary>

  /// 该接口定义了ActiveX的方法

  ///</summary>

  [

  Guid("F3BD342F-14E1-4347-BFBD-F449DD070DF9"),

  InterfaceType(ComInterfaceType.InterfaceIsDual),

  ComVisible(true)]

  publicinterfaceIBosnMaActiveX

  {

  [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)]

  publicinterfaceBosnMaActiveXEvents

  {

  [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)

  ]

  publicclassBosnAcX : IBosnMaActiveX, IObjectSafety

  {

  #region Events, Handlers, Instances

  publicdelegatevoidVolumeChangedHandler(int value);

  publicdelegatevoidSimpleHandler();

  publiceventVolumeChangedHandler OnRecorderVolumeChanged;

  publiceventSimpleHandler OnRecorderStarted;

  publiceventSimpleHandler OnRecorderStopped;

  #endregion

  #region Implementation of IBosnMaActiveX

  ///<summary>

  /// 调用该方法将引发OnRecorderStarted事件,并在3秒后引发OnRecorderVolumeChanged

  ///</summary>

  publicvoid Start()

  {

  OnRecorderStarted();

  SimpleHandler d = Work;

  d.BeginInvoke(null, null);

  }

  publicvoid Work()

  {

  Thread.Sleep(3000);

  OnRecorderVolumeChanged(53);

  }

  ///<summary>

  /// 调用该方法将引发OnRecorderStopped事件

  ///</summary>

  publicvoid 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>

  <headrunat="server">

  <title></title>

  <objectid="myAcX"name="myAcX"classid="clsid:78E683CE-EC77-40b0-B0C3-4060FFC70A93">