gpt4 book ai didi

c# - 使用 C++ 向 COM 公开托管事件

转载 作者:行者123 更新时间:2023-11-30 00:37:52 25 4
gpt4 key购买 nike

可以在使用 C++ 编写的 COM 对象中公开和使用用 C# 编写的公开托管事件。不太熟悉 com 和 atl。对于 MSDN 文章中所示的示例,您能否展示 C++ 方面的情况?

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

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

最佳答案

在 C++ 中,最简单的方法是,IMO,在 ATL 的 IDispEventImplIDispEventSimpleImpl 模板的帮助下实现事件接收器。示例项目的解释 can be found here .

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

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

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

  • 声明一个事件接口(interface)(基于IDispatch)
  • ComSourceInterfaces属性标记coclass,将事件接口(interface)绑定(bind)到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)
  • 声明了所有需要的方法的sink map
  • 为每个事件编写处理方法
  • 接收器已注册到事件源
  • 最终,当不再需要时,接收器会断开连接

这是 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 文件中定义类型信息成员(即上面示例中的 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 会导致代码少一些,因为您不需要提供类型信息对象,这可能是最丑陋的部分。但是,它有两个缺点:

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

关于c# - 使用 C++ 向 COM 公开托管事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12648212/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com