Tru*_*der 1 c# c++ com events atl
可以公开在C#中编写的托管事件,以便在使用c ++编写的COM对象中公开和使用.不熟悉com和atl.你能否展示一下MSDN文章中所示的C++方面的内容
http://msdn.microsoft.com/en-us/library/dd8bf0x3.aspx
显示的VB6代码证明它是可行的.
在C++中最简单的方法是IMO,在ATL IDispEventImpl
和IDispEventSimpleImpl
模板的帮助下实现事件接收器.可以在此处找到示例项目的说明.
有许多关于如何执行此操作的在线资源,例如this或this,但以下是所需步骤的列表:
首先让我们来看看管理方面.
为了提供活动,我们必须做到以下几点:
IDispatch
基于)ComSourceInterfaces
属性标记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;
...
}
Run Code Online (Sandbox Code Playgroud)
现在,回到无人管理的一方.我将使用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
}
...
Run Code Online (Sandbox Code Playgroud)
}
现在,我们需要在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 -----------------------------------------+
Run Code Online (Sandbox Code Playgroud)
这可能会非常棘手类型映射关系并不总是显而易见的(例如,它可能是清楚的管理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);
Run Code Online (Sandbox Code Playgroud)
就是它,或多或少.使用 IDispEventImpl
而不是IDispEventSimpleImpl
产生少量代码的结果,因为您不需要提供可能是最丑陋部分的类型信息对象.但是,它有两个缺点: