Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ionic-framework/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用C+向COM公开托管事件+; P>可以公开在C语言中写的托管事件,并将其暴露在使用C++编写的COM对象中。不太熟悉com和atl。您能显示MSDN文章所示的例子中的C++方面会是什么样子吗?_C#_C++_Events_Com_Atl - Fatal编程技术网

C# 使用C+向COM公开托管事件+; P>可以公开在C语言中写的托管事件,并将其暴露在使用C++编写的COM对象中。不太熟悉com和atl。您能显示MSDN文章所示的例子中的C++方面会是什么样子吗?

C# 使用C+向COM公开托管事件+; P>可以公开在C语言中写的托管事件,并将其暴露在使用C++编写的COM对象中。不太熟悉com和atl。您能显示MSDN文章所示的例子中的C++方面会是什么样子吗?,c#,c++,events,com,atl,C#,C++,Events,Com,Atl,所示的VB6代码证明了它是可行的。如果可以使用C++/CLI,则只需执行()即可: 如果可以使用C++/CLI,则只需执行()即可: < > C++中最简单的方法是用ATL的< IDispEventImpl > 和 IDispEventSimpleImpl < /C>模板来实现事件接收器。带有示例项目的说明 关于如何实现这一点,有许多在线资源,例如或,但以下是所需步骤的列表: 首先让我们看一下托管端。 为了提供活动,我们必须执行以下操作: 声明事件接口(基于IDispatch的) 使用ComS


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

如果可以使用C++/CLI,则只需执行()即可:


如果可以使用C++/CLI,则只需执行()即可:


< > C++中最简单的方法是用ATL的< IDispEventImpl > <代码>和<代码> IDispEventSimpleImpl < /C>模板来实现事件接收器。带有示例项目的说明

关于如何实现这一点,有许多在线资源,例如或,但以下是所需步骤的列表:

首先让我们看一下托管端。

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

  • 声明事件接口(基于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或“手动”使用它

需要执行以下步骤:

  • 接收器将继承
    IDispentSimpleImpl
    (或
    IDispentImpl
  • 声明了包含所有所需方法的sink映射
  • 为每个事件编写处理程序方法
  • 接收器已向事件源注册
  • 最终,当不再需要时,接收器断开连接

这里是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
}

... 
这可能很棘手,因为类型映射并不总是显而易见的(例如,托管的
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
会产生更少的代码,因为您不需要提供可能是最丑陋部分的类型信息对象。但是,它有两个缺点:

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

,C++中最简单的方法是IMO,用ATL的< IDispEventImpl > <代码>和<代码> IDispEventSimpleImpl < /C>模板来实现事件接收器。带有示例项目的说明

关于如何实现这一点,有许多在线资源,例如或,但以下是所需步骤的列表:

首先让我们看一下托管端。

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

  • 声明事件接口(基于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或“手动”使用它

需要执行以下步骤:

  • 接收器将继承
    IDispentSimpleImpl
    (或
    IDispentImpl
  • 声明了包含所有所需方法的sink映射
  • 为每个事件编写处理程序方法
  • 接收器已向事件源注册
  • 最终,当不再需要时,接收器断开连接

这里是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
}

... 
这可能很棘手,因为类型映射并不总是显而易见的(例如,托管的
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
会产生更少的代码,因为您不需要提供可能是最丑陋部分的类型信息对象。但是,它有两个缺点:

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

    • Zdeslav Vojkovic提出的解决方案对我来说几乎是完整的答案,但在Outlook加载项中实现接收器时,我遇到了稳定性问题。在第一次未通知后,系统变得不稳定,并可能在下一次通知时崩溃。我的解决方案是将接收器作为COM对象

      为了简洁起见,我只添加了解决方案的ATL端,因为托管代码可以完全按照Zdeslav Vojkovic的建议保留

      我在Visual Studio 2017中遵循了以下步骤:

      创建ATL简单对象
    • 右键单击项目并选择添加>新建项目
    • 选择ATL简单对象并单击添加按钮
    • 在另一个选项卡中,选择“单一”作为线程模型
    • 单击“完成”
    • 填写接收器接口的详细信息 在生成的idl中,添加用于初始化和关闭的处理程序:

      [
          object,
          uuid(a5211fba-...),
          dual,
          nonextensible,
          pointer_default(unique)
      ]
      interface IMyClient : IDispatch
      {
          [id(1), helpstring("method InitHandler"), local] HRESULT InitHandler(IUnknown* myserver);
          [id(2), helpstring("method ShutdownHandler"), local] HRESULT ShutdownHandler(void); 
      };
      
      填写头文件(MyClient.h)
      请注意,这里的Advise/Unadvise调用是不同的。

      Zdeslav Vojkovic提出的解决方案几乎是我的完整答案,但是在Outlook加载项中实现接收器时,我遇到了稳定性问题。在第一次未通知后,系统变得不稳定,并可能在下一次通知时崩溃。解f
      _ATL_FUNC_INFO SomethingHappenedInfo = { CC_STDCALL, VT_EMPTY, 2 , {VT_DECIMAL, VT_BSTR} };
      
      
      STDMETHODIMP CMyClient::InitHandler(IUnknown* myserver)
      {
          this->mMyComServer = myserver;
          SomethingHappenedEvent::DispEventAdvise((IDispatch*)this->mMyComServer);
      
          return S_OK;
      }
      
      
      STDMETHODIMP CMyClient::ShutdownHandler(void)
      {    
          SomethingHappenedEvent::DispEventUnadvise(this->mMyComServer);          
      
          return S_OK;
      }
      
      void __stdcall CMyClient::OnSomethingHappened(DateTime timestamp, string message)
      {   
          ...
      }