最新WPS办公软件学习教程_免费企业办公软件下载_金山WPS Office官网论坛

 找回密码
 
查看: 854|回复: 62

[C++] 一步步进行WPS插件开发(C++):使用事件

  [复制链接]

2

主题

15

听众

1277

积分

版主

Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

该用户从未签到

金币
5
威望
2222
帖子
258
精华
2

测试体验团

发表于 2014-3-23 10:06 |显示全部楼层
分享到: 新浪微博 腾讯微博
本帖最后由 卡卡尼莫 于 2014-3-23 13:31 编辑

注:1.本人配置:XP+VS2008 SP1+Visual Assist X +WPS Office 2013个人版  (9.1.0.4468),本文假定读者有一定的C++基础;
       2.WPS扩展可分为两种,其一为WPS插件,其二为COM加载项,本人习惯上都称为插件,后面提到的插件均指COM加载项。

一.了解事件
    Windows提供了事件驱动编程的方法。这些事件包括:单击按钮、改变窗口大小、改变文本框中的项等。我们编写的代码可以响应这些事件。用走廊的示例作类比就是:在Windows程序中,要到达走廊的终点,只需单击一下终点就行了,而不用考虑这个走廊。如果到达了终点,却发现这不是我们想去的地方,那么可以直接奔向新的目的地,而不用返回起点。程序会对这个动作作出反应,并且执行必要的动作来完成指定的任务。
    ET表格能够监控各种事件,并且能在一个特殊的事件发生时执行相应的程序代码。
    在ET表格中,为了方便程序员对事件进行处理,常用ET中的事件有二类:
    1. 菜单操作事件:如新建、保存等等按钮点击事件。大家懂得
    2. ET对象事件,有以下几个:
    (1)Application对象事件:会影响到所有打开的工作簿。我们可以通过Application对象设置事件,对所有打开的工作簿进行控制。
    (2)Workbook对象事件:会影响到所有工作簿内的工作表,利用Workbook对象事件,我们可以很方便的对工作簿或者各个工作表进行管理和操作。
    (3)Worksheet对象事件:可以对工作簿内的每个工作表实施不同的操作,从而使得工作表的管理更加灵活和高效率。
注意:ET表格捕获某个事件之后,会执行事件处理程序。在进行处理时,要遵循一定的原则:
    (1)不要依赖事件发生的顺序。
    (2)不要依赖操作的顺序。
    (3)不要从事件处理程序中启动对话框。
    (4)不要从事件处理程序中执行任何触发相同事件的操作。

二.常用的应用程序事件(Application)
应用程序事件用来响应应用程序窗口以及系统变量的更改,应用程序事件在wps表格关闭或事件被注销之前,是一直存在的。Application应用程序事件会影响到所有打开的工作簿(因为)。下表为Application应用程序事件及其简要描述:

             常用的Application应用程序事件
事件
触发事件的描述
WorkbookOpen
当工作簿被打开时执行程序
WorkbookBeforeClose
当工作簿被关闭时执行程序
WorkbookBeforeSave
当工作簿被保存时执行程序
WorkbookActivate
当工作簿成为活动工作簿时执行程序
SheetActivate
当激活工作表时执行程序
SheetSelectionChange
当单元格的选择范围变更时执行程序
SheetChange
当任何单元格的值变更时执行程序
SheetBeforeDoubleClick
当双击工作表时执行程序
SheetBeforeRightClick
当右键单击工作表时执行程序
WorkbookNewSheet
当新建工作表时执行程序
WindowActivate
当任意工作簿窗口被激活时执行程序
WindowDeactivate
当工作簿窗口不是活动窗口时执行程序
WindowResize
当活动工作簿窗口被变更时执行程序
NewWorkbook
当新建工作簿时执行程序
WorkbookBeforePrint
当工作簿被打印时执行程序
WorkbookCancelPrint
当工作簿打印被取消时执行程序
SheetCalculate
当工作表计算(或重新计算)时执行程序
    ps:它们都是_ApplicationEvents类的成员,类的声明及实现在etapiv8.tlh、etapiv8.tli中能找到。工作簿事件(Workbook)是_WorkbookEvents类的成员,类的声明及实现在etapiv8.tlh、etapiv8.tli中能找到,工作表事件(Worksheet)是_WorksheetEvents类的成员,类的声明及实现在etapiv8.tlh、etapiv8.tli中能找到.
三.如何使用事件
     通过上面的介绍,大家应该对事件了解了大概,那接下来进入正题,到底如何使用事件。需要以下4个步骤:
    1.继承CComObjectRootEx、CComCoClass、IDispEventSimpleImpl
    2.添加事件映射链
    3.编写事件处理函数  
    4.连接及断开事件与事件处理函数
    那我们来一步步实现吧。
    1.继承CComObjectRootEx、CComCoClass、IDispEventSimpleImpl
    IDispatch是由OLE自动化协议暴露出来的接口。它是COM对象可以暴露出来的标准接口(Interface)之一,事件响应是基于IDispatch的,为了让我们编写的类暴露这个接口,它必须是个COM对象。CComObjectRootEx类CComCoClass共同使我们编写的类成为一个COM对象。
    IDispEventSimpleImpl提供了IDispatch接口的实现和建立连接点所需的处理函数,当事件发生时IDispEventSimpleImpl则调用我们想要接收的事件的处理函数。
    IDispEventSimpleImpl的模板参数是事件的ID,我们的类名和连接点接口的IID。事件ID可以是任意正数,假设我们连接点对象为Application对象事件,则连接点对象的IID是&__uuidof(ET::_ApplicationEvents),具体可以查看生成的etapiv8.tlh。

class ApplicationEvents :
        public CComObjectRootEx,
        public CComCoClass,
        public IDispatchImpl<_IDTExtensibility2, &IID__IDTExtensibility2, &LIBID_AddInDesignerObjects>,
        public IDispEventSimpleImpl<1, ApplicationEvents, &DIID__CommandBarButtonEvents>,
        public IDispEventSimpleImpl<2, ApplicationEvents, &DIID__CommandBarButtonEvents>,
        public IDispEventSimpleImpl<3, ApplicationEvents, &__uuidof(ET::_ApplicationEvents)>

这里我演示下按钮点击事件Application事件,上面代码中有两个按钮事件,我用来通过点击连接及取消Application事件的。详情可见后面附的源代码。
   2.添加事件映射链
    这个事件映射链将我们感兴趣的事件和我们的处理函数联系起来。这里举个例子:Application对象的SheetActivate事件,当激活工作表时就会触发这个事件,我们响应这个事件。
    我们先来看下SheetActivate事件的原型:
  1. HRESULT SheetActivate (
  2.          IDispatch * Sh );
复制代码
    这个事件需要一个参数,IDispatch * Sh,不需要返回值。为了将这个事件的原型转换成事件响应链,我们需要写一个_ATL_FUNC_INFO结构,它包含返回值,参数的个数和参数类型。由于事件是基于IDispatch的,所以所有的参数都用VARIANT表示,这个数据结构的描述相当长(支持很多个数据类型),以下是常用的几个:
    VT_EMPTY: void
    VT_BSTR: BSTR 格式的字符串
    VT_I4: 4字节有符号整数,用于long类型的参数
    VT_DISPATCH: IDispatch*
    VT_VARIANT>: VARIANT
    VT_BOOL: VARIANT_BOOL (允许的取值是VARIANT_TRUE和VARIANT_FALSE)

    另外,标志VT_BYREF表示将一个参数转换成相应的指针。例如,
    VT_VARIANT|VT_BYREF表示VARIANT*类型。下面是_ATL_FUNC_INFO的定义:
  1. #define _ATL_MAX_VARTYPES 8   
  2. struct _ATL_FUNC_INFO {
  3.     CALLCONV cc;
  4.     VARTYPE  vtReturn;
  5.     SHORT    nParams;
  6.     VARTYPE  pVarTypes[_ATL_MAX_VARTYPES]; };
复制代码
    参数:
     cc  
    我们的事件响应函数的调用方式约定,这个参数必须是CC_STDCALL,表示是__stdcall方式  
    vtReturn  
    事件响应函数的返回值类型  
    nParams  
    事件带的参数个数  
    pVarTypes  
    相应的参数类型,按从左到右的顺序  
    了解这些之后,我们就可以填写SheetActivate事件处理的_ATL_FUNC_INFO结构:
    调用方式为CC_STDCALL,事件响应函数的返回值类型为void ,则为VT_EMPTY,事件带参数一个,类型为IDispatch* ,对应的就是VT_DISPATCH。连在一起如下:
  1. _ATL_FUNC_INFO SheetActivateInfo = { CC_STDCALL, VT_EMPTY, 1,VT_DISPATCH};
复制代码
同样,根据上面的分析,得到按钮点击事件的映射链
  1. _ATL_FUNC_INFO OnClickButtonInfo =
  2. {
  3.         CC_STDCALL,
  4.         VT_EMPTY,
  5.         2,
  6.         { VT_DISPATCH, VT_BYREF | VT_BOOL }
  7. };
复制代码
提示:SheetActivate事件的原型可以在etapiv8.tlh中找到,含在_ApplicationEvents : IDispatch中
    现在,回到事件响应链,我们为每一个我们想要处理的事件添加一个SINK_ENTRY_INFO,下面是处理SheetActivate事件的宏:

class ApplicationEvents :
     public ... { ...
        BEGIN_SINK_MAP(ApplicationEvents)
                SINK_ENTRY_INFO(1, DIID__CommandBarButtonEvents, 0x01, OnClickButton1, &OnClickButtonInfo)
                SINK_ENTRY_INFO(2, DIID__CommandBarButtonEvents, 0x01, OnClickButton2, &OnClickButtonInfo)
                SINK_ENTRY_INFO(3, __uuidof(ET::_ApplicationEvents),0x113005, SheetActivate, &SheetActivateInfo)
        END_SINK_MAP()


    这个宏的第一个参数“3”是事件的ID,这个ID与我们在IDispEventSimpleImpl的继承列表中使用的ID一样,第二个函数“ __uuidof(ET::_ApplicationEvents)”是事件接口的IID,“0x113005”是事件的dispatch ID,“SheetActivate”是事件处理函数的名字,“&SheetActivateInfo”为指向描述这个事件处理的_ATL_FUNC_INFO结构的指针。
    提示:事件的dispatch ID可以在etapiv8.tli中找到,如下:    inline HRESULT ET::_ApplicationEvents::SheetActivate ( IDispatch * Sh ) {        return _com_dispatch_method(this, 0x113005, DISPATCH_METHOD, VT_EMPTY, NULL,             L"\x0009", Sh);        }
    3.编写事件处理函数     
    好了,等了这么长时间,我们可以写事件处理函数了:
  1.     void __stdcall SheetActivate(IDispatch * Sh)
  2.     {......
  3. }
复制代码
    提示:事件处理函数的返回值及原型应和事件原型保持一致:
    4.连接及断开事件与事件处理函数   
    首先给事件定义个简单的名字SheetsActivate2,使用typedef,便于我们程序中调用:

        typedef IDispEventSimpleImpl<1, ApplicationEvents, &DIID__CommandBarButtonEvents>        CommandBarButtonEvents1;
        typedef IDispEventSimpleImpl<2, ApplicationEvents, &DIID__CommandBarButtonEvents>        CommandBarButtonEvents2;
        typedef IDispEventSimpleImpl<3, ApplicationEvents, &__uuidof(ET::_ApplicationEvents)> SheetsActivate2;
然后就连接:
  1. CommandBarButtonEvents1::DispEventAdvise(m_spButton1);
  2. CommandBarButtonEvents2::DispEventAdvise(m_spButton2);
复制代码
断开代码如下:
  1. CommandBarButtonEvents1::DispEventUnadvise(m_spButton1);
复制代码

    提示:1.m_spETApp为Application;可在运行OnConnection时赋值;              
              2.事件连接一次即可,如连续两次连接就会出错,使用try语句也会调试错误。
     --------------------------------------------------------------------------------------------
    下面老规矩,上源代码,源码介绍:启动ET即在菜单上生成个菜单两个按钮,一个连接、一个断开事件SheetsActivate事件。点击连接事件后,切换下面工作表的时候就会在表格中显示工作簿BookXX的工作表SheetXX激活了.用来演示下SheetsActivate事件。注意运行源代码前需要先注册你的dll插件哦,否则ET不能加载哦



本帖子中包含更多资源

您需要 登录 才可以下载或查看,

已有 3 人评分威望 收起 理由
松风水月 + 16 赞一个!
zhouyiran1@126.com + 16 赞一个!
轩少 + 12 WPS有你更精彩!

总评分: 威望 + 44   查看全部评分

To share my knowledge with the world
我的新浪博客
我的百度博客

2

主题

15

听众

1277

积分

版主

Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

该用户从未签到

金币
5
威望
2222
帖子
258
精华
2

测试体验团

发表于 2014-3-23 13:35 |显示全部楼层
本帖最后由 卡卡尼莫 于 2014-3-23 13:37 编辑

代码改了几次发现总有问题,上面代码中需要修改如下
&改为就留最前面那个符号
<改为左边尖括号
> 改为右边尖括号
一直以为是我编辑出错,后来太发现是这个论坛坑爹,编辑时候所见和编辑完的不一样
To share my knowledge with the world
我的新浪博客
我的百度博客
回复

使用道具 举报

53

主题

60

听众

6715

积分

版主

Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

签到天数: 126 天

[LV.7]常住居民III

金币
634
威望
13773
帖子
3973
精华
2

WPS粉丝团 技术分享团 乐于助人奖 技巧教程分享达人 优秀会员奖 活跃会员奖 测试体验团 版主勋章 勤奋版主奖 最佳教程奖 测试体验官 优秀模板奖

发表于 2014-3-23 20:12 |显示全部楼层

感谢分享,支持楼主

点评

卡卡尼莫  感谢大佬支持  发表于 2014-3-24 11:01

点击了解最新动态:【轩少】__实用教程索引(2015-9-6更新)
http://bbs.wps.cn/forum.php?mod= ... amp;fromuid=2404273
回复

使用道具 举报

153

主题

110

听众

1万

积分

解答支持团长

老菜鸟

Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

签到天数: 240 天

[LV.8]以坛为家I

金币
1254
威望
25965
帖子
10314
精华
1

活跃会员奖 解答支持团 测试体验团 乐于助人奖 优秀会员奖

发表于 2014-3-23 21:06 |显示全部楼层
厉害!
佩服。

点评

卡卡尼莫  感谢团长捧场  发表于 2014-3-24 11:01
回复

使用道具 举报

19

主题

111

听众

5338

积分

技术分享团长

Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

签到天数: 5 天

[LV.2]偶尔看看I

金币
5316
威望
11825
帖子
2334
精华
1

WPS粉丝团 荣誉版主奖 活跃会员奖 技术分享团 解答支持团 重阳节勋章

发表于 2014-3-24 10:34 |显示全部楼层
感谢卡卡!学习了!
把什么是WPS插件、什么是事件、常用的事件、如何使用事件都一一介绍得如此详细和明了,要学习插件开发的童鞋看到了,一定会得到很大帮助!

点评

卡卡尼莫  哈哈,感谢团长支持  发表于 2014-3-24 11:02
回复

使用道具 举报

0

主题

0

听众

12

积分

LV.1

Rank: 1

该用户从未签到

金币
0
威望
21
帖子
0
精华
0
发表于 2014-6-5 14:37 |显示全部楼层
设置下载要积分真要命~
回复

使用道具 举报

0

主题

0

听众

12

积分

LV.1

Rank: 1

该用户从未签到

金币
0
威望
21
帖子
0
精华
0
发表于 2014-6-5 14:38 |显示全部楼层
再回一贴,威望不够
回复

使用道具 举报

0

主题

0

听众

34

积分

LV.1

Rank: 1

该用户从未签到

金币
0
威望
7
帖子
2
精华
0
发表于 2014-6-6 17:58 |显示全部楼层
谢谢楼主,继续支持
回复

使用道具 举报

0

主题

0

听众

34

积分

LV.1

Rank: 1

该用户从未签到

金币
0
威望
7
帖子
2
精华
0
发表于 2014-6-7 13:49 |显示全部楼层
谢谢楼主,继续支持
回复

使用道具 举报

2

主题

0

听众

12

积分

LV.1

Rank: 1

该用户从未签到

金币
0
威望
8
帖子
0
精华
0
发表于 2014-6-9 15:32 |显示全部楼层
我顶。。。。。。。。。。。。。。。。。。。
回复

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 更多账号登录:

快速回帖:

fastpost

WPS论坛更新日志|WPS Office官方论坛 ( 粤公网安备 44049102496073号 粤ICP备13015957号-1   

GMT+8, 2018-1-20 07:32

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc.

回顶部