c# – 使用C将托管事件公开给COM

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

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

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

解决方法:

在C语言中,最简单的方法是在ATL的IDispEventImpl和IDispEventSimpleImpl模板的帮助下实现事件接收器.示例项目can be found here的说明.

有许多关于如何执行此操作的在线资源,例如thisthis,但这是所需步骤的列表:

首先让我们来看看管理方面.

为了提供活动,我们必须做到以下几点:

>声明一个事件接口(基于IDispatch)
>使用ComSourceInterfaces属性标记coclass以将事件接口绑定到coclass
>在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或“手动”执行.

需要执行以下步骤:

>接收器将继承IDispEventSimpleImpl(或IDispEventImpl)
>声明了所有需要的方法的接收地图
>为每个事件编写处理程序方法
>接收器已在事件源中注册
>最终,当不再需要时,接收器断开连接

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

// 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文件中定义类型info成员(即上例中的someEvent实例):

_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的情况不太明显).
您需要声明计划在接收器地图中使用的每个事件 – 如果您不需要所有这些事件,请不要映射它们.

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

// 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可以减少代码,因为您不需要提供可能是最丑陋部分的类型信息对象.但是,它有两个缺点:

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

上一篇:c – 将_TCHAR *转换为char *


下一篇:安装loadrunner,缺少VC2005_sp1_with_atl的错