使用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.NETFrameworkv2.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()"/>
<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