Pages

Wednesday, October 21, 2009

Win32: Send email

An improved edition of this article was published on EE: "Sending Email with MAPI".

If you have a task to send an email from your C/C++ application, the most recommended way is to use ShellExecute as in the following sample:
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <ShellAPI.h>

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow)
{
ShellExecute(NULL, L"open",
L"mailto:pgnatyuk@gmail.com\
         ?Subject=Hello, world\
         &body=The email sent from ShellExecute",
L"", L"", SW_SHOWNORMAL );
return 0;
}

You will get the same result, if you will choose this long way:
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <mapi.h>

BOOL SendMail(LPCSTR lpszSubject, LPCSTR lpszTo,
LPCSTR lpszName, LPCSTR lpszText);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow)
{
SendMail("Hello", "SMTP:pgnatyuk@gmail.com",
"Pavel", "Text");
return 0;
}

BOOL SendMail(LPCSTR lpszSubject, LPCSTR lpszTo,
LPCSTR lpszName, LPCSTR lpszText)
{
HINSTANCE hMAPI = ::LoadLibrary(L"mapi32.dll");
LPMAPISENDMAIL lpfnMAPISendMail =
(LPMAPISENDMAIL)::GetProcAddress(hMAPI, "MAPISendMail");

char szTo[MAX_PATH] = { 0 };
strcat_s(szTo, lpszTo);

char szName[MAX_PATH] = { 0 };
strcat_s(szName, lpszName);

MapiRecipDesc recipient[1] = { 0 };
recipient[0].ulRecipClass = MAPI_TO;
recipient[0].lpszAddress = szTo;
recipient[0].lpszName = szName;

char szSubject[MAX_PATH] = { 0 };
strcat_s(szSubject, lpszSubject);

char szText[MAX_PATH] = { 0 };
strcat_s(szText, lpszText);

MapiMessage MAPImsg = { 0 };
MAPImsg.lpszSubject = szSubject;
MAPImsg.lpRecips = recipient;
MAPImsg.nRecipCount = 1;
MAPImsg.lpszNoteText = szText;

ULONG nSent = lpfnMAPISendMail(0, 0,
&MAPImsg, MAPI_LOGON_UI | MAPI_DIALOG, 0);

FreeLibrary(hMAPI);
return (nSent == SUCCESS_SUCCESS || nSent == MAPI_E_USER_ABORT);
}
The SendMail function from this example uses the so called Simple MAPI. If you have checked this link, you see that the Simple MAPI functions are declared in MAPI.H but to call these functions you need load mapi32.dll dynamically in the run-time and use GetProcAddress function to get the function pointers:
HINSTANCE hMAPI = ::LoadLibrary(L"mapi32.dll");
LPMAPISENDMAIL lpfnMAPISendMail =
(LPMAPISENDMAIL)::GetProcAddress(hMAPI, "MAPISendMail");

This way allowed to set the subject and the text of the message.

If your mail client (probably Outlook) already runs on your computer, you can send an email from your code and do not prompt the user, do not show any message dialog, you need to replace MAPI_LOGON_UI | MAPI_DIALOG with NULL in the MAPISendMail function call:
ULONG nSent = lpfnMAPISendMail(0, 0, &MAPImsg, NULL, 0);

On Windows Vista, even in this way you will see a system dialog asking you to allow the message sending from your computer.

You can attach a file to your email as it is whown here:
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdlib.h>
#include <mapi.h>

BOOL SendFile(LPCSTR lpszSubject, LPCSTR lpszTo,
LPCSTR lpszName, LPCSTR lpszText,
LPCSTR lpszFullFileName);


int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow)
{
SendFile("Hello", "SMTP:pgnatyuk@gmail.com",
"Pavel", "Text", "c:\\text.txt");
return 0;
}

BOOL SendFile(LPCSTR lpszSubject, LPCSTR lpszTo,
LPCSTR lpszName, LPCSTR lpszText,
LPCSTR lpszFullFileName)
{
HINSTANCE hMAPI = ::LoadLibrary(L"mapi32.dll");
LPMAPISENDMAIL lpfnMAPISendMail =
(LPMAPISENDMAIL)::GetProcAddress(hMAPI, "MAPISendMail");

char szDrive[_MAX_DRIVE] = { 0 };
char szDir[_MAX_DIR] = { 0 };
char szName[_MAX_FNAME] = { 0 };
char szExt[_MAX_EXT] = { 0 };
_splitpath_s(lpszFullFileName, szDrive, szDir, szName, szExt);

char szFileName[MAX_PATH] = { 0 };
strcat_s(szFileName, szName);
strcat_s(szFileName, szExt);

char szFullFileName[MAX_PATH] = { 0 };
strcat_s(szFullFileName, lpszFullFileName);

MapiFileDesc MAPIfile = { 0 };
ZeroMemory(&MAPIfile, sizeof(MapiFileDesc));
MAPIfile.nPosition = 0xFFFFFFFF;
MAPIfile.lpszPathName = szFullFileName;
MAPIfile.lpszFileName = szFileName;

char szTo[MAX_PATH] = { 0 };
strcat_s(szTo, lpszTo);

char szNameTo[MAX_PATH] = { 0 };
strcat_s(szNameTo, lpszName);

MapiRecipDesc recipient = { 0 };
recipient.ulRecipClass = MAPI_TO;
recipient.lpszAddress = szTo;
recipient.lpszName = szNameTo;

char szSubject[MAX_PATH] = { 0 };
strcat_s(szSubject, lpszSubject);

char szText[MAX_PATH] = { 0 };
strcat_s(szText, lpszText);

MapiMessage MAPImsg = { 0 };
MAPImsg.lpszSubject = szSubject;
MAPImsg.lpRecips = &recipient;
MAPImsg.nRecipCount = 1;
MAPImsg.lpszNoteText = szText;
MAPImsg.nFileCount = 1;
MAPImsg.lpFiles = &MAPIfile;

ULONG nSent = lpfnMAPISendMail(0, 0, &MAPImsg, NULL, 0);

FreeLibrary(hMAPI);
return (nSent == SUCCESS_SUCCESS || nSent == MAPI_E_USER_ABORT);
}
These examples work, if you have your mail client (Outlook) running. Otherwise MAPISendMail function will fail with MAPI_E_LOGON_FAILURE return code.

Simple MAPI has 2 special functions that are supposed to fix the situation:
1. MAPILogon
2. MAPILogoff
You can test these functions with the following sample:
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <mapi.h>

BOOL SendMail(LPCSTR lpszSubject, LPCSTR lpszTo,
LPCSTR lpszName, LPCSTR lpszText);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow)
{
SendMail("Hello", "SMTP:pgnatyuk@gmail.com",
"Pavel", "Text. No mail client running.");

return 0;
}

BOOL SendMail(LPCSTR lpszSubject, LPCSTR lpszTo,
LPCSTR lpszName, LPCSTR lpszText)
{
HINSTANCE hMAPI = NULL;
LPMAPISENDMAIL lpfnMAPISendMail = NULL;
LPMAPILOGON lpfnMAPILogOn = NULL;
LPMAPILOGOFF lpfnMAPILogOff = NULL;

hMAPI = ::LoadLibrary(L"mapi32.dll");
lpfnMAPISendMail =
(LPMAPISENDMAIL)::GetProcAddress(hMAPI, "MAPISendMail");

lpfnMAPILogOn =
(LPMAPILOGON)::GetProcAddress(hMAPI, "MAPILogon");

lpfnMAPILogOff =
(LPMAPILOGOFF)::GetProcAddress(hMAPI, "MAPILogoff");

char szTo[MAX_PATH] = { 0 };
strcat_s(szTo, lpszTo);

char szName[MAX_PATH] = { 0 };
strcat_s(szName, lpszName);

MapiRecipDesc recipient[1] = { 0 };
recipient[0].ulRecipClass = MAPI_TO;
recipient[0].lpszAddress = szTo;
recipient[0].lpszName = szName;

char szSubject[MAX_PATH] = { 0 };
strcat_s(szSubject, lpszSubject);

char szText[MAX_PATH] = { 0 };
strcat_s(szText, lpszText);

MapiMessage MAPImsg = { 0 };
MAPImsg.lpszSubject = szSubject;
MAPImsg.lpRecips = recipient;
MAPImsg.nRecipCount = 1;
MAPImsg.lpszNoteText = szText;

LHANDLE lhSession;
lpfnMAPILogOn(0, NULL, NULL,0, 0, &lhSession);

ULONG nSent = lpfnMAPISendMail(lhSession, 0,
&MAPImsg, MAPI_LOGON_UI | MAPI_DIALOG, 0);

lpfnMAPILogOff(lhSession, 0, 0, 0);

FreeLibrary(hMAPI);
return (nSent == SUCCESS_SUCCESS || nSent == MAPI_E_USER_ABORT);
}

You can pass the outlook profile name and your password to the MAPILogon function:
LHANDLE lhSession;
lpfnMAPILogOn(0, szProfile, szPassword,0, 0, &lhSession);

On CodePlex you can download MFCMAPI - Microsoft's published APIs providing access to MAPI.

No comments:

Post a Comment