Click here to Skip to main content
13,355,018 members (57,923 online)
Click here to Skip to main content
Add your own
alternative version


24 bookmarked
Posted 27 Feb 2004

Another simple MAPI class

, 27 Feb 2004
Rate this:
Please Sign up or sign in to vote.
Adding MAPI functionality to your application.


Many applications can usefully include the ability to send email. Any MFC SDI or MDI application, for instance, can easily send the underlying CDocument as an attachment to an email by simply adding a message map entry. The MFC implementation does, however, throw up an email window and expects user interaction. This class grew out of my need for an application to be able to send email without the user having to interact with the email transport subsystem.


You may have seen this code before. It was first published on CodeGuru[^] in 1999. (As an aside, I was surprised to see just how many sites host this code, having copied it from CodeGuru. Yeah, I do a little ego surfing now and then :) )

The real origin of this code was the implementation of email in MFC. I did a cut and paste of the MFC MAPI code and modified it to do what I needed. From that grew this class. There are still pieces of the original MFC code in the class as you'll see from the comments I copied.

Important Environmental Assumptions

The class relies on having a MAPI transport available and correctly configured. It's been tested, over the years, with Microsoft Outlook and Microsoft Outlook Express. If your system contains a correctly configured copy of either of those programs the class should work like a charm.

But that statement hides a multitude of complications. The most important complication is that Outlook Express (I don't have enough Outlook experience to take the risk of being specific about Outlook) needs to be configured on a per-user basis. If I log on to my computer (the one I'm writing this on) as myself Outlook Express comes up already configured to use my POP3 incoming and SMTP outgoing accounts.

If I create a new user account on this machine and that user account launches Outlook Express it will be launching the exact same binary as my account does. But Outlook Express references the user specific registry hive (HKEY_CURRENT_USER) to get its list of configured email accounts. A new user hasn't set those accounts so Outlook Express will be unable to send or receive mail for this new user account even though it works perfectly fine for my main account. (I suspect it's actually via configuration files that are specified in the registry. For the purpose of this section it doesn't really matter where the data is stored, merely that it's stored on a per-user basis (and to make it even more complicated, on a profile basis within each user account)).

With interactive log on sessions this usually doesn't bite much. It's pretty easy to notice that for some reason the application isn't sending email, note that this user hasn't configured Outlook Express, configure it and the problem is solved. (And in one sentence I've shrugged off a world of remote support problems.) Where it can bite is when you try to use the class in a service.

Services typically start when the machine is booted and continue to run until shutdown. They specifically don't require a logon session to run. So the question is, if they don't require a logon session, who are they running as? The most usual answer to this question is that they are running as the LocalSystem user. This is a special, highly privileged account over which even the Administrator account has little control. You can't log on as LocalSystem and therein lies the rub. If you can't log on as LocalSystem you can't easily configure Outlook Express for that account.

Now read the next sentence carefully. The class will not work in a service that runs as LocalSystem. Go back and read it again. If you want to use the class in a service you must run the service using an account which can log on and configure the mail transport system. Obviously, you must have logged on to that account at least once to configure email. (See below).

The class

Now we've got the caveats (and solutions to most problems) out of the way, let's look at the class. Here's the definition. <PRE lang=c++>class CIMapi { public: CIMapi(); ~CIMapi(); enum errorCodes { IMAPI_SUCCESS = 0, IMAPI_LOADFAILED, IMAPI_INVALIDDLL, IMAPI_FAILTO, IMAPI_FAILCC, IMAPI_FAILATTACH }; // Attributes void Subject(LPCTSTR subject); void Text(LPCTSTR text) { m_text = text; } UINT Error(); void From(LPCTSTR from) { m_from.lpszName = (LPTSTR) from; } static BOOL HasEmail(); // Operations BOOL To(LPCTSTR recip); BOOL Attach(LPCTSTR path, LPCTSTR name = NULL); BOOL Send(ULONG flags = 0); private: BOOL AllocNewTo(); MapiMessage m_message; MapiRecipDesc m_from; UINT m_error; CString m_text; ULONG (PASCAL *m_lpfnSendMail)(ULONG, ULONG, MapiMessage*, FLAGS, ULONG); static HINSTANCE m_hInstMail; static BOOL m_isMailAvail; }; Typical usage would be something like this. <PRE lang=c++>void CBugReport::OnOK() { CIMapi mail; if (mail.Error() == IMAPI_SUCCESS) { mail.To(""); // Set recipient name (me) mail.To(""); // Second recipient mail.Cc(""); // CC recipient mail.From(""); // Identify sender (not strictly // necessary since // MAPI will fill this in for you) mail.Subject("Test Email"); // Subject of this email mail.Attach("somefilename"); // Attaching a file mail.Attach("someotherfile", "different_name_for_recipient"); // Attach another file but give it // a different name inside the // email itself // Put text of message in body mail.Text("Body text for this email"); // Set body text mail.Send(); // Now send the mail! } else { // Do something appropriate to the error... } CDialog::OnOK(); } Well that looks pretty simple. Create a CIMapi instance, call some functions that set things like the recipient, the subject, append some attachments and send the email.

CIMapi internals

The constructor zeroes some internal structures and then attempts to load MAPI32.DLL. If it fails to load the DLL it sets an internal error variable to IMAPI_LOADFAILED and returns. If it succeeds in loading the DLL it searches for the MAPISendMail entry point in the DLL. If it finds this entry point it assumes it has a valid DLL, otherwise it sets the internal error variable to IMAPI_INVALIDDLL and returns.

You can avoid the overhead of loading MAPI32.DLL if all you want to know is whether MAPI is available by calling the static member function HasEmail() which checks the registry for the existence of a specific key and checks that MAPI32.DLL is somewhere on your path.

You'll see lots of calls to malloc(), realloc() and free() sprinkled through the code. The MAPI implementation goes back a long way and uses pointers to arrays of structures. Handling these using new and delete is a pain when you need to append a new structure to the array.

Using the class in a service

Firstly, reread Important Environmental Assumptions. Now include the source files in your project and #define SERVER for the project. The symbol doesn't change anything in the header file but it does suppress some UI related code in the class implementation. Frankly, I don't recommend trying to use this class in a service. I've found the whole subject is just too flaky and vaguely documented. You're on your own if you choose to go ahead and try using the class inside a service.

So why use MAPI instead of SMTP?

In general, if your mail transport eventually delegates to SMTP (for example, using Outlook Express), I'd recommend using an SMTP library in your code. That said, there are still many organisations using MAPI based email systems. I've also found that many SMTP libraries are a pain to use and indeed, in my own code, I prefer to use this class even when dealing with SMTP, as long as I can guarantee Outlook Express is available. Of course, this preference may simply be inertial :)


28 February 2004 - Initial CodeProject release.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Rob Manderson
United States United States
I've been programming for 35 years - started in machine language on the National Semiconductor SC/MP chip, moved via the 8080 to the Z80 - graduated through HP Rocky Mountain Basic and HPL - then to C and C++ and now C#.

I used (30 or so years ago when I worked for Hewlett Packard) to repair HP Oscilloscopes and Spectrum Analysers - for a while there I was the one repairing DC to daylight SpecAns in the Asia Pacific area.

Afterward I was the fourth team member added to the Australia Post EPOS project at Unisys Australia. We grew to become an A$400 million project. I wrote a few device drivers for the project under Microsoft OS/2 v 1.3 - did hardware qualification and was part of the rollout team dealing directly with the customer.

Born and bred in Melbourne Australia, now living in Scottsdale Arizona USA, became a US Citizen on September 29th, 2006.

I work for a medical insurance broker, learning how to create ASP.NET websites in VB.Net and C#. It's all good.

Oh, I'm also a Kentucky Colonel.

You may also be interested in...

Comments and Discussions

GeneralMy vote of 1 Pin
Cpp_Com21-Jul-11 21:01
memberCpp_Com21-Jul-11 21:01 
GeneralThanks! Pin
yfital6-Apr-09 2:24
memberyfital6-Apr-09 2:24 
GeneralRe: Thanks! Pin
SyllasR5-May-09 11:27
memberSyllasR5-May-09 11:27 
GeneralCc function Pin
Member 193612512-Feb-09 3:40
memberMember 193612512-Feb-09 3:40 
GeneralThankyou Pin
PRMARJORAM15-Dec-08 5:58
memberPRMARJORAM15-Dec-08 5:58 
QuestionMAPI forgets to dial when using more than one DUN Pin
Peter_W24-Aug-07 4:36
memberPeter_W24-Aug-07 4:36 
GeneralFrom don't fill Pin
JimmyO8-Jun-07 1:09
memberJimmyO8-Jun-07 1:09 
QuestionDoes not compile with UNICODE Pin
pscholl25-May-07 7:23
memberpscholl25-May-07 7:23 
AnswerRe: Does not compile with UNICODE Pin
jself4430-May-07 19:07
memberjself4430-May-07 19:07 
AnswerRe: Does not compile with UNICODE Pin
pscholl30-May-07 23:44
memberpscholl30-May-07 23:44 
GeneralRe: Does not compile with UNICODE Pin
jself4431-May-07 3:21
memberjself4431-May-07 3:21 
GeneralRe: Does not compile with UNICODE Pin
SyllasR5-May-09 11:57
memberSyllasR5-May-09 11:57 
GeneralThanks, it was great help Pin
giriesh_sir7-May-07 2:01
membergiriesh_sir7-May-07 2:01 
GeneralRe: Thanks, it was great help Pin
Rob Manderson7-May-07 10:17
protectorRob Manderson7-May-07 10:17 
QuestionCIMapi in a BSD licenced tcl API project? Pin
Martin081511-Mar-07 5:11
memberMartin081511-Mar-07 5:11 
AnswerRe: CIMapi in a BSD licenced tcl API project? Pin
Rob Manderson11-Mar-07 6:46
protectorRob Manderson11-Mar-07 6:46 
QuestionHow to use theBat instead of outlook by way of sender? Pin
Merinat9-Mar-07 0:20
memberMerinat9-Mar-07 0:20 
General'554 Message does not conform to standards' Pin
Sebastian Ledesma29-Jan-07 12:26
memberSebastian Ledesma29-Jan-07 12:26 
QuestionHow to save email in file Pin
jigneshrpatel29-Jun-06 5:14
memberjigneshrpatel29-Jun-06 5:14 
QuestionLNK2005 error Pin
gnojas50127-Jan-06 16:30
membergnojas50127-Jan-06 16:30 
AnswerRe: LNK2005 error Pin
philipcunningham24-Jun-06 2:11
memberphilipcunningham24-Jun-06 2:11 
GeneralSender's name Pin
AlexEvans27-Sep-05 14:42
memberAlexEvans27-Sep-05 14:42 
GeneralMAPI and filehandles Pin
TNTDeluxe5-Aug-05 5:19
memberTNTDeluxe5-Aug-05 5:19 
GeneralRe: MAPI and filehandles Pin
CDRAIN6-Aug-07 23:16
memberCDRAIN6-Aug-07 23:16 
I've the same problem on trying to open an XML file after the email's send.

If I take out the "Send()" line on your class everything works well, the problem seems origin on your call to "m_lpfnSendMail" .

Can I you help me? I would like to use your code on my App.

QuestionSaving the message to a file? Pin
ihoapm19-Jan-05 17:37
memberihoapm19-Jan-05 17:37 

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
Web01 | 2.8.180111.1 | Last Updated 28 Feb 2004
Article Copyright 2004 by Rob Manderson
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid