Introduction
I've been using simple MAPI for years, and recently, I had to send some mail programmatically and was reminded of the dreaded Outlook security warnings. So, I decided to look into the extended MAPI. What I found was a huge collection of samples that did one thing or another, but nothing that really did everything related to messages that I needed. This class, hopefully, covers everything an average user would need to do, including sending and receiving mail including attachments, multiple recipients, CC, BCC, etc. I did not put any code to manipulate information stores or services. See the example CMapiAdmin
on CodeProject for this functionality. I've also tested this class using Unicode as well as ANSI strings.
Using CMapiEx
I've included the sample TestMAPI
(link above) to show two cases. The project has been updated to Visual Studio 8 (2005), but I believe could be adapted to nearly every version of Visual Studio and Embedded Visual Studio. It's been tested in WinXP using Office 2003, and on Windows Mobile 2003. With a small amount of work removing the MFC code from the class (basically just CString
), the class will work on Windows SmartPhone as well. Some of the newer code, including Outlook Forms support and AddressBook
support is not supported in Windows Mobile.
To use the class, simply do the following:
- Call
CMAPIEx::Init()
and Login()
to initialize and login to MAPI.
- Use
OpenMessageStore()
to open the desired store, i.e., "SMS"; (NULL
for the default store).
- To receive, call
OpenInbox()
and GetContents()
followed by a looping GetNextMessage()
call for each message (optionally reading in only the unread messages).
- Use
HasAttachment
and SaveAttachments()
to access the message's attachments.
void ReceiveTest(CMAPIEx& mapi)
{
if(mapi.OpenInbox() && mapi.GetContents()) {
CMAPIMessage message;
while(mapi.GetNextMessage(message,TRUE)) {
printf("Message from '%s' subject '%s'\n",
message.GetSenderName(),message.GetSubject());
if(message.HasAttachments()) {
printf("saving attachments...");
message.SaveAttachments(MSG_ATTACHMENT_FOLDER);
printf("done\n");
}
}
}
}
To send, call OpenOutbox()
and CMapiMessage::Create()
. Then, set the message properties, and then call CMapiMessage::Send()
:
void SendTest(CMAPIEx& mapi)
{
if(mapi.OpenOutbox()) {
CMAPIMessage message;
if(message.Create(&mapi,IMPORTANCE_LOW)) {
message.SetSenderName(FROM_NAME);
message.SetSenderEmail(FROM_EMAIL);
message.SetSubject(_T("Subject"));
message.SetBody(_T("Body"));
message.AddRecipient(TO_EMAIL);
message.AddRecipient(TO_EMAIL2);
message.AddAttachment(MSG_ATTACHMENT);
if(message.Send()) printf("Sent Successfully");
}
}
}
Use Logout()
and CMAPIEx::Term()
to exit MAPI. Obviously, if you want to try CMAPIEx
in a Unicode project, you'll have to use wprintf
instead.
RTF/HTML Support
A lot of people requested support for HTML messages. After doing a bunch of research and testing, I found that HTML is actually usually stored in the PR_RTF_COMPRESSED
property, and that it can be decoded into HTML if it contains the text \\fromhtml
. Thanks to Lucian Wischik for his example, the decoding code in MAPIEx is basically lifted right from his example (see the source for the address). I took it a step further by allowing you to set the RTF
property with HTML in order to send an HTML email. Take a look at the SetRTF example in the sample TestMAPI project above, for more details.
.NET Wrapper for CMAPIEx
I originally wrote the NetMAPI
wrapper as a proof of concept; it has little functionality beyond the basics. However over the last few months, I received so many questions and requests for enhancements for .NET that I have completely rewritten NetMAPI
to be a thin layer above the Win32 DLL, CMAPIEx
. NetMAPI
offers support for nearly every feature provided in the C++ interface. The included sample above contains a .NET console project, TestNetMAPI
, illustrating its usage.
To use the wrapper, first make sure your program has a reference to NetMAPI
, and that the compiled MAPIEx.dll is in your output folder or in your path. Then, use NetMAPI.Init()
and Login
functions to access it:
if(NetMAPI.Init()) {
NetMAPI mapi=new NetMAPI();
if(mapi.Login()) {
mapi.Logout();
}
NetMAPI.Term();
}
To receive in .NET (assumes you've logged in successfully):
- Open the message store you want to access
- Then open the inbox and get the contents table
- Iterate through the message using
GetNextMessage
- Don't forget to
Dispose
of the message when you're finished with it!
public static void ReceiveTest(NetMAPI mapi)
{
if(mapi.OpenInbox() && mapi.GetContents()) {
mapi.SortContents(false);
MAPIMessage message;
StringBuilder s=new StringBuilder(NetMAPI.DefaultBufferSize);
while(mapi.GetNextMessage(out message,true)) {
Console.Write("Message from '");
message.GetSenderName(s);
Console.Write(s.ToString()+"' (");
message.GetSenderEmail(s);
Console.Write(s.ToString()+"), subject '");
message.GetSubject(s);
Console.Write(s.ToString()+"', received: ");
message.GetReceivedTime(s,"%m/%d/%Y %I:%M %p");
Console.Write(s.ToString()+"\n");
message.Dispose();
}
}
}
To send a message (assumes you've logged in successfully):
- Open the message store you want to access
- Open the outbox
- Create a new message, set its priority if you like
- Set its properties, recipients and attachments
- Call
Send
public static void SendTest(NetMAPI mapi)
{
if(mapi.OpenOutbox()) {
MAPIMessage message=new MAPIMessage();
if(message.Create(mapi,MAPIMessage.Priority.IMPORTANCE_LOW)) {
message.SetSenderName("Noel");
message.SetSenderEmail("noel@nospam.com");
message.SetSubject("Subject");
message.SetBody("Body");
message.AddRecipient("noel@nospam.com");
message.AddRecipient("noel@nospam2.com");
if(message.Send()) Console.WriteLine("Sent Successfully");
}
}
}
See the ContactsTest
and FoldersTest
functions in TestNetMAPI
for more examples.
Use the Logout
and NetMAPI.Term()
call to close the wrapper. To use Unicode, compile CMAPIEx
with Unicode set, and change the DefaultCharSet
variable in NetMAPI
to CharSet.Unicode
.
Future Improvements
I'd like to add more support for Calendar and Task items. Outlook 2010 support is another thing coming down the pipeline, currently no tests have been done.
I couldn't figure out how to create a One-Off EntryID
in Windows Mobile, as the IAddressBook
interface is not implemented. This has the annoying effect of not sending the outgoing emails on these devices until you manually open the message in your Outbox and hit Send. If anyone knows how this is done, please let me know, and I will update it.
This code is free to use as long as the copyright notice remains at the top of the file and any enhancements or bug fixes are posted here for the community. I hope it helps someone skip the pain I went through!
History
- 07/01/2005 - Initial release
- 07/13/2005 - Small bug fixes and additions, see below for details
- 08/25/2005 - Small modifications and a .NET wrapper (see below)
- 01/27/2006 - Added a bunch of new commands for handling folders and messages (see below)
- 05/16/2006 - Added RTF and HTML support
- 06/02/2006 - Added external folder support, and fixed a couple of small bugs (thanks to all who reported the issues)
- 08/21/2006 - Reorganized files, added support for reading contacts, notifications, and fixed some minor bugs
- 10/02/2006 - Complete rewrite of the .NET wrapper, with full MAPIEx access from .NET
- 11/01/2006 - Added support for many new fields, added
IMessage
Form support and writeable contacts
- 12/04/2009 - Enhanced performance, completed contacts and added rudimentary Calendar support
Detailed History
- Fixed a bug with attachments (thanks alan, see forum below for details).
- While in the attachment code, I modified
PR_ATTACH_FILENAME
to be the filename of the attachment instead of the full path.
- Added support for Outlook 2000. To use this class with Outlook 2000, you must call
CMAPIEx::Init
with a value of zero instead of the default MAPI_UNICODE
. Also, building the project for Unicode doesn't work with Office 2000 as far as I can tell (it responds with ANSI strings).
- Fixed the "mail stays in Outbox after it's sent" problem. See the posting below for more info.
- Added a priority field to
CMAPIMessage::Create
, you can now optionally set IMPORTANCE_LOW
or IMPORTANCE_HIGH
.
- You can now call
AddRecipient
repeatedly to send to more than one user.
- Updated project to Visual Studio 2008.
- Added a post build step to copy MAPIEx.dll to the test projects' output folders.
- Added subfolder support to the DLL, you can now iterate through and directly open subfolders.
- Modified the
Login
command to take a profile name (thanks Chris, see forum for details).
- Added the
GetProfileName
command to find out what the current profile's name is (thanks Jason, author of CMAPIAdmin
).
- Added the
ReceivedTime
property and the GetReceivedTime
function to the message class (thanks again Chris, see forum).
- Added a
CopyMessage
function to copy a message between folders as well as the DeleteMessage
and MoveMessage
functions.
- Added the
CreateSubFolder
, DeleteSubFolder
functions.
- Added a
OpenSentItems
function.
- Added a new
RTF
property, this allows us to set RTF text and even HTML (see TestMAPI, for an example).
- Changed
GetBody
and GetRTF
to be filled on demand rather than when opening the message.
- MAPIEx can now extract SMTP email addresses from Exchange (EX) native addresses (see
FillSenderEmail
, for details).
- MAPIEx can now receive notifications from MAPI (i.e., for new mail arrivals, message deletion etc., see
NotificationTest
for an example.
- Added read only support for contacts, see
ContactsTest
for a sample).
- Fixed a bug with bad strings from MAPI properties (see
GetValidString
for more info).
- Added support for the
MAPI_NO_CACHE
flag (for Outlook 2003).
- Added the
OpenContacts
and OpenDrafts
functions.
- Added a
GetRowCount
for providing progress feedback for long folder operations.
- Added support for the
AddressList
form to get a list of recipients via the common UI. (C++ only, C# if people request it.)
- Added support for custom Named Properties in Contacts and Messages.
- Added
IMessageForm
support to show the default form for editing messages.
- Added
Save
, SubmitTime
, Sensitivity
, MessageFlags
, and DeliveryReceipt
properties to messages.
- Added support for enumerating recipients of a message.
MAPIContact
now supports the three email fields.
- Added many new fields to contacts such as
Company
, HomePage
, DisplayAs
, etc.
- Added support for creating contacts.
- Added rudimentary Calendar support (no create appointment functionality yet).
- Added extensive folder support, see
FolderTest
for an example.
- Added
CreateProfile
and DeleteProfile
because of multiple requests.
- Extended RTF and HTML support, added
GetMessageEditorFormat
to detect formats.
- Many small improvements and additions requested by the userbase (too many to list, see forum below for details).