Table of contents
The application has the following features:
- List local folders.
- Create / rename / delete local folder.
- List messages of a local folder.
- Get message properties.
- Get message source.
- Create / copy / move / delete messages.
- Mark messages as Read or Unread.
- List body properties or headers.
- Get / set property values.
- Navigate body structure.
- Get / set body content.
- Insert bodies.
- List / add / remove attachments.
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.
void CDemoDlg::AddFolders(STOREFOLDERID dwFolderId)
{
FOLDERPROPS props;
HENUMSTORE hEnum;
int nIndex;
hEnum = NULL;
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) {
m_listFolder.SetItemData(nIndex, props.dwFolderId);
AddFolders(props.dwFolderId);
}
hr = m_pStoreNamespace->GetNextSubFolder(hEnum, &props);
}
if(hEnum) {
m_pStoreNamespace->GetSubFolderClose(hEnum);
}
}
MESSAGEPROPS msgProps;
HENUMSTORE hEnumMsg;
CString item;
int nIndex;
hEnumMsg = NULL;
msgProps.cbSize = sizeof(MESSAGEPROPS);
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;
nIndex = m_listMsg.AddString(item);
if(nIndex != LB_ERR && nIndex != LB_ERRSPACE) {
m_listMsg.SetItemData(nIndex, msgProps.dwMessageId);
}
m_pStoreFolder->FreeMessageProps(&msgProps);
hr = m_pStoreFolder->GetNextMessage(hEnumMsg,
0, &msgProps);
}
if(hEnumMsg) {
m_pStoreFolder->GetMessageClose(hEnumMsg);
}
void CMsgDlg::OnView()
{
ULONG ulReaded = 0;
int nIndex;
STOREFOLDERID dwSelMsg;
HRESULT hr;
IStream *pTextStream;
char buffer[4096];
nIndex = m_listMsg.GetCurSel();
if(nIndex == LB_ERR) {
MessageBox(_T("Select a message first."),
_T("Demo Error"));
return;
}
dwSelMsg = m_listMsg.GetItemData(nIndex);
hr = m_pStoreFolder->OpenMessage(dwSelMsg,
IID_IStream, (VOID **) &pTextStream);
if(FAILED(hr)) {
MessageBox(_T("Error opening message."),
_T("Demo Error"));
return;
}
CMsgSrcDlg msgSrcDlg;
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)) {
msgSrcDlg.DoModal();
}
pTextStream->Release();
}
IStream *newMail = NULL;
MESSAGEID msgId;
HRESULT hr;
ULONG len;
CString msgSource;
...
hr = m_pFolder->CreateStream(0, 0, &newMail, &msgId);
if(FAILED(hr)) {
MessageBox(_T("Cannot Create Stream."),
_T("Demo Error"));
return;
}
hr = newMail->Write((const char *) msgSource,
msgSource.GetLength(), &len);
if(FAILED(hr)) {
MessageBox(_T("Cannot Write message."),
_T("Demo Error"));
newMail->Release();
return;
}
hr = m_pFolder->CommitStream(0, 0, 0,
newMail, msgId, NULL);
if(FAILED(hr)) {
MessageBox(_T("Cannot Commit Stream."),
_T("Demo Error"));
newMail->Release();
return;
}
newMail->Release();
hr = pFolder->OpenMessage(msgId,
IID_IMimeMessage,
(LPVOID*) &m_pMimeMsg);
if(FAILED(hr)) {
OutputDebugString("CMessageTreeDlg::"
"SetMessage: OpenMessage.\n");
return;
}
hr = m_pMimeMsg->GetBody(IBL_ROOT, 0, &m_hCurBody);
if(FAILED(hr)) {
OutputDebugString("OEMessage::SetMessage:"
" Cannot get root body.\n");
return;
}
...
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();
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();
}
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;
}
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 {
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);
}
while(1) {
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(m_cntType.Find(_T("text")) == 0) {
encType = IET_UNICODE;
m_isTextBody = TRUE;
}
...
m_bodyContent = _T("");
hr = pMimeBody->GetData(IET_UNICODE,
&pBodyStream);
if(FAILED(hr)) {
OutputDebugString("OEMessage::GetBodyText: GetData\n");
break;
}
if(encType == IET_UNICODE) {
do {
hr = pBodyStream->Read(lpszwBody,
sizeof(lpszwBody)-sizeof(WCHAR),
&ulRead);
if(FAILED(hr)) {
OutputDebugString("OEMessage::GetBodyText: Read\n");
}
else if(ulRead != 0) {
lpszwBody[ulRead/2] = '\0';
m_bodyContent += (WCHAR *) lpszwBody;
}
} while(ulRead != 0);
}
else {
do {
hr = pBodyStream->Read(lpszBody,
sizeof(lpszBody)-sizeof(char),
&ulRead);
if(FAILED(hr)) {
OutputDebugString("OEMessage::GetBodyText: Read\n");
}
else if(ulRead != 0) {
lpszBody[ulRead] = '\0';
m_bodyContent += lpszBody;
}
} while(ulRead != 0);
}
pBodyStream->Release();
break;
}
if(pMimeBody) {
pMimeBody->Release();
}
HRESULT hr;
ULONG ulLength, ulWritten;
BSTR bstr = NULL;
IStream *pStream = NULL;
IMimeBody *pMimeBody = NULL;
PROPVARIANT propValue;
UpdateData(TRUE);
while(1) {
hr = CreateStreamOnHGlobal(NULL,
TRUE,
&pStream);
if(FAILED(hr)) {
MessageBox(_T("Cannot set content:" )
_T(" CreateStreamOnHGlobal."),
_T("Demo Error"), MB_OK);
break;
}
ulLength = m_bodyContent.GetLength() + 1;
bstr = m_bodyContent.AllocSysString();
hr = pStream->Write((LPWSTR) bstr,
ulLength * sizeof(WCHAR),
&ulWritten);
if(FAILED(hr)) {
MessageBox(_T("Cannot set content: Write."),
_T("Demo Error"), MB_OK);
break;
}
hr = pStream->Commit(STGC_DEFAULT);
if(FAILED(hr)) {
MessageBox(_T("Cannot set content: Commit."),
_T("Demo Error"), MB_OK);
break;
}
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;
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;
}
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);
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();
}
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
.