Practical Use of MAPI

Practical Use of MAPI

Hi everyone, I’m Jay Ongg, ready to do more with MAPI. Last time, I wrote a function that displays the names of all the message accounts on your device. In this article, I’ll go a little bit further. I’ll talk a bit about how to manipulate folders, messages, and properties. This post is a bit code-heavy, but if you have any questions about it please feel free to ask (note this is sample code with some error checking thrown in), but I know that there are some ISVs who need some help with this. Rather than answering questions one at a time, I figure it’s more efficient to do this post). Hopefully my color-coded comments in the code (alliteration!) can explain what’s going on.

 

One thing that I really want is a way to back up my text messages. I’m going to present a function that will take an account name and ultimately, pass some message information to a function. This can be the basis of more advanced MAPI property manipulation.

Top-Level Function

HRESULT SaveSmsMessages(IMAPISession *pSession, LPCTSTR pszFilename)

{

    static const SizedSPropTagArray (2, spta) = { 2, PR_DISPLAY_NAME, PR_ENTRYID };

   

  HRESULT hr;

    SRowSet *prowset = NULL;

    CComPtr<IMAPITable> ptbl;

    CComPtr<IMsgStore> pStore;

   

    // Get the table of accounts

    hr = pSession->GetMsgStoresTable(0, &ptbl);

    CHR(hr);

 

    // set the columns of the table we will query

  hr = ptbl->SetColumns((SPropTagArray *) &spta, 0);

    CHR(hr);

 

    while (TRUE)

    {

        // Free the previous row

        FreeProws (prowset);

        prowset = NULL;

 

        hr = ptbl->QueryRows (1, 0, &prowset);

        if ((hr != S_OK) || (prowset == NULL) || (prowset->cRows == 0))

            break;

 

        ASSERT (prowset->aRow[0].cValues == spta.cValues);

        SPropValue *pval = prowset->aRow[0].lpProps;

 

        ASSERT (pval[0].ulPropTag == PR_DISPLAY_NAME);

    ASSERT (pval[1].ulPropTag == PR_ENTRYID);

 

        if (!_tcscmp(pval[0].Value.lpszW, TEXT("SMS")))

        {

            // Get the Message Store pointer

            hr = pSession->OpenMsgStore(0, pval[1].Value.bin.cb, (LPENTRYID)pval[1].Value.bin.lpb, 0, 0, &pStore);

            CHR(hr);

 

            SaveMessages(pStore, pszFilename);

        }

    }

 

Error:

    FreeProws (prowset);

    return hr;

}

 

This function is similar to the function that I wrote last time. Instead of displaying the account names, I instantiate a message store object and navigate into it. The function iterates through each account, searching for SMS. Once the SMS row is found, we know that pval[1] has the ENTRYID of the associated message store object. Every MAPI object has a unique id, called the ENTRYID. The ENTRYID is always stored in the PR_ENTRYID property, which is a binary property (remember, you can tell the type of the property based on the definition of the constant).

 

We can then call IMAPISession::OpenMsgStore in order to obtain an IMsgStore pointer. From here, I call a new function, SaveMessage(), passing it the IMsgStore pointer along with the filename to write to.

SaveMessages

Sorry to have to give you such a big function, but this is somewhat similar to what we did to get the accounts. As above, we have to obtain a table of records, set the columns, and iterate through it. To clarify the code better, I’ll use inline comments to describe the important steps. MAPI requires quite a few stpes to do seemingly simple things. Hopefully this is a good technique for larger functions J.

 

HRESULT SaveMessages(IMsgStore *pStore, LPCTSTR pszFilename)

{

    static const SizedSSortOrderSet(1, sortOrderSet) = { 1, 0, 0, { PR_MESSAGE_DELIVERY_TIME, TABLE_SORT_DESCEND } };

    static const SizedSPropTagArray (3, spta) = { 3, PR_SENDER_NAME, PR_SUBJECT, PR_MESSAGE_DELIVERY_TIME };

    HRESULT hr = S_OK;

    LPENTRYID pEntryId = NULL;

    ULONG cbEntryId = 0;

    CComPtr<IMAPIFolder> pFolder;

    CComPtr<IMAPITable> ptbl;

    ULONG ulObjType = 0;

    SRowSet *prowset = NULL;

// 1 First retrieve the ENTRYID of the Inbox folder of the message store

    // Get the inbox folder

    hr = pStore->GetReceiveFolder(NULL, MAPI_UNICODE, &cbEntryId, &pEntryId, NULL);

    CHR(hr);

// 2 we have the entryid of the inbox folder, let's get the folder and messages in it

    hr = pStore->OpenEntry(cbEntryId, pEntryId, NULL, 0, &ulObjType, (LPUNKNOWN*)&pFolder);

    CHR(hr);

    ASSERT(ulObjType == MAPI_FOLDER);

// 3 From the IMAPIFolder pointer, obtain the table to the contents

    hr = pFolder->GetContentsTable(0, &ptbl);

    CHR(hr);

// 4 Sort the table that we obtained. This is determined by the sortOrderSet variable

    hr = ptbl->SortTable((SSortOrderSet *)&sortOrderSet, 0);

    CHR(hr);

// 5 Set the columns of the table we will query. The columns of each row are determined by spta

    hr = ptbl->SetColumns ((SPropTagArray *) &spta, 0);

    CHR(hr);

    // now iterate through each message in the table

    while (TRUE)

    {

        // Free the previous row

        FreeProws (prowset);

        prowset = NULL;

 

        hr = ptbl->QueryRows (1, 0, &prowset);

        if ((hr != S_OK) || (prowset == NULL) || (prowset->cRows == 0))

            break;

        ASSERT (prowset->aRow[0].cValues == spta.cValues);

        SPropValue *pval = prowset->aRow[0].lpProps;

 

// 6 Get the three properties we need: Sender name, Subject, and Delvery time.

        ASSERT (pval[0].ulPropTag == PR_SENDER_NAME);

        ASSERT (pval[1].ulPropTag == PR_SUBJECT);

        ASSERT (pval[2].ulPropTag == PR_MESSAGE_DELIVERY_TIME);

        LPCTSTR pszSender = pval[0].Value.lpszW;

        LPCTSTR pszSubject = pval[1].Value.lpszW;

        SYSTEMTIME st = {0};

        FileTimeToSystemTime(&pval[2].Value.ft, &st);

// 7 Pass the parameters to a function to archive (this function is not written)

        hr = AppendToFile(pszFilename, pszSender, pszSubject, st);

        CHR(hr);

    }

Error:

    FreeProws (prowset);

    MAPIFreeBuffer(pEntryId);

    return hr;

}

 

 

Hope this helps. Next time I’ll talk about creating an Outlook menu extension, as well as some helpful APIs I haven’t talked about yet.