如何挂接COM事件分派器? [英] How to hook up a COM event dispatcher?

查看:236
本文介绍了如何挂接COM事件分派器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

VBIDE API暴露了奇妙的隐藏的 _dispVBComponentsEvents 接口(其他),其中看起来像可以用来捕获各种有趣的事件在VBE中。

The VBIDE API exposes the wonderfully cryptic _dispVBComponentsEvents interface (among others), which look like something that I could use to capture various interesting events in the VBE.

所以我在类中实现了接口,意图捕获事件并提出一个正常.net事件,以便我的应用程序的其余部分处理,像这样:

So I implemented the interface in a class that intends to capture the event and raise a "normal" .net event for the rest of my application to handle, like this:

public class VBComponentsEventDispatcher : _dispVBComponentsEvents
{
    public event EventHandler<DispatcherEventArgs<VBComponent>> ComponentAdded;
    public void ItemAdded(VBComponent VBComponent)
    {
        OnDispatch(ComponentAdded, VBComponent);
    }

    public event EventHandler<DispatcherEventArgs<VBComponent>> ComponentRemoved;
    public void ItemRemoved(VBComponent VBComponent)
    {
        OnDispatch(ComponentRemoved, VBComponent);
    }

    public event EventHandler<DispatcherRenamedEventArgs<VBComponent>> ComponentRenamed;
    public void ItemRenamed(VBComponent VBComponent, string OldName)
    {
        var handler = ComponentRenamed;
        if (handler != null)
        {
            handler.Invoke(this, new DispatcherRenamedEventArgs<VBComponent>(VBComponent, OldName));
        }
    }

    public event EventHandler<DispatcherEventArgs<VBComponent>> ComponentSelected;
    public void ItemSelected(VBComponent VBComponent)
    {
        OnDispatch(ComponentSelected, VBComponent);
    }

    public event EventHandler<DispatcherEventArgs<VBComponent>> ComponentActivated;
    public void ItemActivated(VBComponent VBComponent)
    {
        OnDispatch(ComponentActivated, VBComponent);
    }

    public event EventHandler<DispatcherEventArgs<VBComponent>> ComponentReloaded;
    public void ItemReloaded(VBComponent VBComponent)
    {
        OnDispatch(ComponentReloaded, VBComponent);
    }

    private void OnDispatch(EventHandler<DispatcherEventArgs<VBComponent>> dispatched, VBComponent component)
    {
        var handler = dispatched;
        if (handler != null)
        {
            handler.Invoke(this, new DispatcherEventArgs<VBComponent>(component));
        }
    }
}

我希望使用类如下:

var componentsEvents = new VBComponentsEventDispatcher();
componentsEvents.ComponentAdded += componentsEvents_ComponentAdded;
componentsEvents.ComponentActivated += componentsEvents_ComponentActivated;
//...





void componentsEvents_ComponentAdded(object sender, DispatcherEventArgs<VBComponent> e)
{
    Debug.WriteLine(string.Format("Component '{0}' was added.", e.Item.Name));
}

void componentsEvents_ComponentActivated(object sender, DispatcherEventArgs<VBComponent> e)
{
    Debug.WriteLine(string.Format("Component '{0}' was activated.", e.Item.Name));
}

但它不工作,我没有调试输出和断点isn打击。显然,我不知道我在做什么。 MSDN在这个问题上是完全没用的,找到关于这个的文档比找到亨利八世的第三个妻子的少女更难。

But it doesn't work, I get no debug output and a breakpoint isn't hit. Clearly I don't know what I'm doing. MSDN is completely useless on the subject, and finding documentation about this is harder than finding the maiden name of the third wife of Henry VIII.

我做错了什么,如何让这个工作?

What am I doing wrong, and how do I get this to work? Am I on the right track?

推荐答案


我在正确的轨道吗?

Am I on the right track?

是的。您在事件接收器中所拥有的 - 您缺少一些代码,可以使用COM服务器注册接收器。

Yes. What you have in an event sink - you're missing a bit of code to register the sink with the COM servers.

VBProjects VBComponents 接口实现了 IConnectionPointContainer 接口 - 您需要使用它来收集 IConnectionPoint 实例。要注销汇,你需要一个数据结构来记住注册步骤给你的 int cookie

The VBProjects and VBComponents interfaces implement (somewhere very deep) the IConnectionPointContainer interface - you need to use that to collect IConnectionPoint instances. And to un-register the sink, you'll need a data structure to remember the int cookie that the registration step gives you.

这是一个粗略的例子 - 假设你有一个 App 类,包含这些字段:

Here's a rough example - say you have an App class with these fields:

private readonly IConnectionPoint _projectsEventsConnectionPoint;
private readonly int _projectsEventsCookie;

private readonly IDictionary<VBComponents, Tuple<IConnectionPoint, int>>  _componentsEventsConnectionPoints = 
    new Dictionary<VBComponents, Tuple<IConnectionPoint, int>>(); 

在构造函数中的某处,您将使用 IConnectionPoint.Advise ,并注册您的自定义事件处理程序:

Somewhere in the constructor, you'll register the sink using IConnectionPoint.Advise, and register your custom event handlers:

var sink = new VBProjectsEventsSink();
var connectionPointContainer = (IConnectionPointContainer)_vbe.VBProjects;
Guid interfaceId = typeof (_dispVBProjectsEvents).GUID;
connectionPointContainer.FindConnectionPoint(ref interfaceId, out _projectsEventsConnectionPoint);

sink.ProjectAdded += sink_ProjectAdded;
sink.ProjectRemoved += sink_ProjectRemoved;
sink.ProjectActivated += sink_ProjectActivated;
sink.ProjectRenamed += sink_ProjectRenamed;

_projectsEventsConnectionPoint.Advise(sink, out _projectsEventsCookie);

然后,当添加项目时,您将使用 IConnectionPoint.Advise ,那么您可以注册自定义事件处理程序,并向您的字典添加一个条目:

Then, when a project is added, you'll register a sink for each component using IConnectionPoint.Advise, then you can register your custom event handlers, and add an entry to your dictionary:

void sink_ProjectAdded(object sender, DispatcherEventArgs<VBProject> e)
{
    var connectionPointContainer = (IConnectionPointContainer)e.Item.VBComponents;
    Guid interfaceId = typeof(_dispVBComponentsEvents).GUID;

    IConnectionPoint connectionPoint;
    connectionPointContainer.FindConnectionPoint(ref interfaceId, out connectionPoint);

    var sink = new VBComponentsEventsSink();
    sink.ComponentActivated += sink_ComponentActivated;
    sink.ComponentAdded += sink_ComponentAdded;
    sink.ComponentReloaded += sink_ComponentReloaded;
    sink.ComponentRemoved += sink_ComponentRemoved;
    sink.ComponentRenamed += sink_ComponentRenamed;
    sink.ComponentSelected += sink_ComponentSelected;

    int cookie;
    connectionPoint.Advise(sink, out cookie);

    _componentsEventsConnectionPoints.Add(e.Item.VBComponents, Tuple.Create(connectionPoint, cookie));
}

当项目被删除时,您可以使用 IConnectionPoint.Unadvise ,并删除字典条目:

When a project is removed, you un-register the sinks using IConnectionPoint.Unadvise, and remove the dictionary entry:

void sink_ProjectRemoved(object sender, DispatcherEventArgs<VBProject> e)
{
    Tuple<IConnectionPoint, int> value;
    if (_componentsEventsConnectionPoints.TryGetValue(e.Item.VBComponents, out value))
    {
        value.Item1.Unadvise(value.Item2);
        _componentsEventsConnectionPoints.Remove(e.Item.VBComponents);
    }
}

然后你可以运行任何你想要的代码handler:

And then you can run any code you want in your handler:

void sink_ComponentAdded(object sender, DispatcherEventArgs<VBComponent> e)
{
    _parser.State.OnParseRequested(e.Item);
}

如果您有 Dispose App 类中的c>方法,这将是清除任何残留的好地方:

If you have a Dispose method in your App class, that would be a good place to clean up any remnants:

public void Dispose()
{
    _projectsEventsConnectionPoint.Unadvise(_projectsEventsCookie);
    foreach (var item in _componentsEventsConnectionPoints)
    {
        item.Value.Item1.Unadvise(item.Value.Item2);
    }
}

这篇关于如何挂接COM事件分派器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆