Click here to Skip to main content
Click here to Skip to main content

Another simple MAPI class

, 27 Feb 2004 CPOL
Rate this:
Please Sign up or sign in to vote.
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 Smile | :) )

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.
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.
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 Smile | :)

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)

Share

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

Comments and Discussions

 
GeneralMy vote of 1 PinmemberCpp_Com21-Jul-11 20:01 
GeneralThanks! Pinmemberyfital6-Apr-09 1:24 
GeneralRe: Thanks! PinmemberSyllasR5-May-09 10:27 
GeneralCc function PinmemberMember 193612512-Feb-09 2:40 
GeneralThankyou PinmemberPRMARJORAM15-Dec-08 4:58 
QuestionMAPI forgets to dial when using more than one DUN PinmemberPeter_W24-Aug-07 3:36 
GeneralFrom don't fill PinmemberJimmyO8-Jun-07 0:09 
QuestionDoes not compile with UNICODE Pinmemberpscholl25-May-07 6:23 
AnswerRe: Does not compile with UNICODE Pinmemberjself4430-May-07 18:07 
AnswerRe: Does not compile with UNICODE Pinmemberpscholl30-May-07 22:44 
GeneralRe: Does not compile with UNICODE Pinmemberjself4431-May-07 2:21 
GeneralRe: Does not compile with UNICODE PinmemberSyllasR5-May-09 10:57 
GeneralThanks, it was great help Pinmembergiriesh_sir7-May-07 1:01 
GeneralRe: Thanks, it was great help PinprotectorRob Manderson7-May-07 9:17 
QuestionCIMapi in a BSD licenced tcl API project? PinmemberMartin081511-Mar-07 4:11 
AnswerRe: CIMapi in a BSD licenced tcl API project? PinprotectorRob Manderson11-Mar-07 5:46 
QuestionHow to use theBat instead of outlook by way of sender? PinmemberMerinat8-Mar-07 23:20 
General'554 Message does not conform to standards' PinmemberSebastian Ledesma29-Jan-07 11:26 
QuestionHow to save email in file Pinmemberjigneshrpatel29-Jun-06 4:14 
QuestionLNK2005 error Pinmembergnojas50127-Jan-06 15:30 
AnswerRe: LNK2005 error Pinmemberphilipcunningham24-Jun-06 1:11 
GeneralSender's name PinmemberAlexEvans27-Sep-05 13:42 
GeneralMAPI and filehandles PinmemberTNTDeluxe5-Aug-05 4:19 
GeneralRe: MAPI and filehandles PinmemberCDRAIN6-Aug-07 22:16 
QuestionSaving the message to a file? Pinmemberihoapm19-Jan-05 16:37 
Generalshow dialog send mail Pinmemberalthuserh13-Sep-04 23:37 
GeneralRe: show dialog send mail Pinmembergaopan21-Oct-05 9:20 
Generalmemory leadk PinmemberNS8817-Jun-04 8:41 
GeneralRe: memory leadk PinmemberKris Kashtanova1-Aug-07 5:31 
QuestionOnly support send? PinmemberBugs Bunny28-Feb-04 9:07 
AnswerRe: Only support send? PinmemberJubjub28-Feb-04 13:00 
GeneralRe: Only support send? PinmemberBugs Bunny29-Feb-04 1:49 
AnswerRe: Only support send? PinmemberJörgen Sigvardsson28-Feb-04 13:58 
GeneralRe: Only support send? PineditorRob Manderson28-Feb-04 23:51 
GeneralRe: Only support send? PinmemberBugs Bunny29-Feb-04 1:37 
GeneralRe: Only support send? PinmemberJubjub29-Feb-04 2:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141022.2 | Last Updated 28 Feb 2004
Article Copyright 2004 by Rob Manderson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid