Introduction
Having read Stephane Rodriguez's excellent and rather clever solution to programmatically adding attachments to Outlook emails, I realized how subtly useful this functionality was. Many was the time I have needed a way to send an application's document to another user. Obviously, it could be done by writing your own dialogs that mimic the mail client dialogs, but you have to hook up the address book and so on. It's far better to exploit the existing mail client software installed on the PC. I needed exactly this for an application so it could have File->Send To functionality - obviously, it needs attachments to work.
SendTo or MailTo
Stephane's article makes the perfectly valid point that using the SendTo approach is functionally better than using the mailto trick. Another compelling reason for not using mailto is it doesn't support attachments. The RFC mailto protocol is simple and doesn't specify attachments.
However, you commonly see code trying to use mailto like this:
mailto:shiver@metimbers.com?Subject=Ahoy there
shipmate&Body=Here's the shipping
manifest&Attach="D:\manifest.doc"
It probably won't attach the document because you are at the liberty of the email client to implement the mailto protocol and include parsing for the attachment clause. You may not know what mail client is installed on the PC, so it may not always work - Outlook certainly doesn't support attachments using mailto.
So what's the best (and easiest) way to do it
Stephane's solution is neat. It simulates a drag 'n drop into Outlook by using an unpublished mail helper COM object. However, by using unpublished functionality, you are at the mercy of the vendor (in this case Microsoft) changing things! And this is what has happened. The drag drop code works with no problem at all when using Outlook 2002/2003 on Win2K, but throws a 'Privileged exception' when running on XP. The presumption is there are permission issues with this combination of OS and the version of Outlook.
I needed a mail client version independent solution that supported attachments, so I chose to investigate MAPI. As it turns out, the answer is pretty simple, which is wrapped up in the CSendFileTo
class.
#ifndef __SENDFILETO_H__
#define __SENDFILETO_H__
#include <mapi.h>
class CSendFileTo
{
public:
bool SendMail(HWND hWndParent,
CString const &strAttachmentFileName,
CString const &strSubject=_T(""))
{
if (strAttachmentFileName.IsEmpty())
return false;
if (!hWndParent || !::IsWindow(hWndParent))
return false;
HINSTANCE hMAPI = ::LoadLibraryA(_T("MAPI32.DLL"));
if (!hMAPI)
return false;
ULONG (PASCAL *SendMail)(ULONG, ULONG_PTR,
MapiMessage*, FLAGS, ULONG);
(FARPROC&)SendMail = GetProcAddress(hMAPI,
_T("MAPISendMail"));
if (!SendMail)
return false;
TCHAR szFileName[_MAX_PATH];
TCHAR szPath[_MAX_PATH];
TCHAR szSubject[_MAX_PATH];
::StrCpy(szFileName, strAttachmentFileName.GetString());
::StrCpy(szPath, strAttachmentFileName.GetString());
::StrCpy(szSubject, strSubject.GetString());
MapiFileDesc fileDesc;
::ZeroMemory(&fileDesc, sizeof(fileDesc));
fileDesc.nPosition = (ULONG)-1;
fileDesc.lpszPathName = szPath;
fileDesc.lpszFileName = szFileName;
MapiMessage message;
::ZeroMemory(&message, sizeof(message));
message.lpszSubject = szSubject;
message.nFileCount = 1;
message.lpFiles = &fileDesc;
int nError = SendMail(0, (ULONG_PTR)hWndParent,
&message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
if (nError != SUCCESS_SUCCESS &&
nError != MAPI_USER_ABORT &&
nError != MAPI_E_LOGIN_FAILURE)
return false;
return true;
}
};
#endif
Example use
This nugatory code fragment shows how easy it is to use.
#include "SendFileTo.h"
...
...
CSendFileTo sendTo;
sendTo.(m_hWnd, _T("c://documents//menu.doc"),
_T("Here's the lunch menu"));
...
...
This is all straightforward, but there are a few points to note.
- If the attachment doesn't exist as a file on the file system, the call to
MAPISendMail
will fail with MAPI_E_ATTACHMENT_NOT_FOUND
. Hence the check at the start of the SendMail call.
- By passing the parent
HWND
to the MAPISendMail
function, the email client is supposed to make the send mail dialog modal to the given HWND
. You may want to remove this modalness(?) and simply use HWND_DESKTOP
.
- When the
MAPISendMail
call is made, it doesn't send the mail, it just pops up the email client dialog with the optional subject line set and the attachment attached.
- This code was written to compile and work with WTL (it rocks), but will work equally well using MFC.
I've successfully tested this with Outlook 2002 and 2003 on Win2K and XP. I'd be interested in hearing if it works with other mail clients I don't have access to - Eudora etc.
This class could probably do with some more error checking but I'll leave that as an exercise for the reader.