-
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:
-
Log onto the messaging system.
-
Compose new messages, add and resolve recipients, send messages.
-
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.
-
Create an MFC Application (exe) from File > New > Project.
-
Specify the project name say MessageAware.
-
Choose document type SDI.
-
Accepts the defaults until you reach the Step 4.
-
There is a check box named "MAPI (Messaging API)" which
is by default not selected. Simply check the option and move to next steps.

-
Derive your view class from CEditView (selects from combo, where CView is the
default view selected).
-
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().
Creating Profile and Configuring Account
-
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.
-
If you already have your profile created, Skip to next Section " Create
a Session, Logon, Fill MAPI structures and Send".
-
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.
-
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).

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 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).
-
Create a MapiMessage structure to
contain the message.
-
Create one or more MapiRecipDesc structures
describing the recipients of the message.
-
Create a text string containing the subject.
-
Create a text string containing the message text.
-
Create an array of MapiFileDesc structures,
if necessary, to contain any attachments.
-
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;
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;
}
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;
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
);
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
|