Are you a world traveler? ZoneTick is a cool utility that'll help you stay in touch over multiple time zones!
 
Modifying Message Properties in Folders  
Nik Okuntseff  MS Exchange Server Programming 

Modifying Message Properties in Folders

Let us suppose that we need to replace a specific property of a message with the same property from another message. For the sake of simplicity let us assume that both messages reside in Inbox. How do we go about it? The program below (Folders/ReplaceProps) does it by first locating and opening the profile's Inbox, opening both messages and then doing IMAPIProp::CopyProps operation.

#include <afxwin.h>
#include <edk.h>

int main()
{
    // Change if necessary
    char * pszSourceMsgSubject = "Source";
    char * pszTargetMsgSubject = "Target";

    HRESULT hr;
    LPMAPISESSION pSession = NULL;

    // Initialize MAPI
    hr = MAPIInitialize(NULL);
    if (FAILED(hr)) throw -1;

    // Obtain MAPI session
    hr = MAPILogonEx(0,    // Handle to parent window
      "MS Exchange Settings", // Profile name
      NULL,   // Password
      MAPI_NEW_SESSION |
      MAPI_EXTENDED |
      MAPI_LOGON_UI, // Logon flags
      &pSession);  // Resulting MAPI session
    if (FAILED(hr)) throw -1;

    // Find default message store
    ULONG cbDefStoreEid = 0;
    LPENTRYID pDefStoreEid = NULL;
    hr = HrMAPIFindDefaultMsgStore(pSession, &cbDefStoreEid, &pDefStoreEid);
    if (FAILED(hr)) throw -1;

    // Open default message store
    LPMDB pDefMsgStore = NULL;
    hr = pSession->OpenMsgStore(0, cbDefStoreEid, pDefStoreEid, NULL,
        MDB_WRITE | MAPI_DEFERRED_ERRORS, &pDefMsgStore);
    if (FAILED(hr)) throw -1;

    // Locate Inbox
    ULONG cbInboxEid = 0;
    LPENTRYID pInboxEid = NULL;
    hr = HrMAPIFindInbox(pDefMsgStore, &cbInboxEid, &pInboxEid);
    if (FAILED(hr)) throw -1;

    // Try to open the Inbox
    ULONG ulObjType = 0;
    LPMAPIFOLDER pInbox = NULL;
    hr = pDefMsgStore->OpenEntry(cbInboxEid,
         pInboxEid,
         NULL,
         MAPI_BEST_ACCESS,
         &ulObjType,
         (LPUNKNOWN *) &pInbox);
    if (FAILED(hr)) throw -1;
 
    // Get contents table...
    LPMAPITABLE pContentsTable = NULL;
    hr = pInbox->GetContentsTable(0, &pContentsTable);
    if (FAILED(hr)) throw -1;

    // Get row count
    ULONG ulRows = 0;
    hr = pContentsTable->GetRowCount(0, &ulRows);
    if (FAILED(hr)) throw -1;
    assert(ulRows > 0);

    // Get all rows
    SRowSet * pRowSet = NULL;
    hr = pContentsTable->QueryRows(ulRows, 0, &pRowSet);
    assert(ulRows == pRowSet->cRows);

    // Scan all rows to locate source and target messages
    SRow row;
    ULONG cbEid = 0;
    LPENTRYID pEid = NULL;
    LPUNKNOWN pSourceMsg = NULL;
    LPUNKNOWN pTargetMsg = NULL;

    for (ULONG ul = 0; ul < ulRows; ul++)
    {
        row = pRowSet->aRow[ul];

        for (ULONG ulIndex = 0; ulIndex < row.cValues; ulIndex++)
        {
            if (PR_SUBJECT == row.lpProps[ulIndex].ulPropTag)
            {
                char * pszCurSubject = row.lpProps[ulIndex].Value.lpszA;
                int iRes = memcmp(pszSourceMsgSubject,
                    pszCurSubject,
                    strlen(pszSourceMsgSubject));
                if (0 == iRes)
                {
                    // We have found the source message!
 
                    // Get PR_ENTRYID
                    SBinary bin;
                    ZeroMemory(&bin, sizeof(bin));
                    for (ULONG ulNewIndex = 0; ulNewIndex < row.cValues; ulNewIndex++)
                    {
                        if (PR_ENTRYID == row.lpProps[ulNewIndex].ulPropTag)
                        {
                            bin = row.lpProps[ulNewIndex].Value.bin;
                            break;
                        }
                    }
                    assert(bin.cb);
 
                    // Open the source message
                    hr = pDefMsgStore->OpenEntry(bin.cb,
                        (LPENTRYID) bin.lpb,
                        NULL,
                        MAPI_BEST_ACCESS,
                        &ulObjType,
                        &pSourceMsg);
                    if (FAILED(hr)) throw -1;
                }

                iRes = memcmp(pszTargetMsgSubject,
                    pszCurSubject,
                    strlen(pszTargetMsgSubject));
                if (0 == iRes)
                {
                    // We have found the target message!
 
                    // Get PR_ENTRYID
                    SBinary bin;
                    ZeroMemory(&bin, sizeof(bin));
                    for (ULONG ulNewIndex = 0; ulNewIndex < row.cValues; ulNewIndex++)
                    {
                        if (PR_ENTRYID == row.lpProps[ulNewIndex].ulPropTag)
                        {
                            bin = row.lpProps[ulNewIndex].Value.bin;
                            break;
                        }
                    }
                    assert(bin.cb);
 
                    // Open the target message
                    hr = pDefMsgStore->OpenEntry(bin.cb,
                        (LPENTRYID) bin.lpb,
                        NULL,
                        MAPI_BEST_ACCESS,
                        &ulObjType,
                        &pTargetMsg);
                    if (FAILED(hr)) throw -1;
                }
                break;
            }
        }
    }
    // At this point we should have the source and target messages open...
    assert(pSourceMsg);
    assert(pTargetMsg);
 
    SPropTagArray tagArray;
    tagArray.cValues = 1;

    // Copy PR_SUBJECT property from source message to target.
    /*
     * Be careful what you copy. Some properties such as PR_BODY
     * are computed. The CopyProps still succeeds.
     */
    tagArray.aulPropTag[0] = PR_SUBJECT;
    hr = ((LPMAPIPROP) pSourceMsg)->CopyProps(&tagArray, NULL, NULL,
                &IID_IMAPIProp, pTargetMsg, 0, NULL);
    if (FAILED(hr)) throw -1;

    // Save changes
    hr = ((LPMAPIPROP) pTargetMsg)->SaveChanges(0/*KEEP_OPEN_READWRITE*/);
    if (FAILED(hr)) throw -1;

    // Cleanup
    if (pTargetMsg)
        pTargetMsg->Release();
    if (pSourceMsg)
        pSourceMsg->Release();
    if (pRowSet)
        MAPIFreeBuffer(pRowSet);
    if (pContentsTable)
        pContentsTable->Release();
    if (pInbox)
        pInbox->Release();
    if (pInboxEid)
        MAPIFreeBuffer(pInboxEid);
    if (pDefMsgStore)
        pDefMsgStore->Release();
    if (pDefStoreEid)
        MAPIFreeBuffer(pDefStoreEid);
    if (pSession)
        pSession->Release();
    ::MAPIUninitialize();
    return 0;
}

There is one thing that you should know about. Some of message properties are computed from others. For example, I have tried to modify PR_BODY, and program did not fail at any point. However, when I opened the target message in Outlook, I did not see my changes. This is because my message had RTF content, and PR_BODY is in fact calculated from one of PR_RTF_... properties. Everything should work fine with PR_SUBJECT. The program assumes that messages with "Source" and "Target" subjects exist in the profile mailbox.
 

[ Contents | Home ]

Send comments and suggestions to niko@wrconsulting.com
Copyright © 1997-1998 by Nik Okuntseff