使用C ++将托管事件公开给COM [英] Exposing managed events to COM using C++

查看:110
本文介绍了使用C ++将托管事件公开给COM的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有可能公开用C#编写的托管事件,以公开并在使用c ++编写的COM对象中使用.不太熟悉com和atl.您能否说明一下MSDN文章中显示的示例的C ++方面会是什么样子?

It is possible to exposed managed events written in C# to be exposed and used in a COM object written using c++. not being that familiar with com and atl. Can you please show what would the C++ side of things would look like for the example shown in the MSDN article

http://msdn.microsoft.com/en-us/library/dd8bf0x3.aspx

显示的VB6代码证明它是可行的.

The VB6 code shown proves that it is doable.

推荐答案

C ++中最简单的方法是IMO,借助ATL的IDispEventImplIDispEventSimpleImpl模板实现事件接收器.可以在此处找到示例项目的说明.

The easiest way in C++ would be, IMO, to implement an event sink with help of ATL's IDispEventImpl and IDispEventSimpleImpl templates. An explanation with sample project can be found here.

有关如何执行此操作的在线资源很多,例如

There are many online resources about how to do this, e.g. this or this, but here is the list of required steps:

首先让我们看一下托管方面.

First let's take a look at managed side.

为了提供事件,我们必须执行以下操作:

In order to provide events, we must do the following:

  • 声明事件接口(基于IDispatch)
  • ComSourceInterfaces属性标记coclass以将事件接口绑定到coclass
  • 在coclass中实现匹配事件
  • declare an event interface (IDispatch-based)
  • mark the coclass with ComSourceInterfaces attribute to bind the event interface to coclass
  • implement matching events in the coclass

这是托管代码:

[ComVisible(true), 
 Guid("D6D3565F-..."), 
 InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //! must be IDispatch
public interface IMyEvents
{
    [DispId(1)] // the dispid is used to correctly map the events
    void SomethingHappened(DateTime timestamp, string message);
}

[ComVisible(true)]
[Guid("E22E64F7-...")]
[ProgId("...")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IMyEvents))] // binding the event interface
public class MyComServer : IMyComServer  
{
    // here we declare the delegate for the event
    [ComVisible(false)]
    public delegate void MyEventHandler(DateTime timestamp, string message);

    // and a public event which matches the method in IMyEvents
    // your code will raise this event when needed
    public event MyEventHandler SomethingHappened;
    ... 
}

现在,回到不受管理的一面.我将使用ATL,因为我发现它是编写COM客户端的最有效方法,但是您可以尝试MFC或手动"完成.

Now, back to unmanaged side. I will use ATL, as I find it the most effective way to write COM clients, but you can try MFC or do it 'manually'.

需要执行以下步骤:

  • 接收器将继承IDispEventSimpleImpl(或IDispEventImpl)
  • 声明了
  • 具有所有必需方法的接收器图
  • 为每个事件编写
  • 处理程序方法
  • 接收器已向事件源注册
  • 最终,当不再需要水槽时,
  • the sink will inherit IDispEventSimpleImpl (or IDispEventImpl)
  • sink map with all the needed methods is declared
  • handler methods are written for each event
  • the sink is registered with event source
  • eventually, when not needed anymore, the sink is disconnected

这是ATL C ++客户端中的代码:

Here's the code in an ATL C++ client:

// import the typelib of your COM server
// 'named_guids' ensures friendly ID of event interface
#import "myserver.tlb" named_guids 
const UINT SINK_ID = 234231341; // we need some sink id

class MyClient : public IDispEventSimpleImpl<SINK_ID, MyClient, &MyServer::DIID_IMyEvents >
{
    public:
    // now you need to declare a sink map - a map of methods handling the events
    BEGIN_SINK_MAP(MyClient)
      SINK_ENTRY_INFO(SINK_ID, MyServer::DIID_IMyEvents, 0x1, OnSomethingHappened, &someEvent)
                                                   ^      ^      ^                   ^
      // event interface id (can be more than 1)---+      |      |                   |
      // must match dispid of your event -----------------+      |                   |
      // method which handles the event  ------------------------+                   |
      // type information for event, see below --------------------------------------+
    END_SINK_MAP()

// declare the type info object. You will need one for each method with different signature.
// it will be defined in the .cpp file, as it is a static member
static _ATL_FUNC_INFO someEvent;  // 'placeholder' object to carry event information (see below)

// method which handles the event
STDMETHOD (OnSomethingHappened)(DATE timestamp, BSTR message)
{ 
   // usually it is defined it in the .cpp file
}

... 

}

现在,我们需要在cpp文件中定义类型信息成员(即,上例中的someEvent实例):

Now, we need to define the type info members in the cpp file (i.e. the someEvent instance from example above):

_ATL_FUNC_INFO MyClient::traceEvent = { CC_STDCALL, VT_EMPTY, 2 , {VT_DECIMAL, VT_BSTR} };  // dispid = 1
                                               ^        ^     ^              ^
// calling convention (always stdcall) --------+        |     |              |
// type of return value (only VT_EMPTY makes sense) ----+     |              |
// number of parameters to the event -------------------------+              |
// Variant types of event arguments -----------------------------------------+

这可能很棘手,因为类型映射并不总是很明显(例如,很可能托管的int映射到VT_I4,但是不太明显的是DateTime映射到VT_DECIMAL). 您需要在接收器映射中声明计划使用的每个事件-如果不需要所有事件,则不要映射它们.

This can be tricky as type mappings are not always obvious (e.g. a it might be clear that managed int maps to VT_I4, but it is less obvious that DateTime maps to VT_DECIMAL). You need to declare each event you plan to use in the sink map - if you don't need all of them, don't map them.

现在,您需要将接收器连接到事件源:

Now you need to connect your sink to the event source:

// IUnknown* pUnk = interface to you COM server instance
pMyClient->DispEventAdvise(pUnk);
// .. from this point, events will be caught by the client
// when you are done, disconnect:
pMyClient->DispEventUnadvise(pUnk);

就是这样,或多或少.使用IDispEventImpl而不是IDispEventSimpleImpl会减少代码量,因为您不需要提供类型信息对象,这可能是最丑陋的部分.但是,它有两个缺点:

This is it, more or less. Using IDispEventImpl instead of IDispEventSimpleImpl results in a bit less code, as you don't need to supply the type info objects which might be the ugliest part. However, it has two drawbacks:

  • 需要访问typelib(因为它需要读取接口元数据以提供类型信息本身)
  • 有点慢(但我想不会很明显)

这篇关于使用C ++将托管事件公开给COM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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