Send HTML Via SMTP By Extending CMimeMessage
Extending the CMimeMessage class to send HTML messages
Introduction
I had a project recently that required sending emails with HTML content. I found an article by PJ Naughter on CodeProject that provided a couple of nice classes from Microsoft that are included in the Visual Studio 8.0 (Visual Studio 2005) development package. These classes are CMimeMessage
and CSMTPConnection
. This article explains how to use these classes to easily send email messages.
I looked through the CMimeMessage
class to see if there was a way to use it without modification to send emails with HTML content and didn't see a clear path, but may be wrong. I searched the internet for a while and found that some others were asking the same question. I didn't find an answer.
So I decided to extend the CMimeMessage
class to add a function that would add Text as HMTL to the Mail Message. It turned out to be pretty simple and could be done inline.
The Code
The first thing that I had to do was to extend the CMimeText
class, as it has a function MakeMimeHeader
that is automatically called to generate the email header for the Mime Section that it represents. I named this class CMimeHtml
.
I just simply copied the MakeMimeHeader
function from the existing CMimeText
class. The only modification was to replace the Content-Type: text/plain
with Content-Type: text/html
:
// This function is a copy of the CMimeText::MakeMimeHeader function. It has been
// updated to change the Content-Type text/html rather than text/plain.
//
// Make the MIME header
virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
{
char szBegin[256];
if (*szBoundary)
{
// this is not the only body part
Checked::memcpy_s(szBegin, sizeof(szBegin),
ATLMIME_SEPARATOR, sizeof(ATLMIME_SEPARATOR));
Checked::memcpy_s(szBegin+6, sizeof(szBegin)-6, szBoundary,
ATL_MIME_BOUNDARYLEN);
*(szBegin+(ATL_MIME_BOUNDARYLEN+6)) = '\0';
}
else
{
// this is the only body part, so output the full MIME header
Checked::memcpy_s(szBegin, sizeof(szBegin), ATLMIME_VERSION, sizeof(ATLMIME_VERSION));
}
_ATLTRY
{
// Here you can see that the Content-Type is text/html instead of text/plain. This is
// the only thing that makes the email message come out as text or html is this
// Content-Type marker that is written in the section header.
header.Format("%s\r\nContent-Type:
text/html;\r\n\tcharset=\"%s\"\r\nContent-Transfer-Encoding:
8bit\r\n\r\n",
szBegin, m_szCharset);
return TRUE;
}
_ATLCATCHALL()
{
return FALSE;
}
}
The next step was to add a function to the CMimeMessage
class to add an HTML Mime section. So I called the function AddHtml
. I copied the code from the AddText
function of the existing CMimeMessage
class. I named this class CExtMimeMessage
.
The only modification here is to replace the CMimeText
class with our extended CMimeHtml
class:
// Pretty much taken from the Microsoft CMimeMessage::AddText function.
// The only difference is that we add a CMimeHtml class to the BodyParts
// list instead of a CMimeText class.
//
// Add some text to the message at position nPos in the body parts list
// szText - the text
// nTextLen - the size of the text in bytes
// (optional - if not specified a _tcslen will be done)
// nPos - the position in the message at which to insert the text (optional)
// uiCodePage - the codepage (optional)
inline BOOL
AddHtml(LPCTSTR szText, int nTextLen = -1, int
nPos = 1, UINT uiCodePage = 0) throw()
{
if (szText == NULL)
return FALSE;
if (nPos < 1)
{
nPos = 1;
}
CAutoPtr<CMimeBodyPart> spNewText;
// Note that we are simply using the CExteMimeHtml Class (Extended CMimeText)
// The extended part is that the MakeMimeHeader function makes the Content-Type
// text/html instead of text/plain
CExtMimeHtml *pNewText = NULL;
ATLTRY(spNewText.Attach(pNewText = new CExtMimeHtml()));
if (!spNewText ||
!pNewText)
return FALSE;
BOOL bRet = pNewText->Initialize(szText, nTextLen, m_spMultiLanguage,
uiCodePage);
if (bRet)
{
_ATLTRY
{
POSITION currPos = m_BodyParts.FindIndex(nPos-1);
if (!currPos)
{
if
(!m_BodyParts.AddTail(spNewText))
bRet = FALSE;
}
else
{
if
(!m_BodyParts.InsertBefore(currPos, spNewText))
bRet = FALSE;
}
}
_ATLCATCHALL()
{
bRet = FALSE;
}
}
return bRet;
}
All done. All you have to do is copy in the included ExtMimeMessage.h file into your project, include "ExtMimeMessage.h", and use the CExtMimeMessage
class in exactly the same way that you would have used the original CMimeMessage
class.
I haven't seen any errors from using this class, but am open to all comments.
History
- 18th January, 2007: Initial post