用MAPI操作短信邮件

用MAPI操作短信邮件

 

    MAPI函数不仅可以读取短信,还可以创建短信,读写邮件等。 具体有哪些功能我也说不全,可以自己google一下。这里只详细说明MAPI读取短信或邮件的过程。在网上其他地方也有不少这有的例子,但不完全,甚至有错误。 在本例中,没有对MSDN中能查到的函数多加说明, 不懂的自己查.
前提,环境VS2005以及以上,语言是C++.
需要包含的头文件:
#include <cemapi.h>
#include <mapix.h>
#include <mapiutil.h>
需要包含的库:
#pragma comment(lib, "cemapi")
  
〇. 变量的声明
    IMAPISession* m_pSession;    //会话
    IMsgStore* m_pMsgStore;      
    IMAPIFolder* m_folder;
    LPMAPITABLE m_pMsgTable;

IMAPISession: MSDN说: The IMAPISession interface is used to manage objects associated with a MAPI logon session. 意思是, IMAPISession接口用来管理与MAPI登录会话相关的对象.
IMsgStore: The IMsgStore interface provides access to message store information and to messages and folders. IMsgStore接口提供对消息存储信息和文件夹的访问.
IMAPIFolder: The IMAPIFolder interface is used to perform operations on the messages and subfolders in a folder. IMAPIFolder接口用来执行对消息和子文件夹的操作.
LPMAPITABLE: IMAPITable接口指针, The IMAPITable interface is used to provide a read-only view of a table. IMAPITable is used by clients and service providers to manipulate the way a table appears. IMAPITable接口用来提供支队的表视图, 此接口被客户端和服务提供者执行表的出现( or 行为?).
变量的初始化: 以上变量初始化为NULL.

一. 首先需要初始化环境,并创建一个会话,
    m_pSession = NULL;

    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    MAPIInitialize(NULL);
    MAPILogonEx(NULL, NULL, NULL, NULL, &m_pSession);

    说明:m_pSession为声明是IMAPISession* m_pSession

    结束会话,退出环境:
    if(m_pSession)
    {
        m_pSession->Logoff(0, 0, 0);
        m_pSession->Release();
        m_pSession = NULL;
    }
    if(m_pMsgStore)
        MAPIFreeBuffer(m_pMsgStore);
    if(m_folder)
        MAPIFreeBuffer(m_folder);
    if(m_pMsgTable)
        MAPIFreeBuffer(m_pMsgTable);

    MAPIUninitialize();
    CoUninitialize();

以上两段代码可以分别放在一个类的构造和析构函数里面

二. 打开指定的帐户:
一台机器上可能存在多个帐户,如:短信帐户,多个邮件帐户,每个帐户名有一个display name。我们可能根据这个名字找到我们所需要的帐户。
OpenMsgStore函数让我们得到m_pMsgStore. 当然,是在函数执行成功的前提下.

// 功能: 打开指定的帐户
// 参数 tchar是指定的帐户名,如果要读取短信,tchar必须为sms。如果想要读取163邮箱中的邮件,tchar可以是“163”,但也不一定,这得看你在创建这个邮件帐户的时候的配置。
// 返回值: 如果成功打开帐户,返回TRUE, 否则返回FALSE.
BOOL CMapi::OpenMsgStore( TCHAR tchar[] )
{
    if(m_pMsgStore)    //帐户已经打开, 返回true
        return TRUE;

    LPMAPITABLE pTable = NULL;    //LPMAPITABLE可以认为类似于数据库的表,是包含多行数据,每行可以顺序读取出来.

    HRESULT hr = 0;
    LPSRowSet pRows = NULL;// 指向表中的行
    ULONG cValues;
    SPropValue* pProps = NULL; // SPropValue用作返回对象属性值的结构

    // get stores table
    hr = m_pSession->GetMsgStoresTable(MAPI_UNICODE , &pTable);     // 获取帐户表
...

        BOOL bFound = FALSE;
    while(SUCCEEDED(pTable->QueryRows(1, 0, &pRows))) //循环读取帐户表, 将读取的行信息返回到pRows指针指向的对象中,一行即代表一个帐户
    {

        if (NULL == pRows || pRows->cRows != 1)
        {
            break;
        }

        // open msg store
        SBinary& blob = pRows->aRow[0].lpProps->Value.bin;        // 此行(帐户)的EntryId,
        hr = m_pSession->OpenMsgStore(NULL, blob.cb, (LPENTRYID)blob.lpb, NULL, 0, &m_pMsgStore);//根据EntryId打开帐户
       if(FAILED(hr))
        {
            FreeProws(pRows);
            pRows = NULL;
            continue;
        }
        SPropTagArray props;                                                  // SpropTagArray是一个数组,用来存储属性列表, 而 SPropValue* 用来存储属性值的列表
        props.cValues = 1;                                                        //指定属性的个数.
        props.aulPropTag[0] = PR_DISPLAY_NAME;                    // 指定属性名, PR_DISPLAY_NAME是指帐户名.
        hr = m_pMsgStore->GetProps(&props, MAPI_UNICODE, &cValues, &pProps); // 查询返回属性值, 属性值存储在pProps指针指向的对象中.
        if (_tcsicmp(pProps[0].Value.lpszW, tchar) == 0)            // 是否等于参数指定的帐户名, 如果是则返回,
        {
            SBinary& sesBin = pRows->aRow[0].lpProps[0].Value.bin;
            m_pSession->OpenEntry(sesBin.cb,                    // 打开容器内的对象, 返回更深入的指针(MSDN上这么说的,我也不明白这是什么意思, 原话是:
                                                                               // The OpenEntry method opens an object within the container, returning an interface pointer for further access.)
                (LPENTRYID)sesBin.lpb,
                NULL,
                MAPI_BEST_ACCESS,
                0,
                (LPUNKNOWN*)&m_pMsgStore);
            FreeProws(pRows);                                        // 释放行资源
            pRows = NULL;
            MAPIFreeBuffer(pProps);                                 // 释放属性资源
            bFound = TRUE;
            break;
        }
        FreeProws(pRows);
        pRows = NULL;
        MAPIFreeBuffer(pProps);
    }

    return bFound;;
}


三. 打开文件夹
OpenFolder函数将打开一个文件夹, 如果执行成功, 我们将得到一个有效的m_folder.

// 功能: 函数将打开指定的文件夹
// folderId, 文件夹的ID
//              PR_CE_IPM_INBOX_ENTRYID    收件箱
//              PR_IPM_SENTMAIL_ENTRYID    发件箱
//              PR_CE_IPM_DRAFTS_ENTRYID 草稿箱
// 返回: 打开成功返回TRUE, 否则返回FALSE.

BOOL CMapi::OpenFolder( DWORD folderId )
{
    if(m_pMsgStore==NULL)
        return FALSE;

    ULONG cValues;
    SPropValue* pProps = NULL;
    HRESULT hr;
    if(m_folder)
    {
        MAPIFreeBuffer(m_folder);
    }


    SPropTagArray propDefaultFolder;propDefaultFolder.cValues = 1;

    propDefaultFolder.aulPropTag[0] =folderId;//PR_CE_IPM_DRAFTS_ENTRYID;    // 指定属性

    hr = m_pMsgStore->GetProps (&propDefaultFolder, MAPI_UNICODE, &cValues, &pProps); // 获得属性值
  
    SBinary& eidDrafts = pProps->Value.bin;                                                                    // 获得文件夹的entryId
    hr = m_pMsgStore->OpenEntry(eidDrafts.cb, (LPENTRYID)eidDrafts.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&m_folder); // 打开文件夹

    MAPIFreeBuffer(pProps);

    return SUCCEEDED(hr);
}


四. 开文信息列表, 打开文件夹后,就可以打开信息列表了. 类似于打开帐户.
OpenRows会打开一个文件信息表, 我们将得到m_pMsgTable.

//功能:打开文件信息列表
//返回: 打开成功返回TRUE, 否则返回FALSE
BOOL CMapi::OpenRows()
{
    if(m_folder==NULL)
        return FALSE;

    if(m_pMsgTable)
    {
        MAPIFreeBuffer(m_pMsgTable);
        m_pMsgTable = NULL;
    }
    HRESULT hr = m_folder->GetContentsTable(0, &m_pMsgTable);
    if(FAILED(hr))
        return FALSE;

    return TRUE;
}


五. 返回行集. 获得 m_pMsgTable以后, 调用OpenOneRow可以返回table中的一行. 在OpenOneRow函数中m_pMsgTable调用QueryRows函数返回行, 根据QueryRows的参数, 我们可以获取table中的一行或多行信息.
HRESULT QueryRows(    
    LONG LRowCount,
    ULONG ulFlags,
    SRowSet **lppRows
);
LRowCount指定请求的行数. 例如, 当LRowCount等于1,第一次调用此函数的时候, 将返回第一行, 再次调用时,将从第二行读取开始读取, 可以用SeekRow来指定读取的位置.
这些操作非常像数据库中的记录集,
LPSRowSet: 是SRowSet的指针,MSDN是这有解释的, The SRowSet structure contains an array of SRow structures, each SRow structure describing a row from a table.

//功能: 获取一行信息.
// 参数: pMsgRows, 指向行的指针, 用来接收行.
// 返回值: 成功返回TRUE, 否则返回FALSE.
BOOL CMapi::OpenOneRow(LPSRowSet& pMsgRows)
{
    pMsgRows = NULL;
    HRESULT hr = m_pMsgTable->QueryRows(1, 0, &pMsgRows);

    if(FAILED(hr))
        return FALSE;
    if( pMsgRows->aRow[0].lpProps==NULL)
    {
        FreeProws(pMsgRows);
        return FALSE;
    }

    return TRUE;
}


六. 得到一条消息. GetMessage和GetSessionMsg将返回一条具体的消息接口IMessage.
参数LPMESSAGE是IMessage的指针, MSDN解释说:The IMessage interface is used for managing messages, attachments, and recipients. 用于输出.
pMsgRows参数是调用OpenOneRow所获得的行集.
// 功能: 获取一条消息
// 参数: LPSRowSet: 输入, 行信息
// LPMESSAGE: 输出, 用来获取一条信息
BOOL CMapi::GetMessage( LPSRowSet pMsgRows, LPMESSAGE& pMessage )
{
    SBinary    sbEntry = pMsgRows->aRow[0].lpProps[0].Value.bin;        // 获取EntryId
    return GetSessionMsg(sbEntry.cb, (LPENTRYID) sbEntry.lpb, pMessage);
}

//功能: 获取一条具体的消息
// 参数: cbEntryId, 消息标识长度
//         lpEntryId, 消息标识
// 成功返回TRUE, 否则返回FALSE.
BOOL CMapi::GetSessionMsg( ULONG cbEntryId, LPENTRYID lpEntryId, LPMESSAGE& pMessage )
{
    if(m_pSession==NULL)
        return FALSE;

    HRESULT hr = m_pSession->OpenEntry(cbEntryId, lpEntryId,NULL, MAPI_BEST_ACCESS, NULL,
        (LPUNKNOWN*) &pMessage);
    if(FAILED(hr))
    {
        TRACE1("Get message error %d/n", hr);
        return FALSE;
    }
    return TRUE;

}


七: 获取消息的文本数据, 利用GetMsgInfo函数, 我们可以获得消息的标题, 内容等字符类型的数据(属性被指定为PT_STRING类型). 如果指定非文本类型的属性, 此函数将会一场.
// 功能: 获取消息的一条属性
// 参数: pMessage,输入, 消息指针; Info,用于获取结果; propId, 属性名
//         例如: PR_SUBJECT,PR_BODY, PR_SENDER_EMAIL_ADDRESS等.
// 返回值: 略.
BOOL CMapi::GetMsgInfo( LPMESSAGE pMessage, CString&Info, DWORD propID )
{
    SPropValue* vl = NULL;
    ULONG cValues;
    SPropTagArray Properties;
    Properties.cValues = 1;
    Properties.aulPropTag[0] = propID;
    vl = NULL;

    HRESULT hr = pMessage->GetProps((LPSPropTagArray)&Properties, MAPI_UNICODE,
        &cValues, &vl);

    if((vl->ulPropTag&propID)!=propID)
        return FALSE;

    if(FAILED(hr))
        return FALSE;

    if((vl->Value.lpszW) != NULL)
    {
        Info = vl->Value.lpszW;
    }

    MAPIFreeBuffer(vl);
    return TRUE;
}

 

八. 获取收件人, 收件人虽然也是LPMESSAGE的一个属性, 但不能向获得发件人一样用GetMsgInfo来获取, 因为收件人可能是多个. GetReceiver函数将在参数Recvr中返回所有的收件人, 每个收件人用逗号分割.

BOOL CMapi::GetReceiver( LPMESSAGE pMsg, CString& recvr )
{
    IMAPITable* pTable = NULL;
    HRESULT hr;

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

    hr = pMsg->GetRecipientTable(NULL, &pTable );
    if(hr==MAPI_E_NO_RECIPIENTS)
    {
        TRACE0("No recipient/n");
        recvr = "N/A";
        return TRUE;

    }
    if(FAILED(hr))
        return FALSE;

    LPADRLIST pRecipentRows = NULL;

    while(!FAILED(hr = pTable->QueryRows(1, 0, (LPSRowSet*)&pRecipentRows)))
    {
        if( pRecipentRows->cEntries == 0 )
            break;

        CString& strContact = recvr;
        for(ULONG n = 0; n < pRecipentRows->cEntries; n++ )
        {
            //每个Entry代表一个联系人信息,每个联系人信息又有多个属性组成
            for(ULONG i = 0; i < pRecipentRows->aEntries[n].cValues ; i++)
            {
                //判断如果是PR_EMAIL_ADDRESS属性,那么就找到了联系人地址
                if( PR_EMAIL_ADDRESS == pRecipentRows->aEntries[n].rgPropVals[i].ulPropTag )
                {
                    if(strContact!=_T(""))
                        strContact += ",";
                    strContact += pRecipentRows->aEntries[n].rgPropVals[i].Value.lpszW;
                    MAPIFreeBuffer(pRecipentRows->aEntries[n].rgPropVals);
                }
            }
        }
        MAPIFreeBuffer(pRecipentRows);
    }
    return TRUE;

}

最后附加CMapi类的声明: 其中DoListen和UndoListen函数没有详细说明, 这两个函数分别是用来监听消息动作和取消监听的. 这部分内容以后讨论.

class CMapi
{
protected:
    IMAPISession* m_pSession;    //会话
    IMsgStore* m_pMsgStore;      
    IMAPIFolder* m_folder;
    LPMAPITABLE m_pMsgTable;


protected:
    BOOL GetMsgInfo(LPMESSAGE pMessage, CString&Info, DWORD propID);
public:
    CMapi(void);
    ~CMapi(void);
public:
    // 功能: 打开指定的帐户
    // 参数 tchar: 帐户名,如果要读取短信,tchar一般为sms。
    // 返回值: 如果成功打开帐户,返回TRUE, 否则返回FALSE.
    BOOL OpenMsgStore(TCHAR tchar[]);
    BOOL OpenFolder(DWORD folderId);
    BOOL OpenRows();
    BOOL OpenOneRow(LPSRowSet& pMsgRows);
    BOOL GetMessage(LPSRowSet pMsgRows, LPMESSAGE& pMessage);
    BOOL GetSessionMsg(ULONG cbEntryId, LPENTRYID lpEntryId, LPMESSAGE& pMessage);

    // 获得发送者
    BOOL GetSender(LPMESSAGE lpMsg, CString& sender);

    //获取消息标题
    BOOL GetMsgSubject(LPMESSAGE lpMsg, CString& subject);

    // 获取接收者,接收者可能有多个,recvr中用逗号分割多个接收者
    BOOL GetReceiver(LPMESSAGE pMsg, CString& recvr);

    // 获得内容
    BOOL GetMsgContent(LPMESSAGE lpMsg, CString& content);

    //获得信息创建时间
    BOOL GetCreateTime(LPMESSAGE, CString& strTime);

    //监听
    BOOL DoListen(ULONG uMask, LPMAPIADVISESINK lpMapiSind, ULONG* lpuLongConnection);
    BOOL UndoListen(ULONG lpuLongConnection);
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值