`
guowee
  • 浏览: 173463 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

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):

      

 

       每一个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 <IUnknown **> (&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);

          } </

分享到:
评论

相关推荐

    最新支付宝接口(asp-支持UTF8、GB2312)

    支付宝接口签约地址(免费):... 这个接口UTF-8,GB2312编码都通用。(支付宝官方的只发出了GB2312版的。。)  用法很简单,直接调用类就可以了。 详细资料请查看“使用说明(必看).txt”,默认编码UTF-8

    网站对接支付宝即时到账接口步骤

    3:在代码里拼装好接口需要的参数,发送一个post请求到支付宝网关(https://mapi.alipay.com/gateway.do)就OK 4:支付宝响应包含同步和异步方式(按时间段最多6次,称为最大补偿策略),同步方式可以在你本地调试...

    支付宝即时到账系统java程序开发包

    String paygateway = "https://www.alipay.com/cooperate/gateway.do?"; //'支付接口 String service = "create_direct_pay_by_user";//快速付款交易服务 String sign_type = "MD5"; String out_trade_no = Now_...

    CommAPI.CHM

    CommAPI CHM 文档 Class Hierarchy class java lang Object interface javax comm CommDriver class javax comm CommPort class javax comm ParallelPort class javax comm SerialPort class javax comm ...

    workflow.part1.rar

    MAPI–compliant mail applications. 与workflow.part1.rar、workflow.part3.rar 一起下载放到同一目录下解压。 下载后保存的名字必须为workflow.part1.rar、workflow.part2.rar、workflow.part3.rar workflow....

    workflow.part2.rar

    MAPI–compliant mail applications. 与workflow.part1.rar、workflow.part3.rar 一起下载放到同一目录下解压。 下载后保存的名字必须为workflow.part1.rar、workflow.part2.rar、workflow.part3.rar workflow....

    workflow.part3.rar

    MAPI–compliant mail applications. 与workflow.part1.rar、workflow.part3.rar 一起下载放到同一目录下解压。 下载后保存的名字必须为workflow.part1.rar、workflow.part2.rar、workflow.part3.rar workflow....

    CE MAPI实例讲解

    CE MAPI实例讲解

    Microsoft Visual Studio

    可再发行编码 - 有限使用: msjet35.dll msjint35.dll msjter35.dll msrd2x35.dll msrepl35.dll expsrv.dll vbajet32.dll msexch35.dll msexcl35.dll mspdox35.dll msltus35.dll mstext35.dll msxbse35.dll ...

    支付宝单笔订单检索接口

    支付宝单笔订单检索接口,https://mapi.alipay.com/gateway.do single_trade_query

    翼支付付款码加入demo

    翼支付付款码接入demo java项目 这里面只有部分逻辑 请慎重下载

    outlookspy

    请叫我雷锋,官网上直接下载的,链接 http://www.dimastr.com/outspy/download.htm 也可以百度 outlookspy http://www.dimastr.com/outspy/download.htm

    ECSHOP专用支付宝会员免注册登录接口

    这个服务本来是需要花费3600元/年 购买付宝的“创业版”套餐才有的功能,现在我们已经申请了该套餐,可以为广大的网店集成该服务...演示效果:http://www.ziyezi.com/alipay/ 注:这是演示文件,如有需要请联系我们。

    如何批量替换相对地址为绝对地址(利用bat批处理实现)

    如果你的url链接是相对路径“static/mapi.css”,你想把他批量替换成绝对路径“http://dev.baidu.com/wiki/static/map/cloud/static/mapi.css”。那么,你可以这样做: 写一个PHP文件,把需要替换的网址写进去。 这...

    \Program Files\Microsoft Platform SDK for Windows Server 2003 R2 LIB文件

    2006-03-03 23:23 34,778 MAPI32.Lib 2006-03-03 23:23 3,444 MgmtAPI.Lib 2006-03-03 23:23 197,976 MiniDump.Lib 2006-03-03 23:23 2,260 MobSync.Lib 2006-03-03 23:23 11,810 Mpr.Lib 2006-03-03 23:23 11,346 ...

    MfcMapi 程序和源代码

    http://mfcmapi.codeplex.com/ mfcmapi是一个查看mapi(outlook和exchange的协议) 对象的使用最广泛的开源工具。 exe是6.0.0.1016 src是46662 46662 by sgriffin Nov 18 2009 at 12:35 AM 479 Build 6.0.0.1016

    VB网络编程实例

    &lt;br/&gt;◆ 88.htm 用VB创建自己的通信程序 &lt;br/&gt;◆ 89.htm 用VB构建Internet的应用[微软提供] &lt;br/&gt;◆ 90.htm 用VB开发标准CGI程序 &lt;br/&gt;◆ 91.htm 用VB实现客户——服务器(TCP IP)编程实例 &lt;br/&gt;◆ 92.htm 用VB实现...

    EurekaLog_7.5.0.0_Enterprise

    16)..Added: Support for environment variables in project option's paths 17)..Added: Support for relative file paths and environment variables for events and various module paths 18)..Added: Logging ...

    winmailtest:Win Mail Tester,用于在Windows上测试MAPI和Outlook

    :closed_mailbox_with_raised_flag: Win Mail测试仪 Win Mail Tester是一个简单的程序,可以在用C ++编写的Windows中测试MAPI和Outlook功能。

    C语言发送电子邮件正文和附件

    用MAPI 发送电子邮件 正文内容和附件。 运行程序前,注意要配置outlookExpress. 具体配置,请参考http://www.bcty365.com/looksetupstudy.php?id=474

Global site tag (gtag.js) - Google Analytics