CEMAPI实战攻略(三)——操作信箱中的短信息(上)
by 吴春雷
QQ:819543772
Email:wuchunlei@163.com
三.操作信箱中的短信息
1. Windows mobile中短消息的组成
用过的Windows Mobile平台设备的朋友在第一次发送的短信的时候或多或少都会有些困惑,尤其是使用Windows Mobile 2003以前版本的朋友,发短信的UI上不但有填写目的号码和正文的地方,还有填写主题,CC甚至BCC的位置,搞得用户不知如何是好。为什么会有这些内容前面实际上已经提到过了。WM系统中邮件和短消息属于一个系统,微软为了开发方便将二者使用了相同的架构和接口,因此才会有短消息中存在CC和BCC这样的事情出现。虽然从WM5.0以后的版本开始,主题,CC,BCC等东西已经不见了,但是为了后面方便阐述解析原始短信息部分内容,这里简单的说一下短信的结构组成。
CC,BCC这些内容是发送短信中不需要的,填写了也没有什么效果。但是主题(subject)如果填写了是会对短信内容产生影响的。MSDN上介绍,如果发送短信的时候在主题和正文(Body)中都填写了内容,那么发送时,系统会在主题的内容后增加一个换行符,然后在把正文添加到后面作为短信真正的正文发送出去。注意WM中的换行符是\n而不是\r\n。除了主题和正文以外,一条短息还包括发送时间,接收时间,发送方号码,接受方号码等属性组成。在某一条具体信息之中这些属性不一定同时存在,对于不同具体信箱(Folder)中的短信息,拥有不同的属性。例如:收件箱中的短信,只有正文、发送方号码、发送时间这几个属性。
2. IMessage接口
前面对短信的结构做了一个简要的了解,现在要介绍一个与短信相关的非常重要的接口,IMessage接口。该接口直接继承自IMAPIProp,有IMAPIProp间接继承自IUnknow接口,同样属于COM对象。接口中封装了与短信相关的基本操作,常用的方法,例如Get/SetProps、SubMessage、SaveChange等方法是值得大家去特别注意的,这些方法的用法,后面会提到。
3. SizedSPropTagArray宏
上一部分中我们已经介绍过SizedSPropTagArray宏以及用它动态生成结构体类型的方法,这里再强调一下,这个宏在cemapi中非常重要,希望大家能够关注。关于这个宏的使用方法,请参见上一部分的内容。
4. 从具体信箱中获取一条原始短消息
上一部分中已经详细的讨论了如何建立与短信邮件系统的会话(Session),如何获取短信仓库(MsgStore)以及如何与具体的信箱(Folder)进行连接。我们先来简单回顾一下,首先利用MAPILogonEx函数建立与短信邮件系统的会话,然后利用IMAPISession接口的对象获取所有消息(邮件)仓库(MsgStore),遍历这些仓库,根据PR_DISPLAY_NAME属性的值获取短信所对应的短信仓库对象。然后根据具体信箱对应的参数获取指向具体信箱的对象(Folder)。
有了具体的信箱以后,下面需要做的就是要从该信箱中获取一条原始短消息了,原始短消息由Imessage接口对应的对象表示。前面说过,Cemapi中无论是短信(邮件)仓库MsgStore,还是具体信箱(Folder)均是由表格的形式表示的,Folder中的短信自然也不例外。我们可以通过GetContentsTable方法来获取信息对应的表格对象IMAPITable。如果大家忘记了,可以返回上一部分再看一下。GetContentsTable方法声明如下:
HRESULT IMAPIFolder::GetContentsTable(ULONG,IMAPITable **);
方法的返回值用于判断方法是否执行正确。参数列表:
ULONG:很抱歉,这个参数我没有看到过相关的介绍,在我们的应用中赋值为0是可以正常工作的。
IMAPITable **:用于返回短信列表对应的表结构。
我认为Cemapi中有一个原则(你可以不这么认为),只要出现了IMAPITable接口的对象,就需要采用SizePropTagArray这个宏来声明一个动态结构体,并且调用SetColumns告诉IMAPITable接口对象,列表中的行记录的组织结构。现在我们需要获取行记录中的ENTRYID,然后用这个ENTRYID去Folder中获取对应的短信息对象。
SizedSPropTagArray(1, Columns) = //动态生成结构体对象,只获取Entry ID属性
{
1,
PR_ENTRYID
};
还记得这么声明的意义吗?我们声明了一个动态结构体变量Columns,该结构体中的ULONG值为1(表示数组元素数量),ULONG数组共一个元素,名为PR_ENTRYID。有了这个结构体以后,采用SetColumns方法就可以对表格中行记录结构进行设置了。源代码如下:
IMAPITable *m_pTable = NULL;
m_pFolder->GetContentsTable(0, &m_pTable); //获取该Box下的短信行记录列表
hr = m_pTable->SetColumns((LPSPropTagArray)&Columns, 0); //设置列格式
行记录结构设置完毕以后,与获取MsgStore对象类似,使用QueryRows方法就可以获取该表中所有的行记录了。获取每一行的ENTRYID属性,然后使用IMAPIFolder中的OpenEntry方法(很熟悉吧,没错,就是与获取MsgStore时ImsgStore中的同名方法相同)来获取EntryId对应的短消息了。
获取原始短消息的代码如下:
LPMAPITABLE m_pTable = NULL;
LPSRowSet m_pRows = NULL;
HRESULT hr;
SizedSPropTagArray(1, Columns) = //动态生成结构体对象,只获取Entry ID属性
{
1,
PR_ENTRYID
};
m_pFolder->GetContentsTable(0, &m_pTable); //获取该Box下的内容列表
hr = m_pTable->SetColumns((LPSPropTagArray)&Columns, 0); //设置列记录结构
if(FAILED(hr))
{
//异常处理
}
ULONG ulRowCount=0;
ULONG i=0;
m_pTable->GetRowCount(0,&ulRowCount); //获取列表中行记录总数
while(!FAILED(m_pTable->QueryRows(1, 0, &m_pRows)))
{
if(i==ulRowCount) break; //如果已经遍历完毕
else i++;
IMessage *m_pMsg = NULL;
ULONG ulMesageType;
//通过OpenEntry获取IMessage对象
hr=m_pFolder->OpenEntry( m_pRows->aRow[0].lpProps[0].Value.bin.cb,
(LPENTRYID)m_pRows->aRow[0].lpProps[0].Value.bin.lpb,
NULL,
MAPI_BEST_ACCESS,
&ulMesageType,
(LPUNKNOWN*)&m_pMsg);
if(!FAILED(hr) && NULL!=m_pMsg) //取到了消息
{
//取到了原始消息
}
if(m_pRows)
{
FreemProws(m_pRows); //释放行
m_pRows=NULL;
}
}
if(m_pTable)
{
m_pTable->Release(); //释放表结构
m_pTable=NULL;
}
5.释放IMessage对象.
使用IMessage::Release()方法,可以安全的释放该对象。代码如下:
m_pMsg->Release();