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

发件人和收件人是邮件和消息很常用的几个属性之一,关于发件人的设置和获取是很简单的,只需要处理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);

          }

          if(NULL != pBuffer)

          {

              delete []pBuffer;

          }

          if(NULL != pstmAttachment)

          {

              pstmAttachment->Release();

          }

          return bRet;

     }

 

     BOOL MAPIHelp_GetAttachment( IMessage* pMsg, LPCTSTR szFilePath )

     {

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

              return FALSE;

 

          LPMAPITABLE pAttachTbl      = NULL;     

          SRowSet* psrs               = NULL;

          LPATTACH pAttach            = NULL;

          LONG     lAttachNum         = 0;

          BOOL     bRet               = FALSE;

    

         //获取附件列表

          if(FAILED(pMsg->GetAttachmentTable(0, &pAttachTbl)))

          {

              goto EXIT;

          }       

 

         //接下来的查询过程是不是很眼熟?

          while(SUCCEEDED(pAttachTbl->QueryRows (1, 0, &psrs)))

          {

              //即使查询返回成功,可能记录数也为0,需要排除这种CASE

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

              {

                   break;

              }

              TCHAR szFile[MAX_PATH];

              //遍历所有属性,找出附件ID和名称

              for(int i = 0; i < (int)(psrs->aRow[0].cValues); ++i)

              {

                   if(PR_ATTACH_NUM == psrs->aRow[0].lpProps[i].ulPropTag)              

                   {

                       //找到附件ID,并打开附件对象        

                        if(FAILED(pMsg->OpenAttach(psrs->aRow[0].lpProps[i].Value.l,

                                                   NULL,

                                                   MAPI_BEST_ACCESS,

                                                   &pAttach)))                

                        {

                            goto EXIT;

                        }  

                        lAttachNum = psrs->aRow[0].lpProps[i].Value.l;

                   }

                   else if(PR_ATTACH_FILENAME == psrs->aRow[0].lpProps[i].ulPropTag)

                   {

                       //获取附件名称,生成保存路径

                        _stprintf( szFile, _T("%s%s"), szFilePath, psrs->aRow[0].lpProps[i].Value.lpszW );

                   }

              }   

 

              if(pAttach)

              {

                   //保存文件

                   MAPIHelp_SaveAttachFile( pAttach, szFile );

                   pAttach->Release();

                   pAttach       = NULL;

              }

              FreeProws(psrs);

              psrs = NULL;

          }

 

          bRet     = TRUE;

 

     EXIT:

          if(NULL != psrs)

          {

              FreeProws(psrs);

          }

          if(NULL != pAttach)

          {

              pAttach->Release();

          }

          if(NULL != pAttachTbl)

          {       

              pAttachTbl->Release();

          }

          return bRet;

     }

 

     外面调用时候很简单,只需要获取IMessage对象,再调用MAPIHelp_GetAttachment即可。

(九)Custom Form介绍

一直很想写些关于Custom Form和Transport方面的东西,但是一方面这几个部分东西比较多,一篇两篇也讲不完,另外一方面感觉用的人不多,写了也是白写,所以一直没动手。最近有不少网友通过MAIL或者在CSDN论坛上都提到了Custom Form的用法(主要是想实现自己的类如MMS之类的客户端),在这里我简单介绍一下Custom Form的使用方法,希望对有需要的朋友能有所帮助。

       实际上在微软的2005 SDK SAMPLE已经有了一个比较详细的例子,叫做Customform,大家可以在SDK安装目录/wce500/Windows Mobile 5.0 Pocket PC SDK/Samples/CPP/Win32下面找到这个例子,所以详细代码我就略过了,我们从系统对一个Custom Form的调用逻辑讲起。

1.       用户点击New或者某条已经存在的Message再编辑,tmail查阅对应的Message Type,比如是IPM.SMSText(SMS)还是IPM.Note(Outlook Email)或者还是其它,然后查询注册表Message Type注册的位置(HKEY_CURRENT_USER/Software/Microsoft/Inbox/MsgTypes/IPM)找到正确的Form Dll.

2.       每个Form DLL必须实现FormFactoryEx输出函数,tmail调用FormFactoryEx获取IFormProviderEx对象。

3.       根据不同的需求,调用IFormProviderEx不同的函数,比如如果是新建或者再编辑一条Message,将会调用IFormProviderEx:: CreateComposeForm,如果是播放,则调用IFormProviderEx:: CreateReadForm,如果是获取Message Icon,则调用IFormProviderEx:: GetMsgStatusIconIndex(可以参考《Pocket PC & Smartphone 短信图标轻松换》一文)。

 

       要实现自己的编辑客户端,如果才能让用户方便的创建你定义的Message呢?微软的Sample里面没有涉及到这一点,它所走的流程是:

1.         通过IMailRuleClient截获EMS消息,把它的Message Type改成IPM.SMStext.SDKEMS。

2.         编辑或者播放时,因为类型是IPM.SMStext.SDKEMS,所以会调用自己一注册的EMS Custom Form。

 

它略过了创建IPM.SMStext.SDKEMS类型Message的细节,那么要如何创建这种类型的消息呢?一种比较方便的方法就是在SMS基础上添加EMS的支持,如下图:

 

这样,一旦用户点了EMS,那么创建的就是自定义的EMS消息,想要实现它其实很简单,在Customform例子的基础上,我们新建Message Type,比如叫IPM.EMS,替换掉Customform里面所有的IPM.SMStext.SDKEMS,接下来我们只需要对注册表做少许的修改:

以下是IPM.EMS类型的注册:

[HKEY_CURRENT_USER/Software/Microsoft/Inbox/MsgTypes/IPM/EMS]

"GlyphInfo"=hex:/

      20,00,00,00,64,00,00,00,64,00,00,00,00,00,00,00,01,00,00,00,03,00,00,00,02,/

      00,00,00,00,00,00,00

"DLL"="EMSViewerForm.dll"

"Name"="EMS"

 

在[HKEY_LOCAL_MACHINE/Software/Microsoft/Inbox/Svc/SMS/MsgClasses]下面添加键值:

"IPM.EMS"=dword:00000001

 

OK,我们单独的EMS 编辑器就成功了。

(十) MAPI的一些问题

1. tmail的后台启动模式:

    前几天有网友问,他想通过SubmitMessage发送message,但是如果tmail没有起来,message只会被放到outbox里面等待发送,但是又不想在自己程序里面点发送就启动tmail界面,也有其他网友也问过类似的问题,希望调用MAPI一些功能,又不想开启tmail UI,我记的以前找到过tmail的后台启动模式,今天翻了出来,希望对大家有所帮助:

::CreateProcess(_T("tmail.exe"), _T("-RunInBKG"),NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);

2.tmail的一些命令行参数介绍:

    -service: 调用类型,比如MMS,SMS等

    -attach: 添加附件

    -subject: 添加subject

    -to: 添加目标地址

    举个例子:

    const szCMD[] = _T(" -service /"MMS/" -to /"test@sina.com;13800571505/"");

    CreateProcess(_T(//Windows//tmail.exe), szCMD, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值