如何连接COM事件调度程序? [英] How to hook up a COM event dispatcher?

查看:173
本文介绍了如何连接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?

推荐答案

System.Runtime.InteropServices 命名空间暴露了一个静态的 ComEventsHelper 类将管理代理连接到非托管调度源。这基本上与其他答案相同,但连接点在运行时可调用的包装器中处理,而不是必须可以从调用代码中明确地进行管理(从而使其更加健壮)。我怀疑这是如何在内部处理源接口(反编译 Microsoft.Vbe.Interop 的问题,使它难以理解)。

The System.Runtime.InteropServices namespace exposes a static ComEventsHelper class to connect managed delegates to unmanaged dispatch sources. This basically does the same thing as the other answer, but the connection points are handled within the runtime callable wrapper instead of having to be managed explicitly from the calling code (thus making it somewhat more robust). I suspect that this is how PIAs are handling source interfaces internally (decompiling the Microsoft.Vbe.Interop in question mangled it enough that it's hard to tell).

在这种情况下,由于一些不可估量的原因,所讨论的接口不被声明为源接口,因此PIA构建没有在运行时包装器中连接事件处理程序。因此,您可以在包装类中手动连接处理程序,并将其转发为包装事件,但仍然将处理连接点的重物(和线程安全管理)放在RCW中。请注意,您需要引用类型库中的2条信息 - _dispVBComponentsEvents 接口和 DispId 您有兴趣聆听的非托管事件:

In this case, for some unfathomable reason the interface in question isn't declared as a source interface, so the PIA build didn't connect the event handlers in the runtime wrapper. So... you can wire up the handlers manually in the wrapper class and forward them as wrapped events, but still leave the heavy lifting (and thread safety management) of dealing with the connection points to the RCW. Note that you need 2 pieces of information from the referenced type library - the guid of the _dispVBComponentsEvents interface and the DispId's of the unmanaged events that you're interested in listening to:

private static readonly Guid VBComponentsEventsGuid = new Guid("0002E116-0000-0000-C000-000000000046");

private enum ComponentEventDispId
{
    ItemAdded = 1,
    ItemRemoved = 2,
    ItemRenamed = 3,
    ItemSelected = 4,
    ItemActivated = 5,
    ItemReloaded = 6
}

然后,它们在类包装器的ctor中(仅为了简洁而显示)...

Then, wire them up in the ctor of the class wrapper (only one shown for the sake of brevity)...

private delegate void ItemAddedDelegate(VB.VBComponent vbComponent);
private readonly ItemAddedDelegate _componentAdded;

public VBComponents(VB.VBComponents target) 
{
    _target = target;
    _componentAdded = OnComponentAdded;
    ComEventsHelper.Combine(_target, 
                            VBComponentsEventsGuid, 
                            (int)ComponentEventDispId.ItemAdded, 
                           _componentAdded);
}

...并转发事件:

public event EventHandler<DispatcherEventArgs<IVBComponent>> ComponentAdded;
private void OnComponentAdded(VB.VBComponent vbComponent)
{
    OnDispatch(ComponentAdded, VBComponent);
}

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

完成后,取消注册委托通过调用 ComEventsHelper.Remove

When you're done, un-register the delegate by calling ComEventsHelper.Remove:

ComEventsHelper.Remove(_target, 
                       VBComponentsEventsGuid,
                       (int)ComponentEventDispId.ItemAdded,
                       _componentAdded);

上面的示例使用一个包装类来解决问题,但可以从任何地方使用相同的方法您需要在COM事件处理之前附加其他功能或将其传递给其他监听器。

The example above uses a wrapper class per the question, but the same method could be used from anywhere if you need to attach additional functionality to a COM event before handling it or passing it on to other listeners.

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

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