Free Web Hosting by Netfirms
Web Hosting by Netfirms | Free Domain Names by Netfirms

@!$h@'s Code World
Welcome to @!$h@'s Free CodeWorld
     .NET Framework
 
   MFC/Win32/COM
 
   C# Section 
 
    Playing with Simple MAPI

  • Platform: Windows NT, 2000, XP
    Programming Tools: Visual C++6, Visual C++7, MFC, Win32 SDK, C++
    Dependancies: Any MAPI based Client (MS outlook, Outlook Express)or Exchange Server

About the Author

Aisha Ikram is a senior software engineer and has been working mainly in VC++ 6, MFC, ATL, COM. She now a days working on .NET framework and C#. She is also a member of various developer's website like code project, code guru, mind cracker and C# corner. You can write the author at ais_ikr@yahoo.com for your feedback concerns and problems.

Introduction

This article accompanying the source code describes how you can do the followings using Simple MAPI:

  • Compose and send (forward, reply, cc, bcc etc) emails with one or more attachments.
  • Resolve Recipients Addresses
  • Search an email in the inbox
  • Read emails from your inbox
  • Save a message.
  • Incorporating in your application the functionality of "Send" to send your application document as an email attachment.

The article also explains how to use the source code and how u can customize to achieve your required functionality. It also discusses about the Simple MAPI functions in the source code.

Note: I used Outlook Express 6 and MS Outlook 2000 and Outlook 2002 (XP). In older versions, there could be slight difference for example, menu options etc or the looks of propertysheets etc. The underlying concept is same, so you can easily follow the mentioned steps in your client.

About Simple MAPI

Simple MAPI is a of 12 functions, which support basic messaging functionality for sending and receiving messages:

  1. Log onto the messaging system.
  2. Compose new messages, add and resolve recipients, send messages.
  3. Retrieve and read messages from the inbox.

Followings are the functions included by SMAPI:

Function

Description

MapiAddress()

Addresses a Mail message.

MapiDeleteMail()

Deletes a Mail message.

MapiDetails()

Displays a recipient details dialog box.

MapiFindNext()

Returns the ID of the next (or first) Mail message of a specified type.

MapiFreeBuffer()

Frees memory allocated by the messaging system.

MapiLogoff()

Ends a session with the messaging system.

MapiLogon()

Begins a session with the messaging system.

MapiReadMail()

Reads a Mail message.

MapiResolveName()

Displays a dialog box to resolve an ambiguous recipient name.

MapiSaveMail()

Saves a Mail message.

MapiSendDocuments()

Sends a standard Mail message.

MapiSendMail()

Sends a Mail message.

The Simple MAPI functions can be called from any application that supports API calls and data structures and types used

Note: You can use Simple MAPI in languages, such as C, C++, Visual Basic/VBA. 
The MAPI controls included with Microsoft Visual Basic and the version of MAPI installed by Outlook Express implement Simple MAPI only.

Simple MAPI Clients

Some of the well known simple mapi based clients are:

  • MS Outlook supports both Simple and Extended MAPI
  • MS Outlook Express supports Simple MAPI only.
  • Netscape Communications
  • Eudora

Simple MAPI Services

Simple Mapi is used in your messaging aware and messging enabled applications. See Messaging clients in "Difference between CDO, Simple and Extended MAPI".

Simple MAPI does not provide you with maximun flexibilty or control but it's the most easy and direct way to achieve any one or all of the above mentioned fucntionalities.

Adding Send Command in your application

MFC has make this very simple to make your application messaging aware by adding a Send command to send document as an attachment. MFC appwizard made this task very simple. Let us see how we can do that.

  1. Create an MFC Application (exe) from File > New > Project.
  2. Specify the project name say MessageAware.
  3. Choose document type SDI.
  4. Accepts the defaults until you reach the Step 4.
  5. There is a check box named "MAPI (Messaging API)" which is by default not selected. Simply check the option and move to next steps.

    Step 4 (Appwizard)

  6. Derive your view class from CEditView (selects from combo, where CView is the default view selected).
  7. Finally Finish.

Note: Source code for the MessageAware application is given to download. You can open the MessageAware.exe and check how does it sends email.

What Appwizard has created for you:

  • Selecting CEditView as your View class will make your document view editable so that you can edit in your document file whihc you can also save as a text file from File >Save.
  • Selecting this MAPI option adds in your File menu, a command named Send ,which enable you to send your edit file as an attachment.  Edit in your view and then either save your file and then Send. The command will open the Compose Form of your default email client. The Compose form will let you compose your message. Note that file you created in your editor is attached in the message. If you don't save the file and File > Send it, the file is attached but without any name or extension.

Behind the Send Command

If you inspect your application, you will see, Behind the Send Command, two handlers are added for ID ID_FILE_SEND_MAIL in your document class. The handler methods OnFileSendMail and  OnUpdateFileSendMail are provided by MFC CDoument class. 

	ON_COMMAND(ID_FILE_SEND_MAIL, OnFileSendMail)
	ON_UPDATE_COMMAND_UI(ID_FILE_SEND_MAIL, OnUpdateFileSendMail)

Another method is Serialize which serilaizes the data, When file is not saved, serialization process creates a temporary file and attaches it with the message.

Behind this Send Command, CDocument uses a simple call to one of the Simple MAPI functions MAPISendDocuments(). So if you don't opt for MAPI support through your wizard, you can also acheive the same functionality using MAPISendDocuments().

Steps to Send an Email using Simple MAPI

Creating Profile and Configuring Account

  1. If you have already configured your account with Outlook or Express, Skip this section and go to next section "Create a Session, Logon, Fill MAPI structures and Send". Else go to Control Panel > Inbox and create a profile using the property sheet and create specify  valid account information (internet or exchange server account). For how to configure your account go to step 4.
  2. If you already have your profile created, Skip to next Section " Create a Session, Logon, Fill MAPI structures and Send".
  3. If you are just a beginner and don't know much about the concept of profile, simply use the default profile named Outlook.  This requires you leaving this profile things and only configure your account. Go to step 4 to create your account.
  4. From Outlook or Express (or whatever the client you are using), select from menu "Tools > Accounts...". It will display a property sheet "Internet Accounts" Click the Add button and then select from menu "Mail...". Connection Wizard will allow you to configure you account. Specify your display name, then email address (account through which the emails will be sent... should be a valid account).

    CreatingAccount

Note: Yahoo don't support accessing your accounts through outlook or express. Hotmail supports it and also for hotmail accounts, it's easy to configure while selection the hotmail option from the drop down for the type of email server.

Create a Session, Logon, Fill MAPI structures and Send

  1. Create the session or use the opened session (If outlook or express or your client is running) by logging on to account using the default profile or your specified profile (if you have created a separate profile).
  2. Create a MapiMessage structure to contain the message.
  3. Create one or more MapiRecipDesc structures describing the recipients of the message.
  4. Create a text string containing the subject.
  5. Create a text string containing the message text.
  6. Create an array of MapiFileDesc structures, if necessary, to contain any attachments.
  7. Submit the message with the MAPISendMail function.

Section below demonstrate that how we are following the above steps to send email.

About the Source Code (Usage and Customization)

Source code in the download link contains a project build in Visual C++ 6. You can open the dsp (project) file into your .NET visual studio and create VC++7 compatible project files also.

The code contains a class named CSMAPI  which incorporates the functions to use Simple MAPI to compose, resolve addresses, send, find and read messages.

Note: The demo application is made in MFC but the CSMAPI is not MFC based so you can use this class anywhere in your win32 or C++ code. You can also take the code into your COM components simply by cut & paste.

Here is the CSMAPI class definition and the members showing you what CSMAPI provides:

class CSMAPI  
{
protected:

	// MAPI related data members
	
	ULONG m_ulRet;

	HMODULE m_hlibMAPI;
	LHANDLE m_lhSession;

	//MAPI function pointers
	
	LPMAPIADDRESS m_MAPIAddress;
	LPMAPIDETAILS m_MAPIDetails;		
	LPMAPIFINDNEXT m_MAPIFindNext;		
	LPMAPIFREEBUFFER m_MAPIFreeBuffer;
	LPMAPILOGOFF m_MAPILogoff;		
	LPMAPILOGON m_MAPILogon;			
	LPMAPIREADMAIL m_MAPIReadMail;		
	LPMAPIRESOLVENAME m_MAPIResolveName;	
	LPMAPISENDDOCUMENTS m_MAPISendDocuments;	
	LPMAPISENDMAIL m_MAPISendMail;
	LPMAPISAVEMAIL m_MAPISaveMail;
	
	//others
	int m_nRecipientCnt,m_nFileCnt;
	lpMapiRecipDesc m_pRecipList;
	lpMapiFileDesc m_pAttachFileList;
	
public:
	CSMAPI();
	virtual ~CSMAPI();
	BOOL Logon(LPSTR lpProfile=NULL, LPSTR lpPassword=NULL);
	void Set_Recipient_List(Recipient* pRecipArray, int nRecipCnt=1);
	void Set_Attachments(LPTSTR* pAttachArray, int FileCnt=1);
	BOOL ResolveFromAddressBook(int nRecipNumber);
	void SendEmail(LPTSTR Subject, LPTSTR MessageText);
	BOOL FindnReadMessage();

protected:
	BOOL IsMAPIInstalled();
	void InitMAPI();
};

Data Members

CSMAPI contains:

  • A handle m_hlibMAPI to MAPI library (MAPI32.dll) which is loaded using LoadLibrary() method. 
  • A member variable for session handle m_lhSession  which can be shared if outlook or MAPI client is already running or can be created if user want to create it's own client session (instance).
  • A member m_ulRet to receive return code from Simple MAPI functions to check if the function is performed successfully.
  • MAPI function pointers. Simple Mapi library is not COM based but a win32 dll so you can not use it's functions directly.  you need to get the Function addresses in the corresponding data member from the loaded library MAPI32.dll using m_hlibMAPI.
  • The recipients (TO,CC and BCC) and recipient count.
  • The files attached and the file count.

Member Functions

CSMAPI()

Constructor to initialize the data. If Simple MAPI is installed on the system, it calls InitMAPI() to initilize the function pointers after loading MAPI library.

CSMAPI::CSMAPI() { . . InitMAPI(); }

~CSMAPI()

Destructor is called when the CMAPI object is destroyed. It performs the clean up tasks and unloads the MAPI library from the process address space using FreeLibrary().

CSMAPI::~CSMAPI(){
.
.
FreeLibrary(m_hlibMAPI);
}

InitMAPI()

Checks if the MAPI is installed on the system. If yes, loads the Simple MAPI Dll MAPI32.dll into the process address space and returns its handle. Initilizes the function pointers through GetProcAddress(). The method is called from the constructor when CSMAPI object is created.

 
// Get instance handle of MAPI32.DLL
m_hlibMAPI= LoadLibrary ( _T("MAPI32.dll") );	
	
//  Get the addresses of all the API's supported by this object
 m_MAPILogon = ( LPMAPILOGON) GetProcAddress (m_hlibMAPI, "MAPILogon");
 m_MAPISendMail = (LPMAPISENDMAIL)GetProcAddress (m_hlibMAPI,"MAPISendMail");
 m_MAPIFindNext = ( LPMAPIFINDNEXT) GetProcAddress (m_hlibMAPI,"MAPIFindNext");
 m_MAPIReadMail = (	 LPMAPIREADMAIL) GetProcAddress (m_hlibMAPI,"MAPIReadMail");
.
.

IsMAPIInstalled()

. . strcpy ( szAppName, "MAIL" ); strcpy ( szKeyName, "MAPI" ); . strcpy ( szFileName, "WIN.INI" );

//search for the MAPI key under MAIL section in WIN.INI file bMAPIInstalled = GetPrivateProfileString(...);
if (strcmp(&szDefault, &szReturn) == 0 ) return FALSE; else return TRUE;

Logon()

The method takes a profile name and the password for the account configured in that profile. If profile not specified, the method uses default profile if exists else prompts the user through a dialog (if specified using MAPI_LOGON_UI flag) for the profile name to use to logon. You can also create a separate session for you by setting a flag value.

BOOL CSMAPI::Logon(LPSTR lpProfile, LPSTR lpPassword)
{	
.
. m_ulRet = m_MAPILogon( 
	(ULONG)AfxGetMainWnd()->m_hWnd,//parent window of the profile picker
	lpProfile,		//profile name - if NULL uses default
	lpPassword,		//password - if using default profile, it's NULL 
	MAPI_LOGON_UI,	//Allow a profile picker box to show if not logged in, 
			//if NULL dialog not appears
	0,		//Reserved- must be 0
	&m_lhSession);	//handle of session

if (m_ulRet != SUCCESS_SUCCESS) 		
	return FALSE;
		
return TRUE;

}

Set_Recipient_List()

Method fills the MAPIRecipDesc structure for the email recipients like those in TO, CC and BCC list of a compose message. See as below:

MAPIRecipDesc m_pRecipList[3];
.
.
m_pRecipList[i].lpszAddress = "ais_ikr@yahoo.com";
m_pRecipList[i].ulRecipClass = MAPI_TO;
m_pRecipList[i].lpszName = "Aisha";
m_pRecipList[i].lpEntryID= NULL;
m_pRecipList[i].ulEIDSize =0L;
m_pRecipList[i].ulReserved =0L;

Set_Attachments()

Method fills the MAPIFileDesc structure for the file attachments to send with the message as below. Flag allows you to play with your file attachments. lpszFileName is the friendly name displayed for the attached file, if NULL it displays the file path name.

MAPIFileDesc m_pAttachFileList[3];
.
.
m_pAttachFileList[i].lpszFileName = "Report File";
m_pAttachFileList[i].lpszPathName = "c:\\report.txt";
m_pAttachFileList[i].nPosition = -1;
m_pAttachFileList[i].ulReserved =0;
m_pAttachFileList[i].flFlags = NULL;

ResolveFromAddressBook()

Method resolves the recipient using the address book entries. It uses the MAPIResolveName(), which returns the pointer to address entry corresponding to the specified recipient name. For example if i specify "Aisha", it will look into the address entry and return with the contact info for "Aisha" along with the email address etc.

lpMapiRecipDesc lpRecip;
m_ulRet = m_MAPIResolveName(m_lhSession,
	NULL,	//parent window
	"Aisha", //name
	NULL, //flags
	0,	//reserved 
	& lpRecip //entry found
	);

SendEmail()

Method composes an email message of type MAPIMessage using the recipients information in the array of type MAPIRecipDec, message subject (parameter), body text (parameter) and file attachments if any from the array of MAPIFileDesc. Method sends email using MAPISendMail(). You can customize the message sending process through the flags, for example, a return reciept can be requested. A flag value MAPI_DIALOG in the MAPISendMail() displays the compose form before sending it. Similarly MAPI_LOGON_UI displays the logon dialog to the user if not already logged on. A NULL specify you don't want anything.

Note: A warning dialog is displayed while sending email through a program asking you to approve the sending of message on your behalf. In short, to avoid this, If you are using exchange server account, you can request your exchange server administrator to give you the authority to send emails or you can use extended MAPI to place your message directly into the inbox folder. For looking into the details of "how we can avoid warning dialog for sending email", see the FAQ.

Here is the code:

void CSMAPI:: SendEmail(LPTSTR Subject, LPTSTR MessageText)
{
//Filling Simple MAPI Message Structure 
	MapiMessage Message;
	Message.ulReserved =0L;	//Reserved, must also be 0
	Message.lpszSubject = Subject;
	Message.lpszNoteText = MessageText;
	Message.lpOriginator = NULL;
	Message.lpRecips = m_pRecipList;
	Message.nRecipCount = m_nRecipientCnt; //recip count
	Message.nFileCount = m_nFileCnt;	//attach file count
	Message.lpFiles = m_pAttachFileList; // attachments
	
	m_ulRet = m_MAPISendMail (m_lhSession,	//session
		(ULONG)AfxGetMainWnd()->m_hWnd, //parent window handle
					//for the log on pop up dialog
		&Message,	
		MAPI_LOGON_UI, // send NULL if you don't want to prompt user for logon
0); //Reserved if(m_ulRet!=SUCCESS_SUCCESS) { AfxMessageBox("Message not sent"); } else AfxMessageBox("All done!!");
}

FindnReadMessage()

Method finds the next unread method from the inbox and reads it into a MAPIMessage structure. Use MAPIFreeBuffer() to release the memory created for this MAPIMessage structure.

// find the first unread message
	CHAR rgchMsgID[513];    // Message IDs should be >= 512 CHARs + a null.
	MapiMessage *lpMessage; // Used to get a message back from MAPIReadMail.
	
	m_ulRet = m_MAPIFindNext(m_lhSession, // explicit session required
		0L,        // always valid ulUIParam
		NULL,      // NULL specifies interpersonal messages
		NULL,      // seed message ID; NULL=get first message
		MAPI_LONG_MSGID | // needed for 512 byte rgchMsgID.
		MAPI_UNREAD_ONLY, // only get unread messages.
		0L,        // reserved; must be 0
		rgchMsgID);// buffer to get back a message ID.
	
	if (m_ulRet == MAPI_E_NO_MESSAGES) // make sure a message was found
	{
		AfxMessageBox("No unread message");
		
	}

Keep visiting the site for updated and new articles.
Sign my guest book OR send me your feedback at ais_ikr@yahoo.com

Copyright 2003, Aisha Ikram