Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Reading and Writing Messages in Outlook Express

, 27 Mar 2006
This article was done to provide an example of IStoreNamespace / IStoreFolder.
/* $Id: DemoDlg.cpp,v 1.3 2005/08/16 17:15:09 pyabo Exp $
 *
 * Author: Pablo Yabo (pablo.yabo@nektra.com)
 *
 * Copyright (c) 2004-2006 Nektra S.A., Buenos Aires, Argentina.
 * All rights reserved.
 *
 * You may modify and/or integrate this code into your commercial software
 * keeping this notice.
 *
 **/

#include "stdafx.h"
#include "demo.h"
#include "MessageTreeDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMessageTreeDlg dialog


CMessageTreeDlg::CMessageTreeDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMessageTreeDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CMessageTreeDlg)
	m_bodyContent = _T("");
	m_displayName = _T("");
	m_propValue = _T("");
	//}}AFX_DATA_INIT

	m_pMimeMsg = NULL;
	m_pAllocator = NULL;
	m_pPropertySet = NULL;
	m_isMultiPart = FALSE;
	m_isParentMultiPart = FALSE;
}


void CMessageTreeDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CMessageTreeDlg)
	DDX_Control(pDX, IDC_REM_ATTACH, m_btnRemAttach);
	DDX_Control(pDX, IDC_BUTTON_GO_PREV, m_btnGoPrev);
	DDX_Control(pDX, IDC_BUTTON_GO_PARENT, m_btnGoParent);
	DDX_Control(pDX, IDC_BUTTON_GO_NEXT, m_btnGoNext);
	DDX_Control(pDX, IDC_BUTTON_GO_CHILD, m_btnGoChild);
	DDX_Control(pDX, IDC_BUTTON_DEL_BODY, m_btnDelBody);
	DDX_Control(pDX, IDC_BUTTON_INS_NEXT, m_btnInstNext);
	DDX_Control(pDX, IDC_BUTTON_INS_CHILD, m_btnInstChild);
	DDX_Control(pDX, IDC_BUTTON_SET_CONTENT, m_btnSetContent);
	DDX_Control(pDX, IDC_LIST_ATTACHS, m_attachs);
	DDX_Control(pDX, IDC_COMBO_PROP_NAME, m_propNames);
	DDX_Text(pDX, IDC_EDIT_CONTENT, m_bodyContent);
	DDX_Text(pDX, IDC_EDIT_DISPLAY_NAME, m_displayName);
	DDX_Text(pDX, IDC_EDIT_PROP_VALUE, m_propValue);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CMessageTreeDlg, CDialog)
	//{{AFX_MSG_MAP(CMessageTreeDlg)
	ON_CBN_EDITCHANGE(IDC_COMBO_PROP_NAME, OnEditchangeComboPropName)
	ON_CBN_SELCHANGE(IDC_COMBO_PROP_NAME, OnSelchangeComboPropName)
	ON_BN_CLICKED(IDC_BUTTON_GO_CHILD, OnButtonGoChild)
	ON_BN_CLICKED(IDC_BUTTON_GO_NEXT, OnButtonGoNext)
	ON_BN_CLICKED(IDC_BUTTON_GO_PARENT, OnButtonGoParent)
	ON_BN_CLICKED(IDC_BUTTON_GO_PREV, OnButtonGoPrev)
	ON_BN_CLICKED(IDC_BUTTON_REM_PROP, OnButtonRemProp)
	ON_BN_CLICKED(IDC_BUTTON_SAVE_CHANGES, OnButtonSaveChanges)
	ON_BN_CLICKED(IDC_BUTTON_SET_CONTENT, OnButtonSetContent)
	ON_BN_CLICKED(IDC_BUTTON_SET_PROP_VALUE, OnButtonSetPropValue)
	ON_BN_CLICKED(IDC_BUTTON_INS_CHILD, OnButtonInsChild)
	ON_BN_CLICKED(IDC_BUTTON_INS_NEXT, OnButtonInsNext)
	ON_BN_CLICKED(IDC_BUTTON_DEL_BODY, OnButtonDelBody)
	ON_BN_CLICKED(IDC_ADD_ATTACH, OnAddAttach)
	ON_BN_CLICKED(IDC_REM_ATTACH, OnRemAttach)
	ON_LBN_SELCHANGE(IDC_LIST_ATTACHS, OnSelchangeListAttachs)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMessageTreeDlg message handlers
CMessageTreeDlg::~CMessageTreeDlg()
{
	if(m_pMimeMsg) {
		m_pMimeMsg->Release();
		m_pMimeMsg = NULL;
	}
	if(m_pAllocator) {
		m_pAllocator->Release();
	}
	if(m_pPropertySet) {
		m_pPropertySet->Release();
	}
}

BOOL CMessageTreeDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	HRESULT hr;

	// TODO: Add extra initialization here
	hr = CoCreateInstance(CLSID_IMimeAllocator, 
							NULL, CLSCTX_INPROC_SERVER, 
							IID_IMimeAllocator, 
							(LPVOID *) &m_pAllocator);
	if(m_pAllocator == NULL) {
		OutputDebugString("CMessageTreeDlg::OnInitDialog: m_pAllocator.\n");
	}

	m_btnRemAttach.EnableWindow(FALSE);

	UpdateBodyInfo();
	UpdateAttachments();

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CMessageTreeDlg::SetMessage(IStoreFolder *pFolder, STOREFOLDERID msgId)
{
	ASSERT(pFolder != NULL);

	HRESULT hr;

	// 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");
		MessageBox(_T("Cannot open message."), _T("Demo Error"), MB_OK);
		return;
	}
	
	hr = m_pMimeMsg->GetBody(IBL_ROOT, 0, &m_hCurBody);
	if(FAILED(hr)) {
		OutputDebugString("OEMessage::SetMessage: Cannot get root body.\n");
		MessageBox(_T("Cannot get root body."), _T("Demo Error"), MB_OK);
		return;
	}
}

CString CMessageTreeDlg::GetContentType(HBODY hBody)
{
	HRESULT hr;
	PROPVARIANT propValue;
	CString cntType = _T("");

	propValue.vt = VT_LPSTR;

	hr = m_pMimeMsg->GetBodyProp(hBody, PIDTOSTR(PID_HDR_CNTTYPE), 0, &propValue);
	if(SUCCEEDED(hr)) {
		cntType = propValue.pszVal;
		cntType.MakeLower();

		PropVariantClear(&propValue);
	}

	return cntType;
}

void CMessageTreeDlg::UpdateBodyInfo()
{
	IMimeBody *pMimeBody = NULL;
	LPSTR displayName;
	ENCODINGTYPE encType;
	IStream *pBodyStream = NULL;
	ULONG ulRead;
	WCHAR lpszwBody[2048];
	char lpszBody[2048];
	HRESULT hr;
	CString parentCntType;
	HBODY hBody;

	if(m_pPropertySet) {
		m_pPropertySet->Release();
		m_pPropertySet = NULL;
	}

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

	m_propValue = _T("");
	
	FillCombo();

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

		hr = pMimeBody->GetDisplayName(&displayName);
		if(SUCCEEDED(hr)) {
			m_displayName = displayName;
			CoTaskMemFree(displayName);
		}
		else {
			m_displayName = _T("");
		}
		
		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_isMultiPart = (m_cntType.Find(_T("multipart")) == 0);

		m_isParentMultiPart = FALSE;

		m_btnGoChild.EnableWindow(FALSE);
		m_btnGoParent.EnableWindow(FALSE);
		m_btnGoNext.EnableWindow(FALSE);
		m_btnGoPrev.EnableWindow(FALSE);

		hr = m_pMimeMsg->GetBody(IBL_PARENT, m_hCurBody, &hBody);
		if(SUCCEEDED(hr)) {
			m_btnGoParent.EnableWindow(TRUE);
			parentCntType = GetContentType(hBody);
			m_isParentMultiPart = (parentCntType.Find(_T("multipart")) == 0);
		}
		hr = m_pMimeMsg->GetBody(IBL_FIRST, m_hCurBody, &hBody);
		if(SUCCEEDED(hr)) {
			m_btnGoChild.EnableWindow(TRUE);
		}
		hr = m_pMimeMsg->GetBody(IBL_NEXT, m_hCurBody, &hBody);
		if(SUCCEEDED(hr)) {
			m_btnGoNext.EnableWindow(TRUE);
		}
		hr = m_pMimeMsg->GetBody(IBL_PREVIOUS, m_hCurBody, &hBody);
		if(SUCCEEDED(hr)) {
			m_btnGoPrev.EnableWindow(TRUE);
		}

		// only text bodies can be set. The others are body trees and must be constructed.
		m_btnSetContent.EnableWindow(m_isTextBody);

		// only multipart bodies can have children.
		m_btnInstChild.EnableWindow(m_isMultiPart);
		m_btnInstNext.EnableWindow(m_isParentMultiPart);

		// multipart bodies cannot be deleted.
		m_btnDelBody.EnableWindow(!m_isMultiPart);

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

	UpdateData(FALSE);
}

void CMessageTreeDlg::FillCombo()
{
	ASSERT(m_pMimeMsg != NULL);

	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(m_propNames.GetCount()) {
		m_propNames.SetCurSel(0);
		UpdatePropValue();
	}

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

void CMessageTreeDlg::UpdatePropValue(CString newValue)
{
	PROPVARIANT propValue;
	HRESULT hr;
	CString cmbValue;

	if(newValue.IsEmpty()) {
		m_propNames.GetWindowText(cmbValue);
	}
	else {
		cmbValue = newValue;
	}

	if(cmbValue.IsEmpty()) {
		m_propValue = _T("Invalid");
	}
	else {
		propValue.vt = VT_LPSTR;
		hr = m_pPropertySet->GetProp((LPCTSTR) cmbValue, 0, &propValue);
		if(SUCCEEDED(hr)) {
			//m_propNames.AddString(propValue.pszVal);
			m_propValue = propValue.pszVal;
			m_pAllocator->PropVariantClear(&propValue);
		}
		else {
			m_propValue = _T("Invalid");
		}
	}

	UpdateData(FALSE);
}

void CMessageTreeDlg::UpdateAttachments()
{
	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: 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);
	}
}

void CMessageTreeDlg::OnEditchangeComboPropName() 
{
	UpdateData(TRUE);

	UpdatePropValue();
}

void CMessageTreeDlg::OnSelchangeComboPropName() 
{
	CString newValue;
	int nSel;

	nSel = m_propNames.GetCurSel();
	if(nSel == CB_ERR) {
		m_propValue = _T("Invalid");
		OutputDebugString("CMessageTreeDlg::OnSelchangeComboPropName: Cannot get property name.\n");
		return;
	}

	m_propNames.GetLBText(nSel, newValue);

	UpdatePropValue(newValue);
}

void CMessageTreeDlg::OnButtonGoChild() 
{
	HBODY hBody;
	HRESULT hr;

	hr = m_pMimeMsg->GetBody(IBL_FIRST, m_hCurBody, &hBody);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot get body."), _T("Demo Error"), MB_OK);
	}
	else {
		m_hCurBody = hBody;
		UpdateBodyInfo();
	}
}

void CMessageTreeDlg::OnButtonGoNext() 
{
	HBODY hBody;
	HRESULT hr;

	hr = m_pMimeMsg->GetBody(IBL_NEXT, m_hCurBody, &hBody);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot get body."), _T("Demo Error"), MB_OK);
	}
	else {
		m_hCurBody = hBody;
		UpdateBodyInfo();
	}
}

void CMessageTreeDlg::OnButtonGoParent() 
{
	HBODY hBody;
	HRESULT hr;

	hr = m_pMimeMsg->GetBody(IBL_PARENT, m_hCurBody, &hBody);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot get body."), _T("Demo Error"), MB_OK);
	}
	else {
		m_hCurBody = hBody;
		UpdateBodyInfo();
	}
}

void CMessageTreeDlg::OnButtonGoPrev() 
{
	HBODY hBody;
	HRESULT hr;

	hr = m_pMimeMsg->GetBody(IBL_PREVIOUS, m_hCurBody, &hBody);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot get body."), _T("Demo Error"), MB_OK);
	}
	else {
		m_hCurBody = hBody;
		UpdateBodyInfo();
	}
}

void CMessageTreeDlg::OnButtonRemProp() 
{
	HRESULT hr;
	CString cmbValue;

	UpdateData(TRUE);

	m_propNames.GetWindowText(cmbValue);

	hr = m_pMimeMsg->DeleteBodyProp(m_hCurBody, cmbValue);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot remove property."), _T("Demo Error"), MB_OK);
	}
	else {
		int nSel;

		nSel = m_propNames.GetCurSel();
		if(nSel != CB_ERR) {
			m_propNames.DeleteString(nSel);

			if(m_propNames.GetCount() > 0) {
				m_propNames.SetCurSel(0);
			}

			UpdatePropValue();
		}
	}
}

void CMessageTreeDlg::OnButtonSetPropValue() 
{
	PROPVARIANT propValue;
	HRESULT hr;
	CString cmbValue;

	UpdateData(TRUE);

	m_propNames.GetWindowText(cmbValue);

	propValue.vt = VT_LPSTR;
	propValue.pszVal = (LPSTR) m_pAllocator->Alloc(m_propValue.GetLength()+1);

	if(propValue.pszVal == NULL) {
		MessageBox(_T("Cannot set property: Alloc."), _T("Demo Error"), MB_OK);
		return;
	}

	strcpy(propValue.pszVal, m_propValue);

	hr = m_pMimeMsg->SetBodyProp(m_hCurBody, cmbValue, NOFLAGS, &propValue);
	
	m_pAllocator->Free(propValue.pszVal);

	if(FAILED(hr)) {
		MessageBox(_T("Cannot set property: SetBodyProp."), _T("Demo Error"), MB_OK);
	}
	else {
		int nSel;

		nSel = m_propNames.GetCurSel();
		if(nSel == CB_ERR) {
			m_propNames.AddString(cmbValue);
		}
	}
}

void CMessageTreeDlg::OnButtonSaveChanges() 
{
	HRESULT hr;

	hr = m_pMimeMsg->Commit(COMMIT_REUSESTORAGE);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot save changes."), _T("Demo Error"), MB_OK);
	}
}

void CMessageTreeDlg::OnButtonSetContent() 
{
	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: 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: 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: 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: 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();
	}
}


void CMessageTreeDlg::OnButtonInsChild() 
{
	HRESULT hr;
	HBODY newBody;

	hr = m_pMimeMsg->InsertBody((BODYLOCATION) IBL_FIRST, m_hCurBody, &newBody);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot create body: InsertBody."), _T("Demo Error"), MB_OK);
	}
	else {
		m_hCurBody = newBody;
		UpdateBodyInfo();
	}
}

void CMessageTreeDlg::OnButtonInsNext() 
{
	HRESULT hr;
	HBODY newBody;

	hr = m_pMimeMsg->InsertBody((BODYLOCATION) IBL_NEXT, m_hCurBody, &newBody);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot create body: InsertBody."), _T("Demo Error"), MB_OK);
	}
	else {
		m_hCurBody = newBody;
		UpdateBodyInfo();
	}
}

void CMessageTreeDlg::OnButtonDelBody() 
{
	HRESULT hr;

	hr = m_pMimeMsg->DeleteBody(m_hCurBody, DELETE_PROMOTE_CHILDREN);
	if(FAILED(hr)) {
		MessageBox(_T("Cannot delete body: DeleteBody."), _T("Demo Error"), MB_OK);
	}
	else {
		hr = m_pMimeMsg->GetBody(IBL_ROOT, 0, &m_hCurBody);
		if(FAILED(hr)) {
			MessageBox(_T("Cannot get root body."), _T("Demo Error"), MB_OK);
			return;
		}

		UpdateBodyInfo();
	}
}

void CMessageTreeDlg::OnAddAttach() 
{
	HRESULT hr;
	HBODY hNewBody;
	char filename[1024] = "";
	OPENFILENAME ofn = { 0 };

	ofn.lStructSize  = sizeof(ofn);
	ofn.hwndOwner    = m_hWnd;
	ofn.lpstrFilter  = "All files (*.*)\0*.*\0";
	ofn.nFilterIndex = 1;
	ofn.lpstrFile    = filename;
	ofn.nMaxFile     = sizeof(filename);
	ofn.Flags        = OFN_FILEMUSTEXIST;
	if(GetOpenFileName(&ofn)) {
		hr = m_pMimeMsg->AttachFile(filename, NULL, &hNewBody);
		if(FAILED(hr)) {
			MessageBox(_T("Cannot attach file: AttachFile."), _T("Demo Error"), MB_OK);
		}
		else {
			UpdateAttachments();
		}
	}
}

void CMessageTreeDlg::OnRemAttach() 
{
	int sel;
	HRESULT hr;
	HBODY hBody;

	sel = m_attachs.GetCurSel();

	if(sel >= 0) {
		hBody = (HBODY) m_attachs.GetItemData(sel);
		hr = m_pMimeMsg->DeleteBody(hBody, 0);
		if(FAILED(hr)) {
			OutputDebugString(_T("OnRemAttach: DeleteBody.\n"));
		}

		UpdateAttachments();
	}
}

void CMessageTreeDlg::OnSelchangeListAttachs() 
{
	m_btnRemAttach.EnableWindow(m_attachs.GetCurSel() >= 0);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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

Share

About the Author

Pablo Yabo
Technical Lead http://www.nektra.com
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 specialized in Outlook Express and Outlook Plugin Development.
Now there is a new Windows Live Mail API 2011 / 2009 that works on all the platforms Windows 7, Vista and XP
Follow on   Twitter

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