CE MAPI实例讲解【http://heliboy110.blog.163.com/blog/s】

阅读更多

(一)如何获取Inbox里的所有消息

第一次写这玩意,感觉挺别扭,不过想想以前遇到问题的时候, 也从网络上得到过很多帮助,同时在CSDN,也看到有些网友在问关于MAPI的问题,自己虽然水平不怎么样,写东西也烂,但是觉的有必要将自己这几年积累 的东西同大家分享一下,希望对大家能有些帮助。我的写作能力不怎么样,这点接下来看我的文章就会有感觉,呵呵,所以我会尽量用代码来表达我的意思,对于一 些理论知识,我了解比较肤浅,有错误的地方,希望大家能够指正,谢谢先! 

      对于MAPI的意义,用处我就不多说了,大家可以去MSDN上找一下,我的英语烂,免的误导群众。接下来的例子为了减少篇幅,我将略去所有错误处理,但是在平时的使用过程中,希望使用者能加上详细的错误处理,这能使我们的程序更加健壮。

      来看第一个例子,如何获取tmail(就是我们熟称的收件箱程序)下各个Message Box下的消息。

      第一步:初始化MAPI:

  //session变量,在停止使用MAPI前,请不要释放它

  IMAPISession* m_pSession = NULL;   

  //COM初始化

  CoInitializeEx(NULL, COINIT_MULTITHREADED);  

  MAPIInitialize(NULL);

  MAPILogonEx(NULL, NULL, NULL, NULL, &m_pSession);

  

    退出MAPI

 if(m_pSession)

  {

  m_pSession->Logoff(0, 0, 0); 

   m_pSession->Release();

  m_pSession = NULL;

 }

  MAPIUninitialize();

  CoUninitialize();

      第二步:获取message store,对于message store,我认为它是管理message和folder的一个集合,它与收件箱下的各个帐户想对应,每个帐户对应有一个store,我们可以通过 session来遍历所有message store,每个store都会有一个DISPLAY NAME,通过它我们可以获得指定的store,下面演示如何获取SMS的Store:

  //IMAPITABLE,通过它我们可以访问数据,比如拿Store,拿Folder,拿Message等,方法都类似,下面会有介绍

  LPMAPITABLE pTable  = NULL;

  ULONG lCount   = 0;

  HRESULT hr  = 0;

  LPSRowSet pRows  = NULL;

  IMsgStore* pMsgStore = NULL;

  //希望获取多少属性,这里我们只需要获得两个属性

  ULONG ulNumCols  = 2;

  //通过下面的定义,我们告诉系统,我们需要获取以下两个属性,一个是Entry ID,通过它我们可以获取Store对象,一个是Display Name,我们用它来比较是不是我们需要的Store

  SizedSPropTagArray(ulNumCols , Columns) =

  { 

   ulNumCols , 

   PR_ENTRYID, //Store的Entry ID

   PR_DISPLAY_NAME //Store的Display Name

  };

  pSession->GetMsgStoresTable(MAPI_UNICODE , &pTable);

  pTable->SetColumns((LPSPropTagArray)&Columns, 0);

  while(SUCCEEDED(pTable->QueryRows(1, 0, &pRows)))

  {

   if (NULL == pRows || pRows->cRows != 1)

   {

       break;

   }

   //开始一条条记录查询,pRows->aRow[0].lpProps[0]代表了第一个查询属性,即Entry ID,pRows->aRow[0].lpProps[1]则表示Display Name。

   if (_tcsicmp(pRows->aRow[0].lpProps[1].Value.lpszW, _T("SMS")) == 0)

   {

    ULONG ulMesageType;

    //如果是SMS,则获取Store对象

    pSession->OpenEntry(pRows->aRow[0].lpProps[0].Value.bin.cb,

      (LPENTRYID)pRows->aRow[0].lpProps[0].Value.bin.lpb,

      NULL,

      MAPI_BEST_ACCESS,

      &ulMesageType,

      (LPUNKNOWN*)&pMsgStore);

    break;

   }

   

   FreeProws(pRows);

   pRows = NULL;

  }

  if(pRows)

  {

   FreeProws(pRows);

   pRows = NULL;

  }

 到此,就拿到了SMS的Message Store,累死了,休息一下,接下来让我们继续来获取Folder。在这里我先提醒一下,前面获取的IMsgStore和后面要讲的IMAPIFolder等对象,在不使用时候需要Release(),千万别忘记。

(二)如何获取Inbox里的所有消息

第三步:获取Folder。我们以Drafts为例。
 LPSPropValue rgprops  = NULL;
 ULONG ulValues   = 0;
 //设置我们想拿哪个Folder,详细可以查阅MSDN关于ImsgStore的介绍,在这里我将常用的几个Folder的属性值列一下:
 //PR_CE_IPM_DRAFTS_ENTRYID     drafts,草稿箱
 //PR_CE_IPM_INBOX_ENTRYID  Inbox,收件箱
 //PR_IPM_OUTBOX_ENTRYID  Outbox,发件箱
 //PR_IPM_SENTMAIL_ENTRYID  Sentbox,这个不知道叫什么好,寄件箱?就是已经发送的消息放的box.
 //PR_IPM_WASTEBASKET_ENTRYID  垃圾箱
 ULONG rgTags[]   = { 1, PR_CE_IPM_DRAFTS_ENTRYID}; 

 // 获取Folder的Entry ID,然后通过OpenEntry获得对象
 pMsgStore->GetProps((LPSPropTagArray) rgTags, MAPI_UNICODE, &ulValues, &rgprops);
 pMsgStore->OpenEntry(rgprops[0].Value.bin.cb, (LPENTRYID)rgprops[0].Value.bin.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)pFolder );
 MAPIFreeBuffer(rgprops);
 这样,我就拿到了需要的Folder,接下来还剩下最后一件事情,就是遍历message,其实它的过程和拿Store的过程十分相似。

第四步:获取message。
 LPMAPITABLE  pTable = NULL;
 LPSRowSet pRows = NULL;
 ULONG   ulNumCols = 1;
 //指定我们需要获取Entry ID属性
 SizedSPropTagArray(ulNumCols, Columns) =
 { 
  ulNumCols,
  PR_ENTRYID  
 };

 pFolder->GetContentsTable(0, &pTable);
 hr = pTable->SetColumns((LPSPropTagArray)&Columns, 0);
 //从这些代码可以看出,和获取Message Store的方法很相似,只不过一个是从Session对象里拿,而另一个是从Folder里面拿而已。

 while(SUCCEEDED(pTable->QueryRows(1, 0, &pRows)))
 {
  LPMESSAGE pMsg = NULL;
  ULONG ulMesageType;
  //通过OpenEntry获取IMessage对象
  pFolder->OpenEntry( pRows->aRow[0].lpProps[0].Value.bin.cb,
     (LPENTRYID)pRows->aRow[0].lpProps[0].Value.bin.lpb,
     NULL,
     MAPI_BEST_ACCESS,
     &ulMesageType,
     (LPUNKNOWN*)&pMsg);
  //这里拿到每个IMessage对象就代表了Folder里面的每一条Message,可以通过对它的操作来获取想要的信息。
  ......
  //不需要时记的释放
  pMsg->Release();
  FreeProws(pRows);
  pRows = NULL;
 }
 if(pRows)
 { 
  FreeProws(pRows);
 }
 if(pTable)
 {
  pTable->Release();
 }

(三)监视Message的状态改变

有时候,我们需要关心某条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而 已,功能还是很强大的,不过我个人对其他功能倒没有很深入的研究,就不做介绍了,如果有朋友做过类似方面的研究,请告知我下,也让我学习一下。

(五)如何设置收件人信息

 发件人和收件人是邮件和消息很常用的几个属性之一,关于发件人的设置和获取是很简单的,只需要处理PR_SENDER_EMAIL_ADDRESS属性即可,下面主要讲述的收件人的设置和获取。

       MAPI收件人结构如图(摘自MSDN):

       CE MAPI实例讲解【http://heliboy110.blog.163.com/blog/s】_第1张图片

 

       每一个Entry代表了一个收件人信息组,每个信息组又可以有多项信息组成,举个例子,下面的代码代表了一个收件人的信息:

       aEntries[0].rgPropVals[0].ulPropTag     = PR_RECIPIENT_TYPE;   //类型,MAPI_TO代表是设置到TO字段上的,相应的还有MAPI_CC和MAPI_BCC。

       aEntries[0].rgPropVals[0].Value.ul        = MAPI_TO;

 

       aEntries[0].rgPropVals[1].ulPropTag    = PR_ADDRTYPE;              //设置地址类型,一般为SMTP

       aEntries[0].rgPropVals[1].Value.LPSZ   = _T("SMTP");

 

       aEntries[0].rgPropVals[2].ulPropTag    = PR_EMAIL_ADDRESS;   //收件人地址

       aEntries[0].rgPropVals[2].Value.LPSZ = _T("1234567");

 

       设置收件人是通过IMessage:: ModifyRecipients来实现的,以下的代码举例说明了如何设置TO、CC和BCC属性:

       INT              nRecipientCount    = 3;        //表示有3个联系人信息

       INT               nListBufSize          = CbNewADRLIST(nRecipientCount);       //计算3个联系人需要的存储空间

       LPADRLIST   pAddressList         = NULL;

       MAPIAllocateBuffer(nListBufSize, (LPVOID FAR *)&pAddressList));            //分配空间

       memset(pAddressList, 0, nBufSize);           

 

       pAddressList->cEntries               = 3;        //表明一共有3个联系人信息

       //设置To

       INT nCurIndex     = 0;

       MAPIAllocateBuffer(sizeof(SPropValue) * 3, (LPVOID FAR *)&pAddressList->aEntries[nCurIndex].rgPropVals));      //分配空间       memset(pAddressList->aEntries[nCurIndex].rgPropVals, 0, sizeof(SPropValue) * 3);

       pAddressList->aEntries[nCurIndex].rgPropVals[0].ulPropTag           = PR_RECIPIENT_TYPE;

       pAddressList->aEntries[nCurIndex].rgPropVals[0].Value.ul               = MAPI_TO;               //表明是写到To

 

       pAddressList->aEntries[nCurIndex].rgPropVals[1].ulPropTag          = PR_ADDRTYPE;

       pAddressList->aEntries[nCurIndex].rgPropVals[1].Value.LPSZ = _T("SMTP");

 

       pAddressList->aEntries[nCurIndex].rgPropVals[2].ulPropTag           = PR_EMAIL_ADDRESS;

       pAddressList->aEntries[nCurIndex].rgPropVals[2].Value.LPSZ = _T("1234567");

 

       pAddressList->aEntries[nCurIndex].cValues = 3;        //表明改联系人有3个属性要设置

 

 

       //同上,现在设置CC

       nCurIndex            = 1;

       MAPIAllocateBuffer(sizeof(SPropValue) * 3, (LPVOID FAR *)&pAddressList->aEntries[nCurIndex].rgPropVals));      //分配空间       memset(pAddressList->aEntries[nCurIndex].rgPropVals, 0, sizeof(SPropValue) * 3);

       pAddressList->aEntries[nCurIndex].rgPropVals[0].ulPropTag           = PR_RECIPIENT_TYPE;

       pAddressList->aEntries[nCurIndex].rgPropVals[0].Value.ul               = MAPI_CC;               //表明是写到CC

 

       pAddressList->aEntries[nCurIndex].rgPropVals[1].ulPropTag          = PR_ADDRTYPE;

       pAddressList->aEntries[nCurIndex].rgPropVals[1].Value.LPSZ        = _T("SMTP");

 

       pAddressList->aEntries[nCurIndex].rgPropVals[2].ulPropTag           = PR_EMAIL_ADDRESS;

       pAddressList->aEntries[nCurIndex].rgPropVals[2].Value.LPSZ        = _T("7654321");

 

       pAddressList->aEntries[nCurIndex].cValues = 3;        //表明改联系人有3个属性要设置

 

       //同上,现在设置BCC

       nCurIndex            = 2;

       MAPIAllocateBuffer(sizeof(SPropValue) * 3, (LPVOID FAR *)&pAddressList->aEntries[nCurIndex].rgPropVals));      //分配空间       memset(pAddressList->aEntries[nCurIndex].rgPropVals, 0, sizeof(SPropValue) * 3);

       pAddressList->aEntries[nCurIndex].rgPropVals[0].ulPropTag           = PR_RECIPIENT_TYPE;

       pAddressList->aEntries[nCurIndex].rgPropVals[0].Value.ul               = MAPI_BCC;            //表明是写到CC

 

       pAddressList->aEntries[nCurIndex].rgPropVals[1].ulPropTag          = PR_ADDRTYPE;

       pAddressList->aEntries[nCurIndex].rgPropVals[1].Value.LPSZ         = _T("SMTP");

 

       pAddressList->aEntries[nCurIndex].rgPropVals[2].ulPropTag           = PR_EMAIL_ADDRESS;

       pAddressList->aEntries[nCurIndex].rgPropVals[2].Value.LPSZ         = _T("88888888");

       pAddressList->aEntries[nCurIndex].cValues = 3;        //表明改联系人有3个属性要设置  

 

       //调用ModifyRecipients添加联系人,完了记的释放申请的内存,pMsg为你想操作的Message的对象实例,关于如何获取可以参考以 前的文章。       pMsg->ModifyRecipients(MODRECIP_ADD, pAddressList)

 

 

       for(INT i = 0; i < nRecipientCount; i++)

              MAPIFreeBuffer(pAddressList->aEntries[i].rgPropVals);

 

       MAPIFreeBuffer(pAddressList);

(六)如何获取收件人信息

接下来开始讲讲如何获取联系人信息,它与设置信息比较相近,以下举例说明:

       IMAPITable* pTable = NULL;

     //通过GetRecipientTable获取联系人信息列表

     pMsg->GetRecipientTable( NULL, &pTable );

 

     LPADRLIST pRecipentRows          = NULL;

     //获取每个联系人信息,这里的做法可以看出和枚举Folder等都相似

     while(!FAILED(hr = pTable->QueryRows(1, 0, (LPSRowSet*)&pRecipentRows)))

     {

         if( pRecipentRows->cEntries == 0 )

              break;

 

         for(int n = 0; n < pRecipentRows->cEntries; n++ )

         {

              //每个Entry代表一个联系人信息,每个联系人信息又有多个属性组成

              for(int i = 0; i < pRecipentRows->aEntries[n].cValues ; i++)

              {

                   //判断如果是PR_EMAIL_ADDRESS属性,那么就找到了联系人地址

                   if( PR_EMAIL_ADDRESS == pRecipentRows->aEntries[n].rgPropVals[i].ulPropTag )

                   {

                       //联系人地址

                       CString strContact     = pRecipentRows->aEntries[n].rgPropVals[i].Value.lpszW;

                        //后续操作

                   }

              }

         }

         //完了记得要释放pRecipentRows和它里面的内容,释放方法见上一篇关于设置联系人信息的介绍。

         ……

     }

 

     上面的代码片段只简单演示了获取联系人信息的基本操作步骤,通过这个例子也可以熟悉IMAPITable的用法,MAPI里面还是有很多地方会用到这个接口,用处还是比较大的。

(七)设置Message附件

本篇主要介绍如何设置Message的附件内容,下一篇会介绍如何获取附件。长话短说,下面的例子将完成如下的事情:

1)  准备工作,在Temp目录下先放上几张图片,在这个例子里面,我在Temp目录放两张JPG图片,1.jpg,2.jpg,我将把这两张图片放到一个Message里面,生成两个附件。

2)  在Outlook草稿箱里面创建出一条新的Message。

3)  为Message添加附件。

 

如何在Outlook草稿箱里面创建一条新的 Message,我想通过前面的文章已经解释清楚了,这里就不罗嗦了,以下假设我们已经获取了IMessage*对象指针。首先提出一个帮助函 数:MAPIHelp_AddAttachment,该函数作用是为指定的Message添加指定文件作为附件,定义如下:

       BOOL          MAPIHelp_AddAttachment( IMessage* pMsg, LPCTSTR szFilePath, LPCTSTR szFileName );

       pMsg : Message目标对象指针

     szFilePath : 需要作为附件添加的文件全路径

     szFileName : 需要作为附件添加的文件名称,作为附件的名称

以下是函数具体实现:

     BOOL MAPIHelp_AddAttachment( IMessage* pMsg, LPCTSTR szFilePath, LPCTSTR szFileName )

     {

          if( NULL == pMsg || NULL == szFilePath )

               return FALSE;

 

          BOOL bRet          = FALSE;

          ULONG ulAttachNum  = 0;

          LPATTACH pAttach   = NULL;

          IStream* pStream   = NULL;

          HANDLE hFile       = NULL;

          SPropValue  rgpropsTo[1]  = {0};

          DWORD dwChunkSize  = 4096;

          DWORD dwSizeRead   = 0;

         //预备BUFFER,用来读写文件内容

          LPBYTE pData       = new BYTE[dwChunkSize];

          if( NULL == pData )

              return FALSE;

 

         //创建附件,返回IAttach对象,每个IAttach对象对应于一个附件, ulAttachNum是这个对象的标识,我们可以通过IMessage:: OpenAttach时传入这个ID来读取这个附件,具体的方法会在下篇时介绍。

          if( FAILED(pMsg->CreateAttach( NULL, NULL, &ulAttachNum, &pAttach )) )

              goto Exit;

    

         //设置附件名称

          rgpropsTo[0].ulPropTag      = PR_ATTACH_FILENAME;

          rgpropsTo[0].Value.lpszW    = (LPTSTR)szFileName;

          if( FAILED(pAttach->SetProps(1, rgpropsTo, NULL)) )

              goto Exit;

 

         //通过OpenProperty获取IStream对象,有了IStream对象,我们就可以读写数据。对于IAttach:: OpenProperty,CE上只支持PR_ATTACH_DATA_BIN属性。

          if( FAILED(pAttach->OpenProperty( PR_ATTACH_DATA_BIN, NULL, NULL, MAPI_MODIFY, (LPUNKNOWN *)&pStream )) )

              goto Exit;

 

         //下面部分是文件读写部分,从原始文件里读出数据,再写到附件里面去

          hFile                  = ::CreateFile( szFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

          if( INVALID_HANDLE_VALUE == hFile )

              goto Exit;

 

          while( ReadFile( hFile, pData, dwChunkSize, &dwSizeRead, NULL ) )

          {

              if( 0 >= dwSizeRead )

                   break;

              pStream->Write( pData, dwSizeRead, &dwSizeRead );

          }

 

          bRet               = TRUE;

     Exit:

         //完毕以后记的释放获取的对象。

          RELEASE_OBJ(pStream);

          RELEASE_OBJ(pAttach);

          DELETE_OBJ(pData);

          if( INVALID_HANDLE_VALUE != hFile )

              ::CloseHandle( hFile );

          return bRet;

}

 

有了上面的帮助函数,当我们想为一条Message添加附件时,可以按照如下调用:

     MAPIHelp_AddAttachment( pMsg, _T("\\Temp\\1.jpg"), _T("1.jpg") );

MAPIHelp_AddAttachment( pMsg, _T("\\Temp\\2.jpg"), _T("2.jpg") );

(八)读取Message附件

在上一篇里面讲述了如何为一条MESSAGE设置附件,下面将继续关于附件的话题,利用上一个例子,我们接下来来看看如何获取一条MESSAGE的附件信息。下面将通过两个帮助函数来完成:

       BOOL MAPIHelp_SaveAttachFile( LPATTACH pAttach, LPCTSTR szFile )

     作用:读取单个附件文件内容,并保存到指定位置

     pAttach: 附件对象

     szFile: 保存文件名

 

     BOOL MAPIHelp_GetAttachment( IMessage* pMsg, LPCTSTR szFilePath )

     作用:获取一条Message的全部附件,并保存到指定目录下

     pMsg: 目标消息对象

     szFilePath: 目标目录

 

     下面来看看具体实现:

     BOOL MAPIHelp_SaveAttachFile( LPATTACH pAttach, LPCTSTR szFile )

     {

          if( NULL == pAttach || NULL == szFile )

              return FALSE;

         HANDLE   hFile              = INVALID_HANDLE_VALUE;

          IStream* pstmAttachment     = NULL;

          char *    pBuffer            = NULL; 

          int      i                  = 0;

          DWORD    dwWrite            = 0;

          BOOL     bRet               = FALSE;

          ULONG    ulRead             = 0;

 

         //打开附件,获取IStream对象,用于获取文件内容,根据MSDN的解释,这里只支持PR_ATTACH_DATA_BIN属性。

          if(FAILED(pAttach->OpenProperty (PR_ATTACH_DATA_BIN, NULL, STGM_READ, MAPI_MODIFY,

                                         reinterpret_cast (&pstmAttachment))))

          {

              goto EXIT;

          }   

         //创建目标文件

          hFile = ::CreateFile(szFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

         if(INVALID_HANDLE_VALUE == hFile)

          {

              goto EXIT;

          }   

    

         //缓冲区,用于文件拷贝

          pBuffer  = new char[4096];

          if(NULL == pBuffer)   

          {

              goto EXIT;

          }

 

         //附件内容拷贝

          while(SUCCEEDED(pstmAttachment->Read(pBuffer, 4096, &ulRead)))

          {

              if(ulRead <= 0)

                   break;

              ::WriteFile(hFile, pBuffer, ulRead, &dwWrite, NULL);

          }

         bRet     = TRUE;

     EXIT:

          if(INVALID_HANDLE_VALUE != hFile)

          {

              ::CloseHandle(hFile);

          }

你可能感兴趣的:(Blog,数据结构,Access,J#)