热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

深入解析C++程序中激发事件和COM中的事件处理

这篇文章主要介绍了深入解析C++程序中激发事件和COM中的事件处理,是C++事件操作的基础,需要的朋友可以参考下

本机 C++ 中的事件处理
在处理本机 C ++ 事件时,您分别使用 event_source 和 event_receiver 特性设置事件源和事件接收器,并指定 type=native。这些特性允许应用它们的类在本机的非 COM 上下文中激发和处理事件。
声明事件
在事件源类中,对一个方法声明使用 __event关键字可将该方法声明为事件。请确保声明该方法,但不要定义它;这样做会产生编译器错误,因为将该方法转换为事件时编译器会隐式定义它。本机事件可以是带有零个或多个参数的方法。返回类型可以是 void 或任何整型。
定义事件处理程序
在事件接收器类中,可定义事件处理程序,这些处理程序是具有与它们将处理的事件匹配的签名(返回类型、调用约定和参数)的方法。
将事件处理程序挂钩到事件
同样在事件接收器类中,可使用内部函数 __hook 将事件与事件处理程序关联,并可使用 __unhook 取消事件与事件处理程序的关联。您可将多个事件挂钩到一个事件处理程序,或将多个事件处理程序挂钩到一个事件。
激发事件
若要激发事件,只需调用声明为事件源类中的事件的方法即可。如果处理程序已挂钩到事件,则将调用处理程序。
本机 C++ 事件代码
以下示例演示如何在本机 C++ 中激发事件。若要编译并运行此示例,请参考代码中的注释。
示例代码

// evh_native.cpp
#include 

[event_source(native)]
class CSource {
public:
  __event void MyEvent(int nValue);
};

[event_receiver(native)]
class CReceiver {
public:
  void MyHandler1(int nValue) {
   printf_s("MyHandler1 was called with value %d.\n", nValue);
  }

  void MyHandler2(int nValue) {
   printf_s("MyHandler2 was called with value %d.\n", nValue);
  }

  void hookEvent(CSource* pSource) {
   __hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
   __hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
  }

  void unhookEvent(CSource* pSource) {
   __unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
   __unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
  }
};

int main() {
  CSource source;
  CReceiver receiver;

  receiver.hookEvent(&source);
  __raise source.MyEvent(123);
  receiver.unhookEvent(&source);
}

输出:

MyHandler2 was called with value 123.
MyHandler1 was called with value 123.

COM 中的事件处理
在 COM 事件处理中,您使用 event_source 和 event_receiver 特性分别设置事件源和事件接收器,并指定 type=com。这些特性为自定义接口、调度接口和双重接口注入相应的代码,从而使这些接口能够应用到的类激发事件并通过 COM 连接点处理事件。
声明事件
在事件源类中,在接口声明上使用 __event 关键字以将该接口的方法声明为事件。当您将该接口的事件作为接口方法调用时,将激发这些事件。事件接口上的方法可以有零个或多个参数(应全是 in 参数)。返回类型可以是 void 或任何整型。
定义事件处理程序
在事件接收器类中,可定义事件处理程序,这些处理程序是具有与它们将处理的事件匹配的签名(返回类型、调用约定和参数)的方法。对于 COM 事件,调用约定不必匹配;有关详细信息,请参阅下文中的依赖于布局的 COM 事件。
将事件处理程序挂钩到事件
同样在事件接收器类中,可使用内部函数 __hook 将事件与事件处理程序关联,并可使用 __unhook 取消事件与事件处理程序的关联。您可将多个事件挂钩到一个事件处理程序,或将多个事件处理程序挂钩到一个事件。
注意
通常,有两种方法使 COM 事件接收器能够访问事件源接口定义。第一种是共享公共头文件,如下所示。第二种是将 #import 与 embedded_idl 导入限定符结合使用,以便让事件源类型库写入到保留了特性生成的代码的 .tlh 文件。
激发事件
若要激发事件,只需调用在事件源类中使用 __event 关键字声明的接口中的方法。如果处理程序已挂钩到事件,则将调用处理程序。
COM 事件代码
下面的示例演示如何在 COM 类中激发事件。若要编译并运行此示例,请参考代码中的注释。

// evh_server.h
#pragma once

[ dual, uuid("00000000-0000-0000-0000-000000000001") ]
__interface IEvents {
  [id(1)] HRESULT MyEvent([in] int value);
};

[ dual, uuid("00000000-0000-0000-0000-000000000002") ]
__interface IEventSource {
  [id(1)] HRESULT FireEvent();
};

class DECLSPEC_UUID("530DF3AD-6936-3214-A83B-27B63C7997C4") CSource;

接着是服务器:

// evh_server.cpp
// compile with: /LD
// post-build command: Regsvr32.exe /s evh_server.dll
#define _ATL_ATTRIBUTES 1
#include 
#include 
#include "evh_server.h"

[ module(dll, name="EventSource", uuid="6E46B59E-89C3-4c15-A6D8-B8A1CEC98830") ];

[coclass, event_source(com), uuid("530DF3AD-6936-3214-A83B-27B63C7997C4")]
class CSource : public IEventSource {
public:
  __event __interface IEvents; 

  HRESULT FireEvent() {
   __raise MyEvent(123);
   return S_OK;
  }
};

再然后是客户端:

// evh_client.cpp
// compile with: /link /OPT:NOREF
#define _ATL_ATTRIBUTES 1
#include 
#include 
#include 
#include "evh_server.h"

[ module(name="EventReceiver") ];

[ event_receiver(com) ]
class CReceiver {
public:
  HRESULT MyHandler1(int nValue) {
   printf_s("MyHandler1 was called with value %d.\n", nValue);
   return S_OK;
  }

  HRESULT MyHandler2(int nValue) {
   printf_s("MyHandler2 was called with value %d.\n", nValue);
   return S_OK;
  }

  void HookEvent(IEventSource* pSource) {
   __hook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler1);
   __hook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler2);
  }

  void UnhookEvent(IEventSource* pSource) {
   __unhook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler1);
   __unhook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler2);
  }
};

int main() {
  // Create COM object
  CoInitialize(NULL);
  {
   IEventSource* pSource = 0;
   HRESULT hr = CoCreateInstance(__uuidof(CSource), NULL,     CLSCTX_ALL, __uuidof(IEventSource), (void **) &pSource);
   if (FAILED(hr)) {
     return -1;
   }

   // Create receiver and fire event
   CReceiver receiver;
   receiver.HookEvent(pSource);
   pSource->FireEvent();
   receiver.UnhookEvent(pSource);
  }
  CoUninitialize();
  return 0;
}

输出

MyHandler1 was called with value 123.
MyHandler2 was called with value 123.

依赖于布局的 COM 事件
布局依赖性只是 COM 编程中的一个问题。在本机和托管事件处理中,处理程序的签名(返回类型、调用约定和参数)必须与其事件匹配,但处理程序的名称不必与其事件匹配。
但是,在 COM 事件处理中,如果将 event_receiver 的 layout_dependent 参数设置为 true,则将强制名称和签名匹配。这意味着事件接收器中处理程序的名称和签名必须与处理程序将挂钩到的事件的名称和签名完全匹配。
当 layout_dependent 设置为 false 时,激发事件方法与挂钩方法(其委托)之间的调用约定和存储类(虚拟、静态等)可以混合和匹配。将 layout_dependent 设置为 true 效率会稍微高一点。
例如,假设 IEventSource 定义为具有下列方法:

[id(1)] HRESULT MyEvent1([in] int value);
[id(2)] HRESULT MyEvent2([in] int value);

假定事件源具有以下形式:

[coclass, event_source(com)]
class CSource : public IEventSource {
public:
  __event __interface IEvents;

  HRESULT FireEvent() {
   MyEvent1(123);
   MyEvent2(123);
   return S_OK;
  }
};

则在事件接收器中,挂钩到 IEventSource 中的方法的任何处理程序必须与其名称和签名匹配,如下所示:

[coclass, event_receiver(com, true)]
class CReceiver {
public:
  HRESULT MyEvent1(int nValue) { // name and signature matches MyEvent1
   ...
  }
  HRESULT MyEvent2(E c, char* pc) { // signature doesn't match MyEvent2
   ...
  }
  HRESULT MyHandler1(int nValue) { // name doesn't match MyEvent1 (or 2)
   ...
  }
  void HookEvent(IEventSource* pSource) {
   __hook(IFace, pSource); // Hooks up all name-matched events 
                // under layout_dependent = true
   __hook(&IFace::MyEvent1, pSource, &CReceive::MyEvent1);  // valid
   __hook(&IFace::MyEvent2, pSource, &CSink::MyEvent2);  // not valid
   __hook(&IFace::MyEvent1, pSource, &CSink:: MyHandler1); // not valid
  }
};


推荐阅读
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 如何基于ggplot2构建相关系数矩阵热图以及一个友情故事
    本文介绍了如何在rstudio中安装ggplot2,并使用ggplot2构建相关系数矩阵热图。同时,通过一个友情故事,讲述了真爱难觅的故事背后的数据量化和皮尔逊相关系数的概念。故事中的小伙伴们在本科时参加各种考试,其中有些沉迷网络游戏,有些热爱体育,通过他们的故事,展示了不同兴趣和特长对学习和成绩的影响。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
author-avatar
mobiledu2502892183
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有