Click here to Skip to main content
15,868,292 members
Articles / Desktop Programming / MFC
Article

Another simple MAPI class

Rate me:
Please Sign up or sign in to vote.
4.77/5 (16 votes)
27 Feb 2004CPOL5 min read 132.8K   2.3K   24   36
Adding MAPI functionality to your application.

Introduction

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.

Background

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.
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.
C++
void CBugReport::OnOK() 
{
     CIMapi mail;

    if (mail.Error() == IMAPI_SUCCESS)
    {
         mail.To("ultramaroon@cox.net");            //  Set recipient name (me)
         mail.To("someoneelse@somewhereelse.com");  //  Second recipient
         mail.Cc("cc@cc.com");                      //  CC recipient
         mail.From("user@somewhere.com");           //  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 :)

History

28 February 2004 - Initial CodeProject release.

License

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


Written By
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. http://www.kycolonels.org

Comments and Discussions

 
GeneralMy vote of 1 Pin
Cpp_Com21-Jul-11 20:01
Cpp_Com21-Jul-11 20:01 
GeneralThanks! Pin
yfital6-Apr-09 1:24
yfital6-Apr-09 1:24 
GeneralRe: Thanks! Pin
SyllasR5-May-09 10:27
SyllasR5-May-09 10:27 
GeneralCc function Pin
Member 193612512-Feb-09 2:40
Member 193612512-Feb-09 2:40 
GeneralThankyou Pin
PRMARJORAM15-Dec-08 4:58
PRMARJORAM15-Dec-08 4:58 
QuestionMAPI forgets to dial when using more than one DUN Pin
Peter_W24-Aug-07 3:36
Peter_W24-Aug-07 3:36 
GeneralFrom don't fill Pin
JimmyO8-Jun-07 0:09
JimmyO8-Jun-07 0:09 
QuestionDoes not compile with UNICODE Pin
pscholl25-May-07 6:23
pscholl25-May-07 6:23 
AnswerRe: Does not compile with UNICODE Pin
jself4430-May-07 18:07
jself4430-May-07 18:07 
AnswerRe: Does not compile with UNICODE Pin
pscholl30-May-07 22:44
pscholl30-May-07 22:44 
GeneralRe: Does not compile with UNICODE Pin
jself4431-May-07 2:21
jself4431-May-07 2:21 
GeneralRe: Does not compile with UNICODE Pin
SyllasR5-May-09 10:57
SyllasR5-May-09 10:57 
GeneralThanks, it was great help Pin
giriesh_sir7-May-07 1:01
giriesh_sir7-May-07 1:01 
GeneralRe: Thanks, it was great help Pin
Rob Manderson7-May-07 9:17
protectorRob Manderson7-May-07 9:17 
But of course! Go for it!

Rob Manderson

I'm working on a version for Visual Lisp++

My blog http://blogs.wdevs.com/ultramaroon/[^]
My blog mirror http://robmanderson.blogspot.com[^]

QuestionCIMapi in a BSD licenced tcl API project? Pin
Martin081511-Mar-07 4:11
professionalMartin081511-Mar-07 4:11 
AnswerRe: CIMapi in a BSD licenced tcl API project? Pin
Rob Manderson11-Mar-07 5:46
protectorRob Manderson11-Mar-07 5:46 
QuestionHow to use theBat instead of outlook by way of sender? Pin
Merinat8-Mar-07 23:20
Merinat8-Mar-07 23:20 
General'554 Message does not conform to standards' Pin
Sebastian Ledesma29-Jan-07 11:26
Sebastian Ledesma29-Jan-07 11:26 
QuestionHow to save email in file Pin
jigneshrpatel29-Jun-06 4:14
jigneshrpatel29-Jun-06 4:14 
QuestionLNK2005 error Pin
gnojas50127-Jan-06 15:30
gnojas50127-Jan-06 15:30 
AnswerRe: LNK2005 error Pin
philipcunningham24-Jun-06 1:11
philipcunningham24-Jun-06 1:11 
GeneralSender's name Pin
AlexEvans27-Sep-05 13:42
AlexEvans27-Sep-05 13:42 
GeneralMAPI and filehandles Pin
TNTDeluxe5-Aug-05 4:19
TNTDeluxe5-Aug-05 4:19 
GeneralRe: MAPI and filehandles Pin
CDRAIN6-Aug-07 22:16
CDRAIN6-Aug-07 22:16 
QuestionSaving the message to a file? Pin
ihoapm19-Jan-05 16:37
ihoapm19-Jan-05 16: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.