(三)监视Message的状态改变

本文介绍如何使用IMAPIAdviseSink接口监测邮件系统的状态变化,包括消息移动、修改及删除等通知。通过实例演示如何注册及取消注册AdviseSink,并解析OnNotify函数中的Notification类型及其应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有时候,我们需要关心某条message的改变,需要做出及时的响应,我们当然不能主动的不断的QUERY MESSAGE的状态,好在系统提供了IMAPIAdviseSink,通过它我们可以获得Message移动、改变以及删除等等通知。

     首先我们要做的是实现自己的IMAPIAdviseSink接口,原型是:

       class CAdviseSink : public IMAPIAdviseSink

     {

         public:

              CAdviseSink();

              ~CAdviseSink();

              MAPIMETHOD_(ULONG,OnNotify)(ULONG cNotif, LPNOTIFICATION lpNotifications);

              MAPIMETHOD(QueryInterface)(REFIID iid, void** ppvObject);

              MAPIMETHOD_(ULONG, AddRef)();

              MAPIMETHOD_(ULONG, Release)();

 

         private:

              ULONG              m_cRef;

     };

       我们主要关注的是OnNotify,其他函数我们可以按照标准实现就可以了。我们先来看看OnNotify的一个简单的实现,关键地方我加了注释:

     ULONG CAdviseSink::OnNotify(ULONG cNotif, LPNOTIFICATION lpNotifications)

     {       

         // cNotif : 指定有多少个Notification通知

         // lpNotifications : Notification数组,个数为cNotif

          for(int i = 0; i < (int)cNotif; ++i)

         {

              //根据不同的Notification类型做不同的处理,类型有很多种,这里只是简单的列出的几种,要获取这些通知和注册AdviseSink密切相关,你需要告诉系统,你关心哪些方面的消息,比如消息的移动,删除等等,系统就会把这些相应的通知发给你,而其他你不关心的,就不会通知到你,这些我们会在后面注册部分讲到。

              switch(lpNotifications[i].ulEventType)

              {

                   case fnevObjectMoved:

                        break;

                   case fnevObjectModified:

                        break;  

                   case fnevObjectDeleted:

                        break;

                   default:

                       break;  

              }

         }

         return 0;

     }   

    

     接下来是注册AdviseSink,它与每个Account的Store相对应,比如SMS、OUTLOOK等等。以下是注册步骤:

A.        获取要监视的Message Store对象,从前面的文章里的我们已经知道如何获得指定的Message Store,这里我们拿SMS的Store来举例。

B.        创建我们自己的CAdviseSink对象

C.        调用IMsgStore::Advise注册

以下是注册示例代码:

IMsgStore* pMsgStore   = …… //获取SMS Message Store

CAdviseSink* g_pAdviseSink = new CAdviseSink();

ULONG m_ulAdviseSink   = 0; //用来标识AdviseSink,当取消注册时我们需要用到它。

// uEventMask : 它的作用是告诉系统,我们关心哪些方面的notification,没有列出来的事件在CAdviseSink::OnNotify里面就不会响应到。

ULONG uEventMask = fnevCriticalError | fnevNewMail | fnevObjectCreated | fnevObjectDeleted |

              fnevObjectModified | fnevObjectMoved | fnevObjectCopied | fnevSearchComplete | fnevTableModified |      

              fnevStatusObjectModified | fnevReservedForMapi | fnevExtended;

pMsgStore->Advise(0, NULL, uEventMask, g_pAdviseSink, &m_ulAdviseSink);

这样就注册成功了。

 

以下是取消注册的示例代码:

if(m_ulAdviseSink)

{

     pMsgStore->Unadvise(m_ulAdviseSink);

}

//记的释放对象

if(g_pAdviseSink)

{

     delete g_pAdviseSink;

     g_pAdviseSink = NULL;

}

(四)IMAPIAdviseSink的一个例子

AdviseSink对于我们了解系统SMS以及OUTLOOK的消息运作有很大帮助,我们可以挂接到SMS、OUTLOOK的Message Store上,看看在做某些操作时,系统到底对Message做了些什么。下面我举个一个例子来说明它的用途:

不知道大家有没有注意,在Smartphone上的Deleted Box里面有个按钮叫Restore,即恢复功能,如果是你用系统菜单把一条Message删除到Deleted Box的话,Restore可以正常工作,但是如果你用MAPI去操作呢?比如IMAPIFolder::CopyMessages,你可以试一下,这个时候Restore失效了,由此可见系统在用菜单删除Message的时候自己做了些手脚,那我们怎么知道它具体做了哪些事情呢?这个时候IMAPIAdviseSink就派上用场了,不过在这之前先简要介绍一下MAPI属性,我们可以看一下mapitags.h中关于属性的定义,非常明显,一个属性由类型和ID两个部分通过PROP_TAG宏作用生成。类型代表这个属性的值是以什么形式存放在数据库里面的,这关系到你能否正确读写该属性,比如我们在调用IMessage:: GetProps时候,返回了结构体SPropValue:

     typedef union _PV

     {

         short int           i;          /* case PT_I2 */

         LONG                l;          /* case PT_LONG */

         ULONG               ul;         /* alias for PT_LONG */

         float               flt;        /* case PT_R4 */

         double              dbl;        /* case PT_DOUBLE */

         unsigned short int b;          /* case PT_BOOLEAN */

         CURRENCY            cur;        /* case PT_CURRENCY */

         double              at;         /* case PT_APPTIME */

         FILETIME            ft;         /* case PT_SYSTIME */

         LPSTR               lpszA;      /* case PT_STRING8 */

         SBinary             bin;        /* case PT_BINARY */

         LPWSTR              lpszW;      /* case PT_UNICODE */

         LPGUID              lpguid;     /* case PT_CLSID */

         …….

     } __UPV;

 

     typedef struct _SPropValue

     {

         ULONG       ulPropTag;

         ULONG       dwAlignPad;

     union _PV   Value;

} SPropValue, FAR * LPSPropValue;

看到union _PV了吗,我们如何知道该取哪个值来用呢?这就得通过属性的类型来告诉我们了,我们可以通过宏PROP_TYPE来获取一个属性的类型,通过PROP_ID来获取ID。

 

好了,下面是OnNotify实现:

     ULONG CAdviseSink::OnNotify(ULONG cNotif, LPNOTIFICATION lpNotifications)

     {       

          if(cNotif > 0 && NULL != lpNotifications)

          {

              for(int i = 0; i < (int)cNotif; ++i)

              {

                   switch(lpNotifications[i].ulEventType)

                   {

                       //Message 发生了改变,这时lpNotifications[i].info.obj.lpPropTagArray就代表了发生改变的属性值列表。

                       case fnevObjectModified:

                       {

                            // iPropCount : 表示一共有多少个属性发生了改变

                            int iPropCount         = lpNotifications[i].info.obj.lpPropTagArray->cValues;

                            for(int j = 0; j < iPropCount; j++)

                            {

                                 // ulPropType : 属性类型,即PT_LONG,PT_DOUBLE,PT_BINARY等等,定义可以在mapidefs.h里面找到

                                 // ulPropID :   属性ID,可以在mapitags.h里面找到

                                 // 在此处打上断点或者打上日志,我们就可以知道在系统删除Message时,对它到底做了什么。

                                 ULONG ulPropType        = PROP_TYPE(lpNotifications[i].info.obj.lpPropTagArray->aulPropTag[j]);

                                 ULONG ulPropID         = PROP_ID(lpNotifications[i].info.obj.lpPropTagArray->aulPropTag[j]);                            }

                            break;

                        }

                       default:

                            break;                

                   }

               }

          }   

          return 0;

}

 

     按照上一篇介绍的方法,把它挂到SMS Store上,启动调试状态,等待Modified事件发生。然后到系统收件箱中创建一条新的SMS,并且删除它,前面新建时的通知我们不关心,当删除时OnNotify一共会收到3次通知消息,认真观察,第一次类型为3(PT_LONG),ID为13827(PR_CONTENT_UNREAD),第二次也一样,第三次呢,类型为258(PT_BINARY),ID为34072,恩?这并不是标准的属性,从SDK上看从0x8000到0xFFFE应该是用户自定义属性区域,可见这是MS在实现SMS程序时自己添加的一个属性,做什么用呢?类型是PT_BINARY,会不会是这条Message原始所在Box的EntryID呢?把它的数据打印出来和Draft Box的EntryID一比较,果然一模一样,这下明白了,MS在这里做了个手脚,在它自己的程序里面删除MESSAGE时,会添加一个自定义的属性,并把MESSAGE原始所在BOX的EntryID写进去,等到点Restore时,就读取这个值,把MESSAGE恢复回去,我们利用MAPI操作的MESSAGE没有置这个属性,当然不起作用了,所以我们所要做的只是在CopyMessage之前,把这个属性写上而已,这时再试一把,OK,搞定。

     这只是一个简单的例子,AdviseSink对于平时对MAPI的调试很有帮助,从注册时的那一堆标志就可以看出,它不仅仅是用来监视Message而已,功能还是很强大的,不过我个人对其他功能倒没有很深入的研究,就不做介绍了,如果有朋友做过类似方面的研究,请告知我下,也让我学习一下。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值