Click here to Skip to main content
13,301,591 members (92,595 online)
Click here to Skip to main content
Add your own
alternative version


105 bookmarked
Posted 28 Dec 2004

Reading and Writing Messages in Outlook Express

, 27 Mar 2006
Rate this:
Please Sign up or sign in to vote.
This article was done to provide an example of IStoreNamespace / IStoreFolder.

Sample Image - Outlook_Express_Messages.jpg

Table of contents


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;
    int nIndex;

    hEnum = NULL;

    // set the size of the structure
    // or the function return error
    props.cbSize = sizeof(FOLDERPROPS);

    HRESULT hr = 
      &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

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

    // close the enum
    if(hEnum) {

List messages of a folder:

//List messages of folder and add 
//all 'Subject' and 'From' to the list box
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,

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.

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

// close the enum
if(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;
    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"));

    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"));

    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;

    } while(SUCCEEDED(hr) && ulReaded != 0);

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


Create a message in a folder:

IStream *newMail = NULL;
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"));

// 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"));

// 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"));

// release the IStream

List body properties:

// add property names to the combo box
// first get IMimeMessage interface
// using IStoreFolder and the message id.
hr = pFolder->OpenMessage(msgId,
                          (LPVOID*) &m_pMimeMsg);
if(FAILED(hr)) {
             "SetMessage: OpenMessage.\n");

// get root body of the message.
hr = m_pMimeMsg->GetBody(IBL_ROOT, 0, &m_hCurBody);
if(FAILED(hr)) {
                      " Cannot get root body.\n");


// bind the body to the IMimePropertySet interface.
hr = m_pMimeMsg->BindToObject(m_hCurBody,
     IID_IMimePropertySet, (LPVOID *) &m_pPropertySet);
if(FAILED(hr)) {
           " BindToObject IID_IMimePropertySet.\n");


IMimeEnumProperties *pEnum = NULL;
ULONG cFetched;


// enum properties of the body.
hr = m_pPropertySet->EnumProps(0, &pEnum);
if(FAILED(hr)) {
          "FillCombo: EnumProps.\n");

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

while(SUCCEEDED(hr) && hr != S_FALSE) {

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

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

if(pEnum) {

List attachments:

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


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

// keep only bodies of type IBT_ATTACHMENT.
for(i=0; i<attachCount;) {
    hr = m_pMimeMsg->IsBodyType(bodyAttachs[i],
    if(hr != S_OK) {
        for(j=i+1; j<attachCount; j++) {
            bodyAttachs[j-1] = bodyAttachs[j];

    else {
        // for the attachments, get display
        // name of the body to add to the listbox.
        hr = m_pMimeMsg->BindToObject(bodyAttachs[i],
                                      (LPVOID *) &pMimeBody);
        if(SUCCEEDED(hr)) {
            hr = pMimeBody->GetDisplayName(&display);
            if(SUCCEEDED(hr)) {
                nItem = m_attachs.AddString(display);
                          (DWORD) bodyAttachs[i]);



if(bodyAttachs) {

Get body content:

while(1) { // just to save code!
    // bind body handle to a IMimeBody interface.
    hr = m_pMimeMsg->BindToObject(m_hCurBody,
                                  (LPVOID *) &pMimeBody);
    if(FAILED(hr)) {
             "UpdateBodyInfo: BindToObject\n");


    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,
    if(FAILED(hr)) {
        OutputDebugString("OEMessage::GetBodyText: GetData\n");

    // 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,
            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,
            if(FAILED(hr)) {
                OutputDebugString("OEMessage::GetBodyText: Read\n");
            else if(ulRead != 0) {
                // Null terminate it
                lpszBody[ulRead] = '\0';
                m_bodyContent += lpszBody;
        } while(ulRead != 0);



if(pMimeBody) {

Set body content:

ULONG ulLength, ulWritten;
BSTR bstr = NULL;
IStream *pStream = NULL;
IMimeBody *pMimeBody = NULL;


while(1) {
    // Create a new stream to write in the new body
    hr = CreateStreamOnHGlobal(NULL,
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content:" )
            _T(" CreateStreamOnHGlobal."),
            _T("Demo Error"), MB_OK);

    // 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),
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content: Write."),
                   _T("Demo Error"), MB_OK);

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

    // bind body handle to IMimeBody interface
    hr = m_pMimeMsg->BindToObject(m_hCurBody,
                                  (LPVOID *) &pMimeBody);
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content:")
                _T(" Commit."), _T("Demo Error"), MB_OK);

    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);

    // 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);

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


    // save the data in this new stream
    // into the body using
    // the save conent-type it had before
    hr = pMimeBody->SetData(IET_UNICODE,
    if(FAILED(hr)) {
        MessageBox(_T("Cannot set content: SetData."),
                   _T("Demo Error"), MB_OK);


if(bstr) {
if(pMimeBody) {
if(pStream) {


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


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


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
Technical Lead
Argentina Argentina
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 a custom software development company specialized in Outlook Plugin Development, Data Loss Prevention Solution Development and Windows Driver Development.

You may also be interested in...


Comments and Discussions

Questionhow to set rtf data to a mime message Pin
ravi malik11-May-10 1:12
memberravi malik11-May-10 1:12 
Questionhow can i find contacts Pin
wolf111118-Mar-10 5:11
memberwolf111118-Mar-10 5:11 
Questionhow to add contact into outlook express group, thanks... Pin
hsq62813-Sep-09 21:20
memberhsq62813-Sep-09 21:20 
QuestionHow can I manipulate contacts in outlook express 6 Pin
VishalIndia3-Sep-09 21:41
memberVishalIndia3-Sep-09 21:41 
AnswerRe: How can I manipulate contacts in outlook express 6 Pin
VishalIndia4-Sep-09 2:48
memberVishalIndia4-Sep-09 2:48 
AnswerRe: How can I manipulate contacts in outlook express 6 Pin
Pablo Yabo4-Sep-09 4:27
memberPablo Yabo4-Sep-09 4:27 
QuestionHow to detect Switch Identity Programatically? Pin
Jitendra Shirolkar31-Aug-09 23:25
memberJitendra Shirolkar31-Aug-09 23:25 
Questiondeleting msg from windows mail Pin
Member 31829027-Jul-09 22:58
memberMember 31829027-Jul-09 22:58 
AnswerRe: deleting msg from windows mail Pin
Pablo Yabo8-Jul-09 3:37
memberPablo Yabo8-Jul-09 3:37 
QuestionRe: deleting msg from windows mail [modified] Pin
Member 31829028-Jul-09 21:36
memberMember 31829028-Jul-09 21:36 
GeneralError with OpenMessage function Pin
Alain favre12-Jun-09 6:34
memberAlain favre12-Jun-09 6:34 
GeneralPassword protected identity Pin
Nico Cuppen4-Jun-09 1:37
memberNico Cuppen4-Jun-09 1:37 
GeneralRe: Password protected identity Pin
Pablo Yabo4-Jun-09 4:25
memberPablo Yabo4-Jun-09 4:25 
GeneralVS2008 Pin
djaus9-Mar-09 20:53
memberdjaus9-Mar-09 20:53 
GeneralRe: VS2008 Pin
Pablo Yabo27-Apr-09 7:25
memberPablo Yabo27-Apr-09 7:25 
GeneralWindows Mail access Pin
phil359565485210-Feb-09 19:30
memberphil359565485210-Feb-09 19:30 
GeneralRe: Windows Mail access Pin
Pablo Yabo27-Apr-09 7:24
memberPablo Yabo27-Apr-09 7:24 
GeneralI must be missing something-please advise Pin
phil359565485210-Feb-09 18:54
memberphil359565485210-Feb-09 18:54 
GeneralRe: I must be missing something-please advise Pin
Pablo Yabo27-Apr-09 7:28
memberPablo Yabo27-Apr-09 7:28 
QuestionProblem when the application is launched from Window Service Pin
Shailk6-Feb-09 22:07
memberShailk6-Feb-09 22:07 
AnswerRe: Problem when the application is launched from Window Service Pin
Pablo Yabo27-Apr-09 7:31
memberPablo Yabo27-Apr-09 7:31 
QuestionBlock Sender List Pin
pakistan2-Feb-09 23:55
memberpakistan2-Feb-09 23:55 
AnswerRe: Block Sender List Pin
Pablo Yabo27-Apr-09 7:34
memberPablo Yabo27-Apr-09 7:34 
Generalcreate a .dbx file at a specified location. Pin
ravimalika29-Jan-09 23:12
memberravimalika29-Jan-09 23:12 
QuestionCan I get the domain of a message from which it was downloaded? Pin
joel_12341-Oct-08 12:43
memberjoel_12341-Oct-08 12:43 
GeneralHooking message store events Pin
gnxfiles21-May-08 12:55
membergnxfiles21-May-08 12:55 
GeneralChanges are not saved in reports in Windows Mail Pin
v_klad8-Apr-08 12:52
memberv_klad8-Apr-08 12:52 
GeneralProblems with IMimeMessage::SetProp and IMimeMessage::SetBodyProp Pin
Vishwananda Hemadri10-Mar-08 0:24
memberVishwananda Hemadri10-Mar-08 0:24 
QuestionCan you tell me what's wrong in the sample of MS? Pin
crystalicetpmc5-Mar-08 1:35
membercrystalicetpmc5-Mar-08 1:35 
AnswerRe: Can you tell me what's wrong in the sample of MS? Pin
Pablo Yabo5-Mar-08 2:46
memberPablo Yabo5-Mar-08 2:46 
GeneralRe: Can you tell me what's wrong in the sample of MS? Pin
crystalicetpmc5-Mar-08 4:47
membercrystalicetpmc5-Mar-08 4:47 
GeneralRe: Can you tell me what's wrong in the sample of MS? Pin
Pablo Yabo5-Mar-08 6:48
memberPablo Yabo5-Mar-08 6:48 
GeneralRe: Can you tell me what's wrong in the sample of MS? Pin
crystalicetpmc5-Mar-08 9:22
membercrystalicetpmc5-Mar-08 9:22 
GeneralRe: Can you tell me what's wrong in the sample of MS? Pin
crystalicetpmc6-Mar-08 5:24
membercrystalicetpmc6-Mar-08 5:24 

You are right! Thank you very much!!!

But It possable can't load Outlook Express save as *.eml files format, so, the line below pMimeMessage->Load can't return S_OK.

Thank you very much!!!

  HRESULT hr = S_OK;
  IStream* pFileStream = NULL;
  IMimeMessage* pMimeMessage = NULL;
  var.vt = VT_LPSTR;
  var.pszVal = NULL;
  HBODY hbody = 0;
  // Open specified file and return an IStream interface.
  hr = SHCreateStreamOnFile((LPCSTR)_T("somefile.eml"), STGM_WRITE, &pFileStream);
  // Get an IMimeMessage interface and use it to...
  //hr = pFileStream->QueryInterface(IID_PPV_ARGS(&pMimeMessage));
  //hr = pFileStream->QueryInterface(IID_IMimeMessage, (LPVOID*) &pMimeMessage);
	hr = CoCreateInstance(CLSID_IMimeMessage, NULL, CLSCTX_INPROC_SERVER, IID_IMimeMessage, (LPVOID*)&pMimeMessage);
	hr = pMimeMessage->Load(pFileStream);  
  // ...change the subject...
<s>  hr = pMimeMessage->SetProp((LPCSTR)PID_HDR_SUBJECT, 0, (LPCPROPVARIANT)_T("Here is the new subject"));
  // the To line...
  pMimeMessage->GetProp((LPCSTR)PID_HDR_TO, 0, &var);
  // the message body...
  hr = pMimeMessage->GetTextBody(TXT_HTML, IET_UNICODE, &pFileStream, &hbody);</s>
  // Release our IMimeMessage object.
  // Release our IStream object.
  return 0;


QuestionHow to link at C# project Pin
ap8212-Oct-07 0:07
memberap8212-Oct-07 0:07 
AnswerRe: How to link at C# project Pin
Pablo Yabo4-Feb-08 2:28
memberPablo Yabo4-Feb-08 2:28 
Questionthis demo can't modify the content of a mail in oe or winodws mail(vista) Pin
fangqing11-Sep-07 16:28
memberfangqing11-Sep-07 16:28 
AnswerRe: this demo can't modify the content of a mail in oe or winodws mail(vista) Pin
Klirik10-Dec-07 6:45
memberKlirik10-Dec-07 6:45 
QuestionModifying .. [modified] Pin
HStrix20-Aug-07 3:38
memberHStrix20-Aug-07 3:38 
QuestionMail Access In Service Mode Pin
surez12-Aug-07 22:04
membersurez12-Aug-07 22:04 
QuestionHow get the access to IMAP folders? [modified] Pin
Dieter_Kr20-Jul-07 2:37
memberDieter_Kr20-Jul-07 2:37 
GeneralWindows Mail: IStoreNamespace/IStoreFolder RegisterNotification Pin
bask198218-Jun-07 17:43
memberbask198218-Jun-07 17:43 
GeneralBulk save attachments and replace with link Pin
baworth18-May-07 18:14
memberbaworth18-May-07 18:14 
QuestionIs there a way to have such functionality in C# Pin
aleccc2-May-07 4:41
memberaleccc2-May-07 4:41 
AnswerRe: Is there a way to have such functionality in C# Pin
Pablo Yabo2-May-07 4:53
memberPablo Yabo2-May-07 4:53 
GeneralCreate Folder Pin
Member #292037814-Feb-07 11:18
memberMember #292037814-Feb-07 11:18 
GeneralI want to save mail attachments Pin
V.A25-Dec-06 0:33
memberV.A25-Dec-06 0:33 
Questionhow can read the deleted mail from outlok express Pin
naveen padiyar10-Dec-06 19:46
membernaveen padiyar10-Dec-06 19:46 
QuestionCan I read attachments in-memory and parse it to the string Pin
BParsi4-Dec-06 17:56
memberBParsi4-Dec-06 17:56 
AnswerRe: Can I read attachments in-memory and parse it to the string Pin
BParsi4-Dec-06 17:57
memberBParsi4-Dec-06 17:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171207.1 | Last Updated 27 Mar 2006
Article Copyright 2004 by Pablo Yabo
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid