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

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

问题描述

可以将使用C#编写的托管事件公开并在使用c ++编写的COM对象中使用。不熟悉com和atl。你可以展示MSDN文章



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



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



提前感谢。

解决方案

C ++中最简单的方法是IMO,通过ATL的 IDispEventImpl IDispEventSimpleImpl 模板。有关示例项目的解释,请点击这里



是很多关于如何做到这一点的在线资源,例如,但这里是必需的步骤列表:



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




  • 声明一个事件接口( IDispatch

  • $ c> ComSourceInterfaces 属性将事件接口绑定到coclass

  • 在coclass中实现匹配的事件



这里是托管代码:

  [ComVisible(true),
Guid(D6D3565F -...),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //!必须是IDispatch
public interface IMyEvents
{
[DispId(1)] // dispid用于正确映射事件
void SomethingHappened(DateTime timestamp,string message);
}

[ComVisible(true)]
[Guid(E22E64F7 -...)]
[ProgId(...)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IMyEvents))] //绑定事件接口
public class MyComServer:IMyComServer
{
//这里我们声明事件的委托
[ComVisible(false)]
public delegate void MyEventHandler(DateTime timestamp,string message);

//和一个与IMyEvents中的方法相匹配的公共事件
//您的代码将在需要时引发此事件
public event MyEventHandler SomethingHappened;
...
}

现在,回到非管理端。我将使用ATL,因为我发现它是编写COM客户端的最有效的方法,但您可以尝试MFC或手动。



需要以下步骤:




  • 接收器将继承 IDispEventSimpleImpl (或 IDispEventImpl

  • 为每个事件写入处理程序方法



  • $ b

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

      //导入COM的typelib server 
    //'named_guids'确保友好的事件接口ID
    #importmyserver.tlbnamed_guids
    const UINT SINK_ID = 234231341; //我们需要一些sink id

    class MyClient:public IDispEventSimpleImpl< SINK_ID,MyClient,& MyServer :: DIID_IMyEvents>
    {
    public:
    //现在你需要声明一个接收器映射 - 处理事件的方法映射
    BEGIN_SINK_MAP(MyClient)
    SINK_ENTRY_INFO(SINK_ID,MyServer :: DIID_IMyEvents,0x1,OnSomethingHappened,& someEvent)
    ^ ^ ^ ^
    //事件接口id(可以大于1)--- + | | |
    //必须与您的事件的dispid匹配----------------- + | |
    //处理事件的方法------------------------ + |
    //事件的类型信息,见下面------------------------------------- - +
    END_SINK_MAP()

    //声明类型info对象。您将需要一个具有不同签名的方法。
    //它将被定义在.cpp文件中,因为它是一个静态成员
    static _ATL_FUNC_INFO someEvent; //placeholder对象携带事件信息(见下文)

    //处理事件的方法
    STDMETHOD(OnSomethingHappened)(DATE时间戳,BSTR消息)
    {
    //通常在.cpp文件中定义它
    }

    ...

    }



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

      _ATL_FUNC_INFO MyClient :: traceEvent = {CC_STDCALL,VT_EMPTY, {VT_DECIMAL,VT_BSTR}}; // dispid = 1 
    ^ ^ ^ ^
    //调用约定(always stdcall)-------- + | | |
    //返回值的类型(只有VT_EMPTY有意义)---- + | |
    //事件的参数数量------------------------- + |
    //事件参数的变体类型--------------------------------------- - +

    这可能很棘手,因为类型映射并不总是明显的管理 int 映射到 VT_I4 ,但不太明显 DateTime 映射到 VT_DECIMAL )。
    您需要声明您计划在水槽地图中使用的每个事件 - 如果您不需要所有这些事件,请不要映射它们。



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

      // IUnknown * pUnk =接口COM服务器实例
    pMyClient-> DispEventAdvise(pUnk);
    // ..从这一点开始,事件将被客户端捕获
    //当你完成后,断开连接:
    pMyClient-> DispEventUnadvise(pUnk);

    这或多或少。使用 IDispEventImpl 而不是 IDispEventSimpleImpl 会导致少一点的代码,因为你不需要提供类型信息对象这可能是最丑的部分。但是,它有两个缺点:




    • 需要访问typelib(因为它需要读取接口元数据以提供类型信息


  • 有点慢

    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

    The VB6 code shown proves that it is doable.

    Thanks in advance.

    解决方案

    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:

    • 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

    Here are is the managed code:

    [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;
        ... 
    }
    

    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'.

    The following steps are required:

    • 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

    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
    }
    
    ... 
    

    }

    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 -----------------------------------------+
    

    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);
    

    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:

    • requires access to the typelib (as it needs to read the interface metadata in order to provide the type info itself)
    • is a bit slower (but I would guess not significantly)

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

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