Click here to Skip to main content
6,295,667 members and growing! (15,954 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » COM / COM+ » Automation     Intermediate

Reading and Writing Messages in Outlook Express

By Pablo Yabo

This article was done to provide an example of IStoreNamespace / IStoreFolder.
VC6Win2K, WinXP, Win2003, COM, COM+, Dev
Posted:28 Dec 2004
Updated:27 Mar 2006
Views:191,028
Bookmarked:92 times
Announcements
Loading...
 
Search    
Advanced Search
Prize winner in Competition "MFC/C++ Nov 2004"
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
39 votes for this article.
Popularity: 7.38 Rating: 4.64 out of 5
2 votes, 5.1%
1

2

3
4 votes, 10.3%
4
33 votes, 84.6%
5

Sample Image - Outlook_Express_Messages.jpg

Table of contents

Introduction

The application has the following features:

  1. List local folders.
  2. Create / rename / delete local folder.
  3. List messages of a local folder.
  4. Get message properties.
  5. Get message source.
  6. Create / copy / move / delete messages.
  7. Mark messages as Read or Unread.
  8. List body properties or headers.
  9. Get / set property values.
  10. Navigate body structure.
  11. Get / set body content.
  12. Insert bodies.
  13. List / add / remove attachments.

Using this Code

This code was written to provide an initial example of the IStoreFolder / IStoreNamespace classes. Then, an example of IMimeMessage / IMimeMessageTree / IMimeBody / IMimePropertySet was added to the application. The idea of this article is to document, with a complete example, all these interfaces to show how Outlook Express storage could be accessed.

In the initial dialog, all the local folders of the main identity are listed to let the user modify them. In the message dialog, you will see all the messages of the selected folder. Identifying the message source and other operations can be done here. In the 'Bodies' dialog, you will be able to view the message structure and modify it.

Points of Interest

List local folders:

// add all the folders to the list box recursively

void CDemoDlg::AddFolders(STOREFOLDERID dwFolderId)
{
    FOLDERPROPS props;
    HENUMSTORE hEnum;
    int nIndex;

    hEnum = NULL;

    // set the size of the structure

    // or the function return error

    props.cbSize = sizeof(FOLDERPROPS);

    HRESULT hr = 
      m_pStoreNamespace->GetFirstSubFolder(dwFolderId, 
      &props, &hEnum);

    while(SUCCEEDED(hr) && hr != 
               S_FALSE && hEnum != NULL) {
        nIndex = m_listFolder.AddString(props.szName);

        if(nIndex != LB_ERR && nIndex != LB_ERRSPACE) {
            // set the folder id as the data of the item

            m_listFolder.SetItemData(nIndex, props.dwFolderId);

            // add children of this folder too

            AddFolders(props.dwFolderId);
        }

        hr = m_pStoreNamespace->GetNextSubFolder(hEnum, &props);
    }

    // close the enum

    if(hEnum) {
        m_pStoreNamespace->GetSubFolderClose(hEnum);
    }
}

List messages of a folder:

//List messages of folder and add 

//all 'Subject' and 'From' to the list box

MESSAGEPROPS msgProps;
HENUMSTORE hEnumMsg;
CString item;
int nIndex;

hEnumMsg = NULL;

// set the size of the structure

// or the function return error

msgProps.cbSize = sizeof(MESSAGEPROPS);

// as we want the subject and other

// staff we get all the properties.

// you can use MSGPROPS_FAST as first parameter

// to get only a few properties of the message.

HRESULT hr = m_pStoreFolder->GetFirstMessage(0,
                                             0,
                                             MESSAGEID_FIRST,
                                             &msgProps,
                                             &hEnumMsg);

while(SUCCEEDED(hr) && hr != S_FALSE) {
    item = msgProps.pszDisplayFrom;
    item += _T("      ");
    item += msgProps.pszNormalSubject;

    // message subject and from is displayed in the list box.

    // data of each item is the message id.

    nIndex = m_listMsg.AddString(item);

    if(nIndex != LB_ERR && nIndex != LB_ERRSPACE) {
        m_listMsg.SetItemData(nIndex, msgProps.dwMessageId);
    }

    // free the message properties

    // as they are allocated by IStoreFolder.

    m_pStoreFolder->FreeMessageProps(&msgProps);

    hr = m_pStoreFolder->GetNextMessage(hEnumMsg, 
                                   0, &msgProps);
}

// close the enum

if(hEnumMsg) {
    m_pStoreFolder->GetMessageClose(hEnumMsg);
}

Display the source of a message:

// this function displays the source

// of the selected message in the list box

void CMsgDlg::OnView()
{
    ULONG ulReaded = 0;
    int nIndex;
    STOREFOLDERID dwSelMsg;
    HRESULT hr;
    IStream *pTextStream;
    char buffer[4096];

    // Get selected folder id

    nIndex = m_listMsg.GetCurSel();
    if(nIndex == LB_ERR) {
        MessageBox(_T("Select a message first."), 
                   _T("Demo Error"));
        return;
    }

    dwSelMsg = m_listMsg.GetItemData(nIndex);

    // create a IStream from the message

    hr = m_pStoreFolder->OpenMessage(dwSelMsg, 
         IID_IStream, (VOID **) &pTextStream);
    if(FAILED(hr)) {
        MessageBox(_T("Error opening message."), 
                   _T("Demo Error"));
        return;
    }

    CMsgSrcDlg msgSrcDlg;

    // read all the message

    do {
        hr = pTextStream->Read(buffer, 
             sizeof(buffer)-1, &ulReaded);

        if(FAILED(hr)) {
            MessageBox(_T("Error reading message."), 
                       _T("Demo Error"));
        }
        else {
            buffer[ulReaded] = 0;

            msgSrcDlg.AddMessageSource(buffer);
        }
    } while(SUCCEEDED(hr) && ulReaded != 0);

    if(SUCCEEDED(hr)) {
        // display message

        msgSrcDlg.DoModal();
    }

    pTextStream->Release();
}

Create a message in a folder:

IStream *newMail = NULL;
MESSAGEID msgId;
HRESULT hr;
ULONG len;
CString msgSource;

// Set msgSource to contain

// the source of the new message

...

// Create the IStream to write the new message

// this function returns the id of the new message

hr = m_pFolder->CreateStream(0, 0, &newMail, &msgId);
if(FAILED(hr)) {
    MessageBox(_T("Cannot Create Stream."), 
               _T("Demo Error"));
    return;
}

// write message source in the IStream

hr = newMail->Write((const char *) msgSource, 
     msgSource.GetLength(), &len);
if(FAILED(hr)) {
    MessageBox(_T("Cannot Write message."), 
               _T("Demo Error"));
    newMail->Release();
    return;
}

// Commit the IStream in the folder

// and use the returned msgId

hr = m_pFolder->CommitStream(0, 0, 0, 
                  newMail, msgId, NULL);
if(FAILED(hr)) {
    MessageBox(_T("Cannot Commit Stream."), 
               _T("Demo Error"));
    newMail->Release();
    return;
}

// release the IStream

newMail->Release();

List body properties:

// add property names to the combo box

// first get IMimeMessage interface

// using IStoreFolder and the message id.

hr = pFolder->OpenMessage(msgId,
                          IID_IMimeMessage,
                          (LPVOID*) &m_pMimeMsg);
if(FAILED(hr)) {
    OutputDebugString("CMessageTreeDlg::"
             "SetMessage: OpenMessage.\n");
    return;
}

// get root body of the message.

hr = m_pMimeMsg->GetBody(IBL_ROOT, 0, &m_hCurBody);
if(FAILED(hr)) {
    OutputDebugString("OEMessage::SetMessage:"
                      " Cannot get root body.\n");
    return;
}

...

// bind the body to the IMimePropertySet interface.

hr = m_pMimeMsg->BindToObject(m_hCurBody,
     IID_IMimePropertySet, (LPVOID *) &m_pPropertySet);
if(FAILED(hr)) {
    OutputDebugString("OEMessage::UpdateBodyInfo:"
           " BindToObject IID_IMimePropertySet.\n");
    return;
}

...

IMimeEnumProperties *pEnum = NULL;
ENUMPROPERTY eProp = {0};
ULONG cFetched;
HRESULT hr;

m_propNames.ResetContent();

// enum properties of the body.

hr = m_pPropertySet->EnumProps(0, &pEnum);
if(FAILED(hr)) {
    OutputDebugString("OEMessage::"
          "FillCombo: EnumProps.\n");
    return;
}

hr = pEnum->Next(1, &eProp, &cFetched);

while(SUCCEEDED(hr) && hr != S_FALSE) {
    m_propNames.AddString(eProp.pszName);

    hr = m_pAllocator->FreeEnumPropertyArray(1,
                                    &eProp, FALSE);
    if(FAILED(hr)) {
        OutputDebugString("OEMessage::FillCombo:"
                          " FreeEnumPropertyArray.\n");
    }

    hr = pEnum->Next(1, &eProp, &cFetched);
}

if(pEnum) {
    pEnum->Release();
}

List attachments:

ULONG attachCount, i, j;
HBODY *bodyAttachs = NULL;
HRESULT hr;
IMimeBody *pMimeBody;
LPSTR display;
int nItem;

m_attachs.ResetContent();

hr = m_pMimeMsg->GetAttachments(&attachCount,
                                   &bodyAttachs);
if(FAILED(hr)) {
    MessageBox(_T("Cannot get attachments:")
       _T(" GetAttachments."), _T("Demo Error"), MB_OK);
    return;
}

// keep only bodies of type IBT_ATTACHMENT.

for(i=0; i<attachCount;) {
    hr = m_pMimeMsg->IsBodyType(bodyAttachs[i],
                                  IBT_ATTACHMENT);
    if(hr != S_OK) {
        for(j=i+1; j<attachCount; j++) {
            bodyAttachs[j-1] = bodyAttachs[j];
        }

        attachCount--;
    }
    else {
        // for the attachments, get display

        // name of the body to add to the listbox.

        hr = m_pMimeMsg->BindToObject(bodyAttachs[i],
                                      IID_IMimeBody,
                                      (LPVOID *) &pMimeBody);
        if(SUCCEEDED(hr)) {
            hr = pMimeBody->GetDisplayName(&display);
            if(SUCCEEDED(hr)) {
                nItem = m_attachs.AddString(display);
                m_attachs.SetItemData(nItem,
                          (DWORD) bodyAttachs[i]);

                CoTaskMemFree(display);
            }
        }

        i++;
    }
}

if(bodyAttachs) {
    CoTaskMemFree(bodyAttachs);
}

Get body content:

while(1) { // just to save code!

    // bind body handle to a IMimeBody interface.

    hr = m_pMimeMsg->BindToObject(m_hCurBody,
                                  IID_IMimeBody,
                                  (LPVOID *) &pMimeBody);
    if(FAILED(hr)) {
        OutputDebugString("CMessageTreeDlg::"
             "UpdateBodyInfo: BindToObject\n");
        break;
    }

    ...

    encType = IET_BINARY;
    m_isTextBody = FALSE;

    m_cntType = GetContentType(m_hCurBody);

    // if the body is a 'text' treat as a text.

    // Otherwise, read it as a buffer char

    // by char.

    if(m_cntType.Find(_T("text")) == 0) {
        encType = IET_UNICODE;
        m_isTextBody = TRUE;
    }

    ...

    m_bodyContent = _T("");

    // Get body as a stream

    hr = pMimeBody->GetData(IET_UNICODE,
                            &pBodyStream);
    if(FAILED(hr)) {
        OutputDebugString("OEMessage::GetBodyText: GetData\n");
        break;
    }

    // if it is a text when we read it it comes unicode.

    if(encType == IET_UNICODE) {
        // for text bodies

        do {
            // Read the IStream into our buffer

            hr = pBodyStream->Read(lpszwBody,
                                   sizeof(lpszwBody)-sizeof(WCHAR),
                                   &ulRead);
            if(FAILED(hr)) {
                OutputDebugString("OEMessage::GetBodyText: Read\n");
            }
            else if(ulRead != 0) {
                // Null terminate it

                lpszwBody[ulRead/2] = '\0';
                m_bodyContent += (WCHAR *) lpszwBody;
            }
        } while(ulRead != 0);
    }
    else {
        do {
            // Read the IStream into our buffer.

            // It can be binary so it could

            // be displayed truncated.

            hr = pBodyStream->Read(lpszBody,
                                   sizeof(lpszBody)-sizeof(char),
                                   &ulRead);
            if(FAILED(hr)) {
                OutputDebugString("OEMessage::GetBodyText: Read\n");
            }
            else if(ulRead != 0) {
                // Null terminate it

                lpszBody[ulRead] = '\0';
                m_bodyContent += lpszBody;
            }
        } while(ulRead != 0);
    }

    pBodyStream->Release();

    break;
}

if(pMimeBody) {
    pMimeBody->Release();
}

Set body content:

HRESULT hr;
ULONG ulLength, ulWritten;
BSTR bstr = NULL;
IStream *pStream = NULL;
IMimeBody *pMimeBody = NULL;
PROPVARIANT propValue;

UpdateData(TRUE);

while(1) {
    // Create a new stream to write in the new body

    hr = CreateStreamOnHGlobal(NULL,
                               TRUE,
                               &pStream);
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content:" )
            _T(" CreateStreamOnHGlobal."),
            _T("Demo Error"), MB_OK);
        break;
    }

    // compute the new body length + the

    // zero that terminates the string

    ulLength = m_bodyContent.GetLength() + 1;

    // there are better ways

    // to do it but this is the easiest

    bstr = m_bodyContent.AllocSysString();

    // write in the new body

    hr = pStream->Write((LPWSTR) bstr,
                        ulLength * sizeof(WCHAR),
                        &ulWritten);
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content: Write."),
                   _T("Demo Error"), MB_OK);
        break;
    }

    // Commit the stream

    hr = pStream->Commit(STGC_DEFAULT);
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content: Commit."),
                   _T("Demo Error"), MB_OK);
        break;
    }

    // bind body handle to IMimeBody interface

    hr = m_pMimeMsg->BindToObject(m_hCurBody,
                                  IID_IMimeBody,
                                  (LPVOID *) &pMimeBody);
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content:")
                _T(" Commit."), _T("Demo Error"), MB_OK);
        break;
    }

    CString priCon, secCon;

    propValue.vt = VT_LPSTR;

    // get content-type property to save the body

    hr = m_pMimeMsg->GetBodyProp(m_hCurBody,
         PIDTOSTR(PID_HDR_CNTTYPE), 0, &propValue);
    if(FAILED(hr) || hr == S_FALSE) {
        MessageBox(_T("Cannot set content:")
             _T(" GetBodyProp."), _T("Demo Error"), MB_OK);
        break;
    }

    // this property has the format

    // of 'primaryType/secondaryType'

    char *sep = strchr(propValue.pszVal, '/');

    if(sep == NULL) {
        MessageBox(_T("Cannot set content:")
             _T("Content Type error."),
             _T("Demo Error"), MB_OK);
        PropVariantClear(&propValue);
        break;
    }

    secCon = sep+1;
    *sep = 0;
    priCon = propValue.pszVal;

    PropVariantClear(&propValue);

    // save the data in this new stream

    // into the body using

    // the save conent-type it had before

    hr = pMimeBody->SetData(IET_UNICODE,
                            priCon,
                            secCon,
                            IID_IStream,
                            pStream);
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content: SetData."),
                   _T("Demo Error"), MB_OK);
        break;
    }

    break;
}

if(bstr) {
    ::SysFreeString(bstr);
}
if(pMimeBody) {
    pMimeBody->Release();
}
if(pStream) {
    pStream->Release();
}

License

You can use this freely, leaving the copyright notice at the top of the files.

History

  • 28-Dec-2004 - First released: IStoreFolder / IStoreNamespace.
  • 25-Mar-2006 - Update: IMimeMessage / IMimeMessageTree / IMimePropertySet / IMimeBody.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Pablo Yabo


Member
Pablo Yabo is a Software Developer since he was young, specialized in system internals.
In 2003 years ago founded with Sebastian Wain a Company named Nektra specialized in Outlook Express and Outlook products and services.
Occupation: Web Developer
Location: United States United States

Other popular COM / COM+ articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 226 (Total in Forum: 226) (Refresh)FirstPrevNext
GeneralError with OpenMessage function PinmemberAlain favre6:34 12 Jun '09  
GeneralPassword protected identity PinmemberNico Cuppen1:37 4 Jun '09  
GeneralRe: Password protected identity PinmemberPablo Yabo4:25 4 Jun '09  
GeneralVS2008 Pinmemberdjaus20:53 9 Mar '09  
GeneralRe: VS2008 PinmemberPablo Yabo7:25 27 Apr '09  
GeneralWindows Mail access Pinmemberphil359565485219:30 10 Feb '09  
GeneralRe: Windows Mail access PinmemberPablo Yabo7:24 27 Apr '09  
GeneralI must be missing something-please advise Pinmemberphil359565485218:54 10 Feb '09  
GeneralRe: I must be missing something-please advise PinmemberPablo Yabo7:28 27 Apr '09  
QuestionProblem when the application is launched from Window Service PinmemberShailk22:07 6 Feb '09  
AnswerRe: Problem when the application is launched from Window Service PinmemberPablo Yabo7:31 27 Apr '09  
QuestionBlock Sender List Pinmemberpakistan23:55 2 Feb '09  
AnswerRe: Block Sender List PinmemberPablo Yabo7:34 27 Apr '09  
Generalcreate a .dbx file at a specified location. Pinmemberravimalika23:12 29 Jan '09  
QuestionCan I get the domain of a message from which it was downloaded? Pinmemberjoel_123412:43 1 Oct '08  
GeneralHooking message store events Pinmembergnxfiles12:55 21 May '08  
GeneralChanges are not saved in reports in Windows Mail Pinmemberv_klad12:52 8 Apr '08  
GeneralProblems with IMimeMessage::SetProp and IMimeMessage::SetBodyProp PinmemberVishwananda Hemadri0:24 10 Mar '08  
GeneralCan you tell me what's wrong in the sample of MS? Pinmembercrystalicetpmc1:35 5 Mar '08  
GeneralRe: Can you tell me what's wrong in the sample of MS? PinmemberPablo Yabo2:46 5 Mar '08  
GeneralRe: Can you tell me what's wrong in the sample of MS? Pinmembercrystalicetpmc4:47 5 Mar '08  
GeneralRe: Can you tell me what's wrong in the sample of MS? PinmemberPablo Yabo6:48 5 Mar '08  
GeneralRe: Can you tell me what's wrong in the sample of MS? Pinmembercrystalicetpmc9:22 5 Mar '08  
GeneralRe: Can you tell me what's wrong in the sample of MS? Pinmembercrystalicetpmc5:24 6 Mar '08  
GeneralHow to link at C# project Pinmemberap820:07 12 Oct '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 27 Mar 2006
Editor: Smitha Vijayan
Copyright 2004 by Pablo Yabo
Everything else Copyright © CodeProject, 1999-2009
Web16 | Advertise on the Code Project