(一)如何获取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
{
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);
}