基于VC编写COM连接点事件的分析介绍

COM 中的典型方案是让客户端对象实例化服务器对象,然后调用这些对象。然而,没有一种特殊机制的话,这些服务器对象将很难转向并回调到客户端对象。COM 连接点便提供了这种特殊机制,实现了服务器和客户端之间的双向通信。使用连接点,服务器能够在服务器上发生某些事件时调用客户端。

原理如下图:

有了连接点,服务器可通过定义一个接口来指定它能够引发的事件。服务器上引发事件时,要采取操作的客户端会向服务器进行自行注册。随后,客户端会提供服务器所定义接口的实现。

客户端可通过一些标准机制向服务器进行自行注册。COM 为此提供了 IConnectionPointContainer 和 IConnectionPoint 接口。

COM 连接点服务器的客户端可用 C++ 和 C# 托管代码来编写。C++ 客户端会注册一个类的实例,该类提供了接收器接口的实现。托管客户端会注册单个事件的委托,因而会按每个事件通知方法创建单个接收器,具体参考C#的互操作部分内容。

一、连接点程序编写
1、使用ATL建立组件程序。
2、添加ATL     SIMPLE     OBJECT,支持连接点事件。
注:如果当时没有现在连接点事件,可以在.idl文件中手动添加。比如


代码如下:

[
       uuid(57CCB7A5-F3B6-4990-91CD-33A82E1AAA46),
       helpstring("IFunEvent dispinterface")
    ]
    dispinterface _IFunEvent
    {
       properties:
           //  事件接口没有任何属性
       methods:
           [id(1), helpstring("方法OnResult")] HRESULT OnResult([out,retval] LONG* retval);
           [id(2), helpstring("方法OnType")] HRESULT OnType([in] LONG nType);
}

3、因为支持连接点事件,这样将会自动生成一个     _XXXEVENT源接口。我们在其中增加想要触发的方法。
4、选择组件下的事件对象,弹出对话框选择添加方法。可以继续添加多个方法…
5、实现方法(其实组件里只是做方法的申明,客户调用时才实现这些方法)。实现时选中组件/类,按右键,在弹出菜单中选中implement     connection....
就会产生CProxy_xxxEvent类,里面有Fire函数的实现,都是自动生成的。
6、完成组件的其他接口函数。
组件的连接点编写比较简单,关键是如何在客户端实现事件监听与接收。在.NET下很容易实现。但在VC中比较繁琐。
二、连接点客户端实现(VC)
1、包含“工程_i.h”头文件,引入“工程.tlb”ole库文件。比如:
#include "ATLDemo_i.h"
#import "ATLDemo.tlb" named_guids raw_interfaces_only
2、创建一个类:由_IXXXEvent派生过来。(XXX为实际事件名)
实现类各个虚函数重载,如果_IXXXEvent是IUnkown接口只需要重载QueryInterface、AddRef、Release函数;如果_IXXXEvent是双向接口需要重载实现IUnkown接口三个函数和IDispatch接口四个函数。
实现事件功能,通过函数、用SINK_ENTRY_INFO实现事件的映射、Invoke函数里面实现(通过事件ID)三种方法之一来实现。
用SINK_ENTRY_INFO实现事件的映射
如:


代码如下:

BEGIN_SINK_MAP(CEventSink)
    SINK_ENTRY_INFO(1,DIID__INew01Events,DISPID_MSG,Msg,&MsgInfo)
END_SINK_MAP()

我在组件中定义了一个Msf函数,所以在这里对其进行消息隐射。然后实现Msg方法。
3、如何调用
3.1使用工程支持COM,使用afxoleinit或者CoInitialize/Un CoInitialize
3.2得到组件接口
3.3得到连接点容器,查找连接点。
3.4利用Advise将一个监听对象传给组件,这样当事件发生的时候事件就会响应。在不使用时,通过UnAdvise来断开连接点事件。同时也利用AfxConnectionAdvice将监听对象传给组件接口。
3.5 释放资源。
具体代码如下:


代码如下:

#pragma once

#include "ATLDemo_i.h"
#import "ATLDemo.tlb" named_guids raw_interfaces_only

class CSkin : public _IFunEvent
{
public:
    CSkin(void);
    ~CSkin(void);
private:
    DWORD       m_dwRefCount;
public:
    STDMETHODIMP Fire_OnType( LONG nType)
    {
       CString    strTemp;
       strTemp.Format(_T("The result is %d"), nType);
       AfxMessageBox(strTemp);
       return S_OK;;
    }
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
    {
       if (iid == DIID__IFunEvent)
       {
           m_dwRefCount++;
           *ppvObject = (void *)this;
           return S_OK;
       }

if (iid == IID_IUnknown)
       {
           m_dwRefCount++;          
           *ppvObject = (void *)this;
           return S_OK;
       }

return E_NOINTERFACE;
    }
    ULONG STDMETHODCALLTYPE AddRef()
    {
       m_dwRefCount++;
       return m_dwRefCount;
    }

ULONG STDMETHODCALLTYPE Release()
    {
       ULONG l;

l  = m_dwRefCount--;

if ( 0 == m_dwRefCount)
       {
           delete this;
       }

return l;
    }

HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
       /* [out] */ __RPC__out UINT *pctinfo)
    {
       return S_OK;
    }

HRESULT STDMETHODCALLTYPE GetTypeInfo(
       /* [in] */ UINT iTInfo,
       /* [in] */ LCID lcid,
       /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo)
    {
       return S_OK;
    }

HRESULT STDMETHODCALLTYPE GetIDsOfNames(
       /* [in] */ __RPC__in REFIID riid,
       /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
       /* [range][in] */ UINT cNames,
       /* [in] */ LCID lcid,
       /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)
    {
       return S_OK;
    }

/* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
       /* [in] */ DISPID dispIdMember,
       /* [in] */ REFIID riid,
       /* [in] */ LCID lcid,
       /* [in] */ WORD wFlags,
       /* [out][in] */ DISPPARAMS *pDispParams,
       /* [out] */ VARIANT *pVarResult,
       /* [out] */ EXCEPINFO *pExcepInfo,
       /* [out] */ UINT *puArgErr)
    {
       switch(dispIdMember) // 根据不同的dispIdMember,完成不同的回调函数,事件函数的ID编号
       {
       case 2:
           {
              // 1st param : [in] long lValue.
              VARIANT varlValue;
              long lValue = 0;
              VariantInit(&varlValue);
              VariantClear(&varlValue);
              varlValue = (pDispParams->rgvarg)[0];
              lValue = V_I4(&varlValue);
              Fire_OnType(lValue);
           }
           break;
       default:   break;
       }

return S_OK;
    }
};

#include "StdAfx.h"
#include "Skin.h"

CSkin::CSkin(void)
{
     m_dwRefCount =0;
}

CSkin::~CSkin(void)
{
}

实现部分:


代码如下:

CoInitialize(NULL);

CComPtr<IFun> pFun;
    HRESULT hr = pFun.CoCreateInstance(CLSID_Fun);
    if(hr!=S_OK)
    {
       return ;
    }

IConnectionPointContainer *pCPC;
    hr = pFun->QueryInterface(IID_IConnectionPointContainer,(void **)&pCPC);
    if(!SUCCEEDED(hr))
    {
       return ;
    }
    IConnectionPoint *pCP;
    hr = pCPC->FindConnectionPoint(DIID__IFunEvent,&pCP);
    if ( !SUCCEEDED(hr) )
    {
       return ;
    }
    pCPC->Release();

IUnknown *pSinkUnk;
    CSkin *pSink = new CSkin();
    hr = pSink->QueryInterface(IID_IUnknown,(void **)&pSinkUnk);
    DWORD dwAdvise;
    hr = pCP->Advise(pSinkUnk,&dwAdvise);//接收器与连接点建立关联

LONG c = 0;
    pFun->Add(1,5,&c);
    //pCP->Unadvise(dwAdvise) //断开连接点事件
    pCP->Release();
    pFun.Release();

CoUninitialize();

(0)

相关推荐

  • C++中事件机制的简洁实现及需要放弃的特性

    事件模型是被广泛使用的好东西,但是C++标准库里没有现成的,其他实现又复杂或者不优雅,比如需要使用宏.现在VC11可以用在XP下了,那么就痛快的拿起C++11提供的先进设施组合出一个轻便的实现吧. 为了达到简洁的目的,需要放弃一些特性: 1.不支持判断函数是否已经绑定过(因为std::function不提供比较方法,自己实现function的话代码又变多了) 2.需要使用者接收返回的回调函数标识来移除事件绑定(原因同上) 3.事件没有返回值,不支持回调函数优先级.条件回调等事件高级特性(比如返回

  • VC实现给窗体的一个按钮添加事件的方法

    本文实例讲述了VC实现给窗体的一个按钮添加事件的方法.分享给大家供大家参考.具体实现方法如下: 1. 在MainFrm.h中 复制代码 代码如下: // Generated message map functions 添加: 复制代码 代码如下: afx_msg void OnSelectRed(); //声明事件名称 2. MainFrm.cpp中 复制代码 代码如下: BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 下面 添加: 复制代码 代码如下: ON_

  • MFC中动态创建控件以及事件响应实现方法

    本文实例讲述了MFC中动态创建控件以及事件响应实现方法,分享给大家供大家参考.具体实现方法如下: 动态控件是指在需要时由Create()创建的控件,这与预先在对话框中放置的控件是不同的. 一.创建动态控件: 为了对照,我们先来看一下静态控件的创建. 放置静态控件时必须先建立一个容器,一般是对话框,这时我们在对话框编辑窗口中,从工具窗口中拖出所需控件放在对话框中即可,再适当修改控件ID,设置控件属性,一个静态控件就创建好了,当对话框被显示时,其上的控件也会显示. 静态控件不需要调用Create()

  • VC6.0打开文件以及向工程中添加文件时程序崩溃自动退出解决方法

    换了一台电脑,vc6.0程序中,点击打开文件以及向工程中添加文件时,程序竟然崩溃自动退出了. 不知什么原因,安装相同的vc程序,本本竟然出现此缘故.但是这个操作又是自己经常用到的,所以不得不解决. 与上一台电脑不同的是,此电脑是win7系统,而上一个则是xp系统.此电脑office是2010版本,而上一个则是WPS:于是乎,在网上查资料,来解决. 看到网上也有类似的问题,有的说是win7系统原因,有的说是office2007版本缘故,有的说是viso缘故.总之,这几种说法,我都符合.win7系统

  • 解决C++中事件不响应的方法详解

    在C++开发过程由于这样那样的原因,可以会出现点击菜单.右键菜单无响应的问题,或者点击A菜单,但是响应的却是B菜单.遇到上述问题时,你可以从下面几个方面分析,一般都可以解决问题.下面从四个方面来解决问题: 1.检查菜单ID对于的事件映射表是否存在,如果不存在肯定不会响应2.检查菜单ID与其它菜单ID是否出现重复,如果出现重复可能出现张冠李戴的现象(点击A菜单,但是响应B菜单的行为)3.菜单ID与对应菜单事件的映射表ID是否一致,可能出现菜单ID与事件映射表的ID不一致4.在Qt中,要注意信号函数

  • C++设置事件通知线程工作的方法

    本文实例讲述了C++设置事件通知线程工作的方法,其中主线程通过将事件状态设置为"受信"来通知工作线程工作.具体实现方法如下: 复制代码 代码如下: // eventDemo.cpp : 定义控制台应用程序的入口点.  //    #include "stdafx.h"  #include <Windows.h>  #include <process.h>  HANDLE g_event;    UINT __stdcall ThreadPro

  • 基于VC编写COM连接点事件的分析介绍

    COM 中的典型方案是让客户端对象实例化服务器对象,然后调用这些对象.然而,没有一种特殊机制的话,这些服务器对象将很难转向并回调到客户端对象.COM 连接点便提供了这种特殊机制,实现了服务器和客户端之间的双向通信.使用连接点,服务器能够在服务器上发生某些事件时调用客户端. 原理如下图: 有了连接点,服务器可通过定义一个接口来指定它能够引发的事件.服务器上引发事件时,要采取操作的客户端会向服务器进行自行注册.随后,客户端会提供服务器所定义接口的实现. 客户端可通过一些标准机制向服务器进行自行注册.

  • 基于java编写局域网多人聊天室

    由于需要制作网络计算机网络课程设计,并且不想搞网络布线或者局域网路由器配置等等这种完全搞不懂的东西,最后决定使用socket基于java编写一个局域网聊天室: 关于socket以及网络编程的相关知识详见我另一篇文章:Java基于socket编程 程序基于C/S结构,即客户端服务器模式. 服务器: 默认ip为本机ip 需要双方确定一个端口号 可设置最大连接人数 可启动与关闭 界面显示在线用户人以及姓名(本机不在此显示) 客户端: 需要手动设置服务器ip地址(局域网) 手动设置端口号 输入姓名 可连

  • 基于Java编写串口通信工具

    最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^ 一: 首先,你需要下载一个额外的支持Java串口通信操作的jar包,由于java.comm比较老了,而且不支持64位系统,这里推荐Rxtx这个jar包(32位/64位均支持). 官方下载地址:http://fizzed.com/oss/rxtx-for-java (注:可能需要FQ才能下载) 不能FQ的童鞋,可以在这里下载: http://xiazai.jb51

  • 基于C#编写经理评分系统

    先写需求: 01.显示员工信息 02.实现项目经理给员工评分的功能 第一步: 建立两个类,员工类和项目经理类 定义属性和方法 员工类:工号.年龄.姓名.人气值.项目经理年度评分.经理评价 项目经理类:ID.年龄.姓名.性别.资历,由于经理可以给员工评分,因此还有评分的方法 先上两张图再说: 查看窗体FrmShow 评分窗体FrmJudge 不说了,上代码 首先是员工类 using System.Text; using System.Threading.Tasks; namespace 经理评分系

  • 基于Python编写一个宝石消消乐小游戏

    目录 开发工具 环境搭建 原理简介 开发工具 python版本:3.6.4 相关模块: pygame:以及一些python自带的模块. 环境搭建 安装python并添加到环境变量,pip安装需要的相关模块即可. 原理简介 游戏规则: 玩家通过鼠标交换相邻的拼图,若交换后水平/竖直方向存在连续三个相同的拼图,则这些拼图消失,玩家得分,同时生成新的拼图以补充消失的部分,否则,交换失败,玩家不得分.玩家需要在规定时间内获取尽可能高的得分. 实现过程: 首先加载一些必要的游戏素材: 加载背景音乐: py

  • 基于Python编写一个自动关机程序

    目录 1.实现效果 2.实现步骤 3.全部代码 1.实现效果 2.实现步骤 模块导入 import os,sys,time from PyQt5 import QtCore,QtWidgets,QtGui 窗口设置 def pageShow(self,page): #设置窗口的位置和大小 page.setGeometry(400,400,400,200) #设置窗口的标题 page.setWindowTitle('Window shutdown') #设置窗口的图标 #page.setWindo

  • 基于Python编写一个B站全自动抽奖的小程序

    目录 导语 开发工具 环境搭建 原理简介 导语 应好友邀请,帮他写了个小程序,功能类似于实时监控自己关注的UP主,如果关注的UP主中有人发布了抽奖的动态,就自动参与这个抽奖.这样就能不错过任何一个可以暴富的机会了.写完之后感觉这个想法还是挺有意思的,于是上来分享一波. 废话不多说,让我们愉快地开始吧~ 开发工具 Python版本:3.7.8 相关模块: DecryptLogin模块: 以及一些python自带的模块. 环境搭建 安装Python并添加到环境变量,pip安装需要的相关模块即可. 原

  • 基于Python编写微信清理工具的示例代码

    目录 主要功能 运行环境 核心代码 完整代码 前几天网上找了一款 PC 端微信自动清理工具,用了一下,电脑释放了 30GB 的存储空间,而且不会删除文字的聊天记录,很好用,感觉很多人都用得到,就在此分享一下,而且是用 Python 写的,喜欢 Python 的小伙伴可以探究一下. 主要功能 它可以自动删除 PC 端微信自动下载的大量文件.视频.图片等数据内容,释放几十 G 的空间占用,而且不会删除文字的聊天记录,可以放心使用. 工作以后,微信的群聊实在太多了,动不动就被拉入一个群中,然后群聊里大

  • 基于Python编写一个图片识别系统

    目录 项目介绍 环境准备 程序原理 实现脚本 测试效果 总结 项目介绍 本项目将使用python3去识别图片是否为色情图片,会使用到PIL这个图像处理库,并且编写算法来划分图像的皮肤区域 介绍一下PIL: PIL(Python Image Library)是一种免费的图像处理工具包,这个软件包提供了基本的图像处理功能,如:改变图像大小,旋转图像,图像格式转化,色场空间转换(这个我不太懂),图像增强(就是改善清晰度,突出图像有用信息),直方图处理,插值(利用已知邻近像素点的灰度值来产生未知像素点的

  • 基于Python3编写一个GUI翻译器

    目录 1.引言 2.代码实战 2.1 思路 2.2 实战 3.总结 1.引言 小屌丝:鱼哥,你说百度翻译的准确,还是google翻译的准确? 小鱼:自己翻译的最准确. 小屌丝:你这… 抬杠. 小鱼:没有啊,英语自己就能翻译,还需要啥翻译软件 小屌丝:如果是俄语,意大利语,西班牙语呢? 小鱼:你这是…抬杠. 小屌丝:也没有啊,我就是觉得网页版翻译器太麻烦了. 小鱼:早说啊,我这有现成的翻译器. 小屌丝:嗯?? 你下载的是什么翻译器? 小鱼:你觉得我会下载吗? 小屌丝:嗯,确实,那就是说,你自己写了

随机推荐