使用.NET 应用程序的Classic COM 组件
输入IAirlineArrivalPager接口的AddArrivalDetails方法的执行情况。
.......这个方法的执行情况使用了Fire_OnAirlineArrivedEvent帮助程序方法,以此来通报所有接受对象执行IAirlineArrivalPagerEvents,IAirlineArrivalPagerEvents早已预定了事件通知通知。Fire_OnAirlineArrivedEvent是一个函数代理类型方法,通过Implement Connection point wizard,自动地从IConnectionPointImpl派生而来。本质上,当IConnectionPoint::Advise被调用时,它通过映射迭代,这个映射把接口指针存储到接受对象,使用这些接口指针来调用事件通知方法(OnAirlineArrivedEvent),此事件通知方法由客户端接受对象执行。
STDMETHODIMP CAirlineArrivalPager::AddArrivalDetails(
BSTR bstrAirlineName,BSTR bstrArrivalTerminal)
{
Fire_OnAirlineArrivedEvent(bstrAirlineName,bstrArrivalTerminal);
// 返回状态
return S_OK;
}
.......
如果你是一个C++程序师,编译COM感知客户端应用程序来接受通知,你将在客户端应用程序中建立一个接受对象,此客户端应用程序执行IAirlineArrivalPagerEvents接口。然后创建AirlineArrivalPager对象,通过调用,将接受对象的IUnknown接口指针传递到IConnectionPoint::Advise,或者使用一个帮助程序方法,例如:AtlAdvise,把你的接受用线路连接到引起事件的对象,因此你可以接受事件通知。使用VB 6.0,就像在你的宣告中使用WithEvents和为接受通知定义处理程序一样简单。VB将处理简单外表下面所有困难的工作,把由输出接口制造的通知连接到正确的处理程序功能上。
使用委托处理事件
如果对.NET中怎样使用委托很熟悉了,可以跨过这一章进入下一章。.NET中的事件处理基本上是建立在委托事件模式基础上的。委托与C/C++中使用的功能指针相似。基于委托的事件模式,因其使用的简单性而得到普及,正如WFC(Visual J++中的微软基础类库)时代一样。委托允许任何组件引起的事件被联系到处理程序功能或者任何其它组件的方法上,只要函数功能或者方法的函式标记式与委托的函式标记式完全匹配。下面的简单例子,可以了解委托是如何运行。
// 下面是 SayGoodMorning的委托
delegate string SayGoodMorning();
public class HelloWorld
{
public string SpeakEnglish() {
return "Good Morning";
}
public string SpeakFrench() {
return "Bonjour";
}
public static void Main(String[] args) {
HelloWorld obj = new HelloWorld();
// 将委托与方法相关联
SayGoodMorning english = new SayGoodMorning(obj.SpeakEnglish);
SayGoodMorning french = new SayGoodMorning(obj.SpeakFrench);
// 引用委托
System.Console.WriteLine(english());
System.Console.WriteLine(french());
}
}/* 结束class类 */
这是你得到的输出情况:
Good Morning在上面的例子宣告了一个SayGoodMorning委托。然后把委托用线路连接起来,以此来引用HelloWorld对象的SpeakEnglish 和SpeakFrench方法。所有的要求就是SpeakEnglish 和SpeakFrench和SayGoodMorning委托有同样的标记式。通过示例委托,可以完成引用,就好像它是一个对象,并作为参数在引用方法中传递。这个引用方法既可以是一个实例方法或这是一个类型的静态方法。这使得委托被称为一流的以对象为目标的成员,而且它们类型安全,处理起来也很安全。.NET事件处理模式主要建立在委托事件模式基础之上,让我们看一下下面的例子。
Bonjour
......当得应用程序在处理控件时,想要重新收到特定的通知,它建立一个事件处理程序委托得新实例,此事件处理程序委托包含有一个实际处理程序功能引用,而此实际处理程序功能将处理由控件引起得事件。在上面得例子中,事件处理程序委托包含了一个AngryButton_Click方法引用。AngryButton_Click方法需要拥有和事件处理程序委托一样的方法标记式.。下面就是System.EventHandler 委托的样子:
// 创建一个按钮
private System.Windows.Forms.Button AngryButton = new Button();
....
// 在按钮的单击事件列表上增加一个委托
AngryButton.Click += new System.EventHandler(AngryButton_Click);
.....
// 委托的句柄函数
protected void AngryButton_Click(object sender,EventArgs e)
{
MessageBox.Show("Please Stop clicking me !!");
}
.......
public delegate void EventHandler(object sender, EventArgs e);然后,事件处理程序委托实例必须被补充到委托实例的Click事件表中。通过使用C#操作器,例如:+= and -=,它们都是委托.Combine and 委托.Remove方法的包装器,扩展System.Multicast委托的委托,允许你将多个处理程序功能增添到委托调用表中。使用一个事件给用户提供了一个十分安全的方法,仅仅只需要通过使用+= and -=操作器,就能把委托实例移动到事件,不会意外地多写调用表。当控件引起一个事件,每一个被补充到按钮Click事件的委托都会被调用,而且委托会把它发送到它所引用的正确的处理程序功能上。
在例子中,无论什么时候,按钮中的Click 事件发生后,这个调用将会被发送到AngryButton_Click方法。我猜想这个将会为.NET结构中事件处理机制的委托和事件扮演的角色这一方面提供一个很好的方案。我要解释委托是怎样工作的原因就是——它是.NET事件处理模式内其中一个主要的推动力量,而且理解并欣赏.NET应用程序运用委托怎样从Classic COM 组件预定事件通知这一点非常重要。
.NET应用程序中的接受非托管事件

这里有一个简单的VB客户端 应用程序,此VB客户端 应用程序担任John Doe International Airport上控制台的角色,调用输入接口里的AddArrivalDetails方法。反之,执行这个方法能引起事件通知,而这些事件通知是后来被.NET 应用程序(此.NET应用程序早已预定了OnAirlineArrivedEvent事件通知)里的处理程序功能占有。AirlineArrivalPager COM对象它自己是一个被安装在out-of-proc COM 服务器里的单件对象,因此,对象服务的同一个实例——基于VB的控制台应用程序(引起事件)和.NET寻呼机应用程序都预定了OnAirlineArrivedEvent事件通知。
Dim AirlinePager As New AIRLINENOTIFYLib.AirlineArrivalPager接下来看一看.NET 托管应用程序是怎样接受事件通知,这些事件通知是由AirlineArrivalPager COM对象生成的。首先需要从COM对象类型库生成一个.NET元数据代理,以致于它能被.NET应用程序使用。让我们使用类型库导入程序 (TLBIMP)生成元数据代理程序集。
Private Sub AirlineArrived_Click()
AirlinePager.AddArrivalDetails Me.AirlineName, Me.ArrivalTerminal
End Sub
tlbimp AirlineNotify.tlb /out:AirlineNotifyMetadata.dll我们将在.NET应用程序中引用这个元数据代理。这里有一个简单的.NET Windows Forms应用程序,通过使用委托,.NET Windows Forms应用程序从AirlineArrivalPager COM组件预定了事件通知。

Collapse
......
using AirlineNotifyMetadata;
public class AirlineNotifyForm : System.WinForms.Form
{
private System.Windows.Forms.CheckBox checkBoxPaging;
private System.Windows.Forms.ListBox listPager;
private AirlineArrivalPager m_pager = null;
......
public AirlineNotifyForm() {
.....
// 从AirlineArr ivalPagercomponent 订阅事件通知
subscribePaging();
}
......
void subscribePaging() {
// 创建 AirlineArrivalPager对象
m_pager = new AirlineArrivalPager();
m_pager.OnAirlineArrivedEvent +=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(
OnMyPagerNotify);
}/*结束subscribePaging */
protected void checkBoxPaging_CheckedChanged (object sender,
System.EventArgs e) {
if(checkBoxPaging.Checked) {
m_pager.OnAirlineArrivedEvent +=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(
OnMyPagerNotify);
}
else {
m_pager.OnAirlineArrivedEvent -= new
_IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(
OnMyPagerNotify);
}
public int OnMyPagerNotify(String strAirline, String strTerminal) {
StringBuilder strDetails = new StringBuilder("Airline ");
strDetails.Append(strAirline);
strDetails.Append(" has arrived in ");
strDetails.Append(strTerminal);
listPager.Items.Insert(0,strDetails);
return 0;
}/* 结束 OnMyPagerNotify */
}/* 结束 class类 */
下面是最重要的代码行:
m_pager.OnAirlineArrivedEvent += new如果了解委托工作的语法就能完全能理解这里正在进行什么。你现在所作的就是增添IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler委托实例,此IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler委托实例将OnMyPagerNotify方法引用到OnAirlineArrivedEvent 事件表。通常事件OnAirlineArrivedEvent的名称和输出接口里的方法名称是一样的。委托名称(_IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler)通常遵循InterfaceName_EventNameEventHandler模式。这里所完成的就是从COM组件接受事件通知。你必须要做的就是创建组件实例,然后添加一个委托,把处理程序函数引用到事件表中。你在这里所做的与COM世界里IConnectionPoint::Advise所做的相类似。无论什么时候,当COM组件引起OnAirlineArrivedEvent事件,将会调用OnMyPagerNotify方法来处理事件通知。在.NET中,用线路连接处理程序接受,从引起事件的COM对象中接受事件通知非常简单。
_IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(
OnMyPagerNotify);

Classic COM 中的连接点事件处理机制是怎样映射到.NET中的委托事件处理机制。当不想再接受通知时可以通过调用从事件表中将委托移出来。
m_pager.OnAirlineArrivedEvent -= new这个与IConnectionPoint::Unadvise方法调用相似。在Advise 调用中接受到的cookie,将接受对象接口指针从机器中移出来,IConnectionPoint::Unadvise方法调用就能撤回进一步的通知。但是,Classic COM中的连接点事件处理模式和.NET中的委托事件模式之间的映射由谁来处理?由类型库导入程序(TLBIMP)生成的元数据代理包含类型,这些元数据代理包含类型执行适配器的工作,通过运行库可调用的包装存根(在运行时创建的),把非托管世界里的连接点事件处理模式和.NET世界里的基于委托事件模式用线路连接起来。如果对简单外表下面发生的事情很感兴趣,我期望你使用IL拆装器(ILDASM)来打开元数据代理(AirlineNotifyMetadata.dll),在帮助程序类型中为不同的方法检查MSIL代码。
_IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);
0
相关文章