MAPI Mail without MFC

The Mail program sprang from the need to be able to notify a user from a program. It turned out that the easiest way to do this was to programatically send an e-mail to the user. Google "How To Send Email To Any Cell Phone" and you will find numerous articles on how to directly e-mail a person's cell phone to get their immediate attention.

I leave it to the user to figure out how to deal with the Microsoft "Security Updates". This code used to work fine for embedded systems but the security patches broke it. In theory, you can work out the details and get it to work again. Even if you are unable to modify the settings, this code is still useful for implementing a "Send" command. God bless Microsoft for making working code break in the name of security.

MAPIMail.h

/***********************************************************************************************************************
* Copyright (c) 2009-2010 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342, David.Cass.Tyler@gmail.com *
***********************************************************************************************************************/

#ifndef _MAPIMAIL_H__0316D5D4_B8BE_4FDC_83F7_90E2438E8A6D__INCLUDED_
#define _MAPIMAIL_H__0316D5D4_B8BE_4FDC_83F7_90E2438E8A6D__INCLUDED_

#define _WIN32_WINNT 0x0400

#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers

#include <WINDOWS.H>

#ifndef MAPI_H
#include <MAPI.H>
#endif

// The Class which Encapsulates the MAPI Connection/Session

class CMail

   {

public:

   // Constructors/Destructors

   CMail();
   ~CMail();

   bool IsValid ( void ) const;

protected:

   bool                 m_fIsValid;          // Is this a Valid Session?

   HINSTANCE            m_hMAPIDLL;          // Instance Handle of the MAPI DLL
   ULONG                m_nMAPIError;        // Last MAPI Error Value
   LHANDLE              m_hSession;          // MAPI Session Handle

   volatile LONG        m_lnSpinLock;        // The SpinLock
   DWORD                m_dwExclusiveThread; // The ID of the Exclusive Use Thread

   MapiMessage          m_stMAPIMessage;     // The Structure that Holds the Mail Message

   // MAPI.DLL Function Pointers

   LPMAPILOGON          MAPILogon;           // MAPILogon      Function Pointer
   LPMAPILOGOFF         MAPILogoff;          // MAPILogoff     Function Pointer
   LPMAPISENDMAIL       MAPISendMail;        // MAPISendMail   Function Pointer

   // Protected Functions

   bool                 AddRecipient         ( LPCSTR strRecipient, ULONG ulRecipClass );

public:

   // Send a Message

   bool                 Take                 ( bool fReturnEarly = false );

   bool                 Send                 ( MapiMessage & stMAPIMessage );

   bool                 To                   ( LPCSTR strTo );
   bool                 Cc                   ( LPCSTR strCc );
   bool                 Bcc                  ( LPCSTR strBcc );

   bool                 Subject              ( LPCSTR strSubject );
   bool                 Message              ( LPCSTR strMessage );
   bool                 Format               ( const char szFormat[], ... );

   bool                 Attach               ( LPCSTR strPathName, LPCSTR strFileName = NULL );

   bool                 Send                 ( void );

   bool                 Give                 ( void );

   const MapiMessage *  GetMapiMessage       ( void ) const;

   };

#endif   // _MAPIMAIL_H__0316D5D4_B8BE_4FDC_83F7_90E2438E8A6D__INCLUDED_

MAPIMail.cpp

/***********************************************************************************************************************
* Copyright (c) 2009-2010 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342, David.Cass.Tyler@gmail.com *
***********************************************************************************************************************/

#include <STDIO.H>      // _vsnprintf()

#include "MAPIMail.h"   // The CMail Class Prototypes and Defines

CMail::CMail() :

   m_fIsValid           ( false  ), // Is this a Valid Session?

   m_hMAPIDLL           ( NULL   ), // Instance Handle of the MAPI DLL
   m_nMAPIError         ( 0      ), // Last MAPI Error Value
   m_hSession           ( 0      ), // MAPI Session Handle

   m_lnSpinLock         ( 0L     ), // The SpinLock
   m_dwExclusiveThread  ( 0LU    ), // The ID of the Exclusive Use Thread

   MAPILogon            ( NULL   ), // MAPILogon      Function Pointer
   MAPILogoff           ( NULL   ), // MAPILogoff     Function Pointer
   MAPISendMail         ( NULL   )  // MAPISendMail   Function Pointer

   {

   // Load the MAPI DLL and Get the Function Pointers We are Interested In

   m_hMAPIDLL = LoadLibrary ( "MAPI32.DLL" );

   if ( m_hMAPIDLL )
      {
      MAPILogon      = ( LPMAPILOGON      ) GetProcAddress ( m_hMAPIDLL, "MAPILogon"      );
      MAPILogoff     = ( LPMAPILOGOFF     ) GetProcAddress ( m_hMAPIDLL, "MAPILogoff"     );
      MAPISendMail   = ( LPMAPISENDMAIL   ) GetProcAddress ( m_hMAPIDLL, "MAPISendMail"   );

      // If Any of the Functions are Not Installed then Fail the Load

      if ( MAPILogon == NULL || MAPILogoff == NULL || MAPISendMail == NULL )
         return;
      }

   // Zero Out the m_stMAPIMessage Structure

   memset ( &m_stMAPIMessage, 0, sizeof m_stMAPIMessage );

   // Create a new MAPI Session

   m_fIsValid = ( m_nMAPIError = MAPILogon ( NULL, NULL, NULL, MAPI_NEW_SESSION, 0, &m_hSession ) ) == SUCCESS_SUCCESS;

   }  // CMail::CMail()

CMail::~CMail()

   {

   // Invalidate the Object

   m_fIsValid = false;

   // Unload the MAPI DLL and Reset the Function Pointers to NULL

   m_nMAPIError = MAPILogoff ( m_hSession, 0, 0, 0 );

   FreeLibrary ( m_hMAPIDLL );

   // Free Up the m_stMAPIMessage Structure

   if ( m_stMAPIMessage.lpRecips )
      free ( m_stMAPIMessage.lpRecips );

   if ( m_stMAPIMessage.lpFiles )
      free ( m_stMAPIMessage.lpFiles );

   if ( m_stMAPIMessage.lpszNoteText )
      free ( m_stMAPIMessage.lpszNoteText );

   }  // CMail::~CMail()

bool CMail::IsValid() const

   {

   // Assure that "this" Points to Valid Read/Write Memory

   if ( IsBadWritePtr ( ( void * ) this, sizeof ( CMail ) ) )
      return false;

   return m_fIsValid;

   }  // bool CMail::IsValid() const

bool CMail::Take ( bool fReturnEarly )

   {

   while ( InterlockedIncrement ( ( LPLONG ) &m_lnSpinLock ) != 1 )
      {
      InterlockedDecrement ( ( LPLONG ) &m_lnSpinLock );

      // Return if Requested

      if ( fReturnEarly )
         return false;

      SwitchToThread();
      }

   m_dwExclusiveThread = GetCurrentThreadId();

   return true;

   }  // bool CMail::Take ( bool fReturnEarly )

bool CMail::Send ( MapiMessage & stMAPIMessage )

   {

   // Do the Actual Send using MAPISendMail

   if ( ! IsValid() )
      return false;

   return MAPISendMail ( m_hSession, 0, &stMAPIMessage, 0, 0 ) == SUCCESS_SUCCESS;

   }  // BOOL CMail::Send ( CMapiMessage & message )

bool CMail::AddRecipient ( LPCSTR strRecipient, ULONG ulRecipClass )

   {

   if ( ! IsValid() )
      return false;

   // Allocate Memory for Another Recipient

   if ( ! m_stMAPIMessage.lpRecips )
      {
      m_stMAPIMessage.lpRecips = ( MapiRecipDesc * ) malloc ( sizeof ( MapiRecipDesc ) );
      }
   else
      {
      m_stMAPIMessage.lpRecips = ( MapiRecipDesc * ) realloc ( m_stMAPIMessage.lpRecips, ( m_stMAPIMessage.nRecipCount + 1 ) * sizeof ( MapiRecipDesc ) );
      }

   // Fail if Memory Not Allocated

   if ( ! m_stMAPIMessage.lpRecips )
      {
      m_stMAPIMessage.nRecipCount = 0;
      return false;
      }

   MapiRecipDesc * lpstMapiRecipDesc = &m_stMAPIMessage.lpRecips [ m_stMAPIMessage.nRecipCount ];

   memset ( lpstMapiRecipDesc, 0, sizeof ( MapiRecipDesc ) );

   lpstMapiRecipDesc->ulRecipClass  = ulRecipClass;
   lpstMapiRecipDesc->lpszName      = ( LPSTR ) strRecipient;

   m_stMAPIMessage.nRecipCount++;

   return true;

   }  // bool CMail::AddRecipient ( LPCSTR strRecipient, ULONG ulRecipClass )

bool CMail::To ( LPCSTR strTo )

   {

   if ( ! IsValid() )
      return false;

   return AddRecipient ( strTo, MAPI_TO );

   }  // bool CMail::To ( LPCSTR strTo )

bool CMail::Cc ( LPCSTR strCc )

   {

   if ( ! IsValid() )
      return false;

   return AddRecipient ( strCc, MAPI_CC );

   }  // bool CMail::Cc ( LPCSTR strCc )

bool CMail::Bcc ( LPCSTR strBcc )

   {

   if ( ! IsValid() )
      return false;

   return AddRecipient ( strBcc, MAPI_BCC );

   }  // bool CMail::Bcc ( LPCSTR strBcc )

bool CMail::Subject ( LPCSTR strSubject )

   {

   if ( ! IsValid() )
      return false;

   m_stMAPIMessage.lpszSubject = ( LPSTR ) strSubject;

   return true;

   }  // bool CMail::Subject ( LPCSTR strSubject )

bool CMail::Message ( LPCSTR strMessage )

   {

   if ( ! IsValid() )
      return false;

   // If a malloc'ed Message Already Exists, Free It

   if ( m_stMAPIMessage.lpszNoteText )
      free ( m_stMAPIMessage.lpszNoteText );

   // Save the Message String

   return ( ( m_stMAPIMessage.lpszNoteText = strdup ( strMessage ) ) != NULL );

   }  // bool CMail::Message ( LPCSTR strMessage )

bool CMail::Format ( const char szFormat[], ... )

   {
   char     szBuffer [ 1024 * 8 ]   = { 0 };

   va_list  pArgPtr                 = NULL;
   size_t   cbBuffer                = sizeof szBuffer;
   int      nBytes                  = 0;

   // Use the Format and Argument List to Encode the Buffer

   va_start ( pArgPtr, szFormat );
   nBytes = _vsnprintf ( szBuffer, cbBuffer - 1, szFormat, pArgPtr );
   va_end   ( pArgPtr );

   if ( nBytes < 1 )
      return false;

   // If a malloc'ed Message Already Exists, Free It

   if ( m_stMAPIMessage.lpszNoteText )
      free ( m_stMAPIMessage.lpszNoteText );

   // Save the Message String

   return ( ( m_stMAPIMessage.lpszNoteText = strdup ( szBuffer ) ) != NULL );

   }  // bool CMail::Format ( const char szFormat[], ... )

bool CMail::Attach ( LPCSTR strPathName, LPCSTR strFileName )

   {

   if ( ! IsValid() )
      return false;

   // Allocate Memory for Another Recipient

   if ( ! m_stMAPIMessage.lpFiles )
      {
      m_stMAPIMessage.lpFiles = ( MapiFileDesc * ) malloc ( sizeof ( MapiFileDesc ) );
      }
   else
      {
      m_stMAPIMessage.lpFiles = ( MapiFileDesc * ) realloc ( m_stMAPIMessage.lpFiles, ( m_stMAPIMessage.nFileCount + 1 ) * sizeof ( MapiFileDesc ) );
      }

   // Fail if Memory Not Allocated

   if ( ! m_stMAPIMessage.lpFiles )
      {
      m_stMAPIMessage.nFileCount = 0;
      return false;
      }

   MapiFileDesc * lpstMapiRecipDesc = &m_stMAPIMessage.lpFiles [ m_stMAPIMessage.nFileCount ];

   memset ( lpstMapiRecipDesc, 0, sizeof ( MapiFileDesc ) );

   lpstMapiRecipDesc->lpszPathName = ( LPSTR ) strPathName;
   lpstMapiRecipDesc->lpszFileName = ( LPSTR ) strFileName;

   m_stMAPIMessage.nFileCount++;

   return true;

   }  // bool CMail::Attach ( LPCSTR strPathName, LPCSTR strFileName )

bool CMail::Send ( void )

   {

   // Do the Actual Send using MAPISendMail

   if ( ! IsValid() )
      return false;

   bool fResult = false;

   // Send the Message

   if ( m_stMAPIMessage.lpRecips && m_stMAPIMessage.lpszSubject && m_stMAPIMessage.lpszNoteText )
      fResult = ( m_nMAPIError = MAPISendMail ( m_hSession, 0, &m_stMAPIMessage, 0, 0 ) ) == SUCCESS_SUCCESS;

   if ( ! fResult )
      fprintf ( stderr, "MAPIError = %lu\n", m_nMAPIError );

   // Zero Out the m_stMAPIMessage Structure

   if ( m_stMAPIMessage.lpRecips )
      free ( m_stMAPIMessage.lpRecips );

   if ( m_stMAPIMessage.lpFiles )
      free ( m_stMAPIMessage.lpFiles );

   if ( m_stMAPIMessage.lpszNoteText )
      free ( m_stMAPIMessage.lpszNoteText );

   memset ( &m_stMAPIMessage, 0, sizeof m_stMAPIMessage );

   // Return the Success/Failure Indicator

   return fResult;

   }  // BOOL CMail::Send ( CMapiMessage & message )

bool CMail::Give ( void )

   {

   if ( m_dwExclusiveThread != GetCurrentThreadId() )
      return false;

   m_dwExclusiveThread = 0LU;

   InterlockedDecrement ( ( LPLONG ) &m_lnSpinLock );

   return true;

   }  // bool CMail::Give ( void )

const MapiMessage * CMail::GetMapiMessage ( void ) const

   {

   return IsValid() ? &m_stMAPIMessage : NULL;

   }  // const MapiMessage * GetMapiMessage  ( void ) const

Mail.cpp

/************************************************************************************************************************
*                                                                                                                       *
* Copyright (c) 2009-2010 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342, David.Cass.Tyler@gmail.com  *
*                                                                                                                       *
*  Mail uses the CMail class to programmatically generate and send e-mails to the specified recipients.                 *
*                                                                                                                       *
*  Usage:                                                                                                               *
*                                                                                                                       *
*     F:\Projects\Mail>cl /MT Mail.cpp MAPIMail.cpp                                                                     *
*     Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86                                       *
*     Copyright (C) Microsoft Corp 1984-1998. All rights reserved.                                                      *
*                                                                                                                       *
*     Mail.cpp                                                                                                          *
*     MAPIMail.cpp                                                                                                      *
*     Generating Code...                                                                                                *
*     Microsoft (R) Incremental Linker Version 6.00.8447                                                                *
*     Copyright (C) Microsoft Corp 1992-1998. All rights reserved.                                                      *
*                                                                                                                       *
*     /out:Mail.exe                                                                                                     *
*     Mail.obj                                                                                                          *
*     MAPIMail.obj                                                                                                      *
*                                                                                                                       *
*     F:\Projects\Mail>mail                                                                                             *
*     Success!                                                                                                          *
*                                                                                                                       *
*     F:\Projects\Mail>                                                                                                 *
*                                                                                                                       *
************************************************************************************************************************/

#include <STDIO.H>      // Standard I/O Prototypes and Defines

#include "MAPIMail.h"   // The CMail Class Prototypes and Defines


bool SendRaw ( void )

   {
   CMail Mail;

   if ( ! Mail.IsValid() )
      return false;

   // Create and Send a Raw Message

   MapiRecipDesc  aRecipients    [ 1 ] = { { 0LU, MAPI_TO, "David.Cass.Tyler@gmail.com" } };
   MapiFileDesc   aMapiFileDesc  [ 1 ] = { { 0LU, 0LU, 0xFFFFFFFFLU, "C:\\Check.csv", NULL, NULL } };

   MapiMessage    stMAPIMessage        = { 0LU, "Test 4", "Not MFC", NULL, NULL, NULL, 0LU, NULL, 1, aRecipients, 1, aMapiFileDesc };

   if ( ! Mail.Send ( stMAPIMessage ) )
      return false;

   return true;

   }  // bool SendRaw ( void )

int AlternateMain ( void )

   {
   CMail Mail;

   // Create and Send the Message Using the Class Functions

   Mail.Take();   // Take Exclusive Use to Make Mail Thread Safe (Not Really Needed Here)

   // Use Any or All of the Following to Define the Recipient(s)

   Mail.To        ( "david.cass.tyler@gmail.com"            );
// Mail.Cc        ( "david.cass.tyler@gmail.com"            );
// Mail.Bcc       ( "david.cass.tyler@gmail.com"            );

   Mail.Subject   ( "Mail Test"                             );

   // Use Either of the Following to Generate the Message

   Mail.Message   ( "This uses attachment functions"        );
// Mail.Format    ( "Test %d", 5                            );

   // Use Either of the Following to Attach the File

   Mail.Attach    ( "C:\\DirSize.csv", "DifferentName.csv"  );
// Mail.Attach    ( "C:\\DirSize.xlsx"                      );

   bool fSuccess = Mail.Send();

   Mail.Give();   // Release Exclusive Use

   // Return Success!

   if ( fSuccess )
      fprintf ( stdout, "Success!\n" );
   else
      fprintf ( stderr, "Send Failed!\n\a" );

   return 0;

   }  // int AlternateMain ( void )

int main ( int /* argc */, char * /* argv */ [], char * /* envp */ [] )

   {
   CMail Mail;

   // Create and Send the Message Using the Class Functions

   Mail.To        ( "David.Cass.Tyler@gmail.com"   );
   Mail.Subject   ( "Formatted Mail Test"          );

   Mail.Attach    ( "C:\\DirSize.csv"              );

   // Create the e-mail Body

Mail.Format (

      "This e-mail is being programatically formatted and delivered\r\n\r\n"

      "The \"To\", \"Cc\", \"Bcc\", \"Subject\", \"Message\" and \"Attach\" "
      "commands are implemented, as well as a \"Format\" command that lets "
      "the user create the e-mail output as easily as writing an fprintf "
      "statement.\r\n\r\n"

      "There are %d recipients and a file attachment, \"%s\".",

      Mail.GetMapiMessage()->nRecipCount,
      Mail.GetMapiMessage()->lpFiles->lpszPathName

   );

   if ( ! Mail.Send() )
      return 1;

   // Return Success!

   fprintf ( stdout, "Success!\n" );

   return 0;

   }  // int main ( int argc, char * argv [], char * envp [] )