Click here to Skip to main content
Email Password   helpLost your password?

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(""))
    {
        // The attachment must exist as a file on the system

        // or MAPISendMail will fail, so......

        if (strAttachmentFileName.IsEmpty())
            return false;

        // You may want to remove this check, but if a valid

        // HWND is passed in, the mail dialog will be made

        // modal to it's parent.

        if (!hWndParent || !::IsWindow(hWndParent))
            return false;

        HINSTANCE hMAPI = ::LoadLibraryA(_T("MAPI32.DLL"));
        if (!hMAPI)
            return false;

        // Grab the exported entry point for the MAPISendMail function

        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;

        // Ok to send

        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.

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.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralCan I use this solution for my problem....
dkperez
13:16 21 Jan '10  
Tabular html form into which a user enters 1 or more titles and matching file spec. In this case the filespec is the full path, filename, and extension on the user's local system... SO, for an image called sunrise.jpg what they'd have to enter is something like
d:\images\morning\sunrise.jpg.

When done the user hits SUBMIT and everything gets sent to php and stored.

I need them to email the files they've entered to an address, and I'd prefer to do this from the client so I don't have to upload a bunch of large image files to the server.

So, I'd like to use some form of client emailer, preferably one that makes use of the mail engine on the user's PC so they don't have to wait while a server "mail" synchronously processes and sends a bunch of images (seems very slow)......

If it WILL work in this case, can someone provide very specific information about where the code has to go and how to use whatever does the call in html...

thanks.
GeneralWARNING = THIS CODE PROBABLY UNSAFE
sashasawchai
23:21 1 Dec '08  
I highly recommend reading this post on MSDN:

http://blogs.msdn.com/mstehle/archive/2007/10/03/fyi-why-are-mapi-and-cdo-1-21-not-supported-in-managed-net-code.aspx[^]

My reading of it is that the code presented here - while it will work, is probably unsafe and will probably introduce memory leaks and other potential problems into your application. WTF

Thanks be to Microsoft. Mad

You have been warned...
GeneralRe: WARNING = THIS CODE PROBABLY UNSAFE
David M Brooks
6:14 27 Aug '09  
It depends on how you clean things up when you've finished with them.

To counter the argument, I've used a variation of this code in a corporate application with a user base of over 1600. It's been running for over 3 years with no problems yet.

Dave
Generalto send more than 1 mail address [modified]
ArielR
11:08 2 Jul '08  
what must to modify at the code to send more than one mail address. Thanks

modified on Wednesday, July 2, 2008 5:32 PM

Generalwhat for ?
kilt
20:59 25 Jun '08  
An "article" which just copies MSDN code or hundreds of Usenet codes posted for 13 years ?
Is it a joke ?
Pathetic Frown
QuestionLotus Notes ignores lpszFileName
tiggerlily
0:08 30 Nov '07  
Lotus Notes (v7 and v8) ignores lpszFileName when attaching a file with Simple MAPI. Instead, Notes uses the filename from lpszPathName as the displayable attachment filename seen by the recipients.

Caveat: If a single file is attached with lpszPathName ending in ".tmp", Notes does use lpszFileName as displayed name. But, attempting to attach more than one file with name ending in ".tmp" causes "Notes MailMan error" to pop up. In order to attach multiple files, each lpszPathName must end in other than ".tmp", e.g., ".temp" or ".jpg". But, Notes disregards lpszFileName unless lpszPathName ends with ".tmp".

Outlook correctly honors lpszFileName.

As a workaround for Notes, I could use the desired displayable name as the filename portion of lpszPathName. For example, my app could download URL to \docs and settings\joe\Local Settings\Temp\Harry Potter.txt. But, that doesn't work when lpszPathName contains special unicode characters.

Does anyone have a solution?



http://msdn2.microsoft.com/en-us/library/ms530443.aspx says:

lpszPathName

Pointer to the fully qualified path of the attached file. This path should include the disk drive letter and directory name.

lpszFileName

Pointer to the attachment filename seen by the recipient, which may differ from the filename in the lpszPathName member if temporary files are being used. If the lpszFileName member is empty or NULL, the filename from lpszPathName is used.

Generalwhy it saves the draft into inbox folder?
xuxiaohui
23:37 7 Nov '07  
Thanks your code!

But I have the same question about "why it saves the draft into inbox folder?"

Thank you!

Peter

-----------------------------------------------------------
Two Questions? Sstar 23:13 1 May '06

2. When the Mail dialog pops up, instead of sending it, if I click on the 'save' button, found that the mail is saved into my INBOX folder. By right, the mail should be saved into the DRAFT folder, shouldn't it? Is there any option that we could set somewhere to correct this problem when using the MAPI_SENDMAIL function? I am using Ms Outlook 2003.





QuestionNewbie problem - can't compile (get errors in mapi.h)
cgalpin
5:59 1 Nov '07  
Sorry for the Newbie question, but I have tried to make a simple test of this using both a console application project and a windows application project in Visual Studio 2005 with no luck. I'm not sure what headers to include to make this work, but both projects fail compiling mapi.h (I get 198 errors - first few shown below).

thanks
charles

Error 1 error C2143: syntax error : missing ';' before '*' C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include\mapi.h 43
Error 2 error C4430: missing type specifier - int assumed. Note: C++ does not support default-int C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include\mapi.h 43
Error 3 error C2146: syntax error : missing ';' before identifier 'LHANDLE' C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include\mapi.h 48
Error 4 error C4430: missing type specifier - int assumed. Note: C++ does not support default-int C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include\mapi.h 48
Error 5 error C4430: missing type specifier - int assumed. Note: C++ does not support default-int C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include\mapi.h 48
Error 6 error C2143: syntax error : missing ';' before '*' C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include\mapi.h 48

and so on
AnswerRe: Newbie problem - can't compile (get errors in mapi.h)
Varchas R S
0:03 5 Nov '07  
Same problem with me too!!!!Frown
GeneralRe: Newbie problem - can't compile (get errors in mapi.h)
cgalpin
8:42 6 Nov '07  
I did figure out I need ATL/WTL (it was even mentioned in the original post) but still get various errors I can't resolve. Would someone mind making a quick project that compiles for this so we can see how to satisfy the dependencies?
QuestionSending html formatted email
kryzzozz78
10:09 10 Oct '07  
Has anyone sent an html formatted email?

I have this:

MapiMessage Msg;

Msg.lpszNoteText = szBody; // body

//If I sent text in lpszNoteText, then email format will be plain text
//If I sent lpsZNoteText empty, then email format will be html

So, I want to send a link in the body, something like:

<a href:"http://www.codeproject.com">Link to codeproject</a>

Can anyone help me on this. Any advice would be greatly appreciated.

Thanks.
GeneralRe: Sending html formatted email
Member 2163470
5:56 31 Mar '08  
I have the same problem!
Generalprofile problem with MAPI
rob0103
0:48 30 Jul '07  
this code works great !!! thaaanksBig Grin
But i 've one problem
We run our solution on a CITRIX server and
so i've another profile then the profile on my local workstation.

so everytime i send an email a dialog appears where i
should coose the profile.

How can i change the profile name or set the default profile name

Maybe someone can give me an example ? Pleeaasee !!

I don't know how to do

greets Robert
GeneralRe: profile problem with MAPI
BhushanKalse
21:28 27 Aug '07  
Have a look at this, it might help you:
http://support.microsoft.com/kb/180233
Generalthanks!
cronky78
21:17 12 Jul '07  
this really helped
Generalsending problem
133mmxtr
3:20 11 Jun '07  
When my outlook is off, MAPISendMail does not work properly. When i call MAPISendMail, the new mail dialog is shown, subject, to and attachment parts are filled. I click send button and the new mail window terminates. But the mail is not sent until i click send-receive button on outlook. With mailto call, the mail is sent directly without clicking send-receive button. But mailto call does not support attachments.
QuestionWCHAR version of Mapi32.dll ?? [modified]
ajitatif angajetor
3:49 24 Jan '07  
thanks for the code,it was more than a bunch of help.
but my platform sdk (Win2K3 R2) has the Mapi structures defined using LPSTR,rather than LPTSTR,which gives me trouble especially about non-english characters in the message.

i hopelessly tried changing the header file but as expected without result.
does anyone have any idea about this matter?
AnswerRe: WCHAR version of Mapi32.dll ??
hannahb
4:22 21 Mar '07  
Here is a UNICODE only version. I didn't bother setting up for TCHAR template types.


#ifndef __SENDFILETO_H__
#define __SENDFILETO_H__

#include <mapi.h>
#include
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 = ::LoadLibrary(_T("MAPI32.DLL"));
if (!hMAPI)
return false;

ULONG (PASCAL *SendMail)(ULONG, ULONG_PTR, MapiMessage*, FLAGS, ULONG);
(FARPROC&)SendMail = GetProcAddress(hMAPI, "MAPISendMail");

if (!SendMail)
return false;

char szFileName[_MAX_PATH];
char szPath[_MAX_PATH];
char szSubject[_MAX_PATH];

USES_CONVERSION;

::strcpy(szFileName, W2A(strAttachmentFileName.GetString()));
::strcpy(szPath, W2A(strAttachmentFileName.GetString()));
::strcpy(szSubject, W2A(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
GeneralRe: WCHAR version of Mapi32.dll ??
ajitatif angajetor
7:21 27 Mar '07  
yes this seems to handle the WCHAR characters.

but the problem for using USES_CONVERSION macros is they tend to change the non-english characters to the english ones. like "ı" to "i" and "ğ" to "g".

so i think i'll need a win32 api that directly supports WCHAR, which i think is not available currently.
Generalwants demo & source
shital_harode@rediffmail.com
0:24 18 Nov '06  
Plz send me the source & demo
as early as possible

Thank u.

Shital

dfgsd

GeneralRe: wants demo & source
prathuraj
0:10 6 Jun '07  
what u want
GeneralAnyone know how to do this with C#
c-a-b-
9:49 24 Aug '06  
I've been struggling with how to make this work with c#. Has anyone been successful?
GeneralRe: Anyone know how to do this with C# [modified]
David Broooks
5:01 12 Dec '06  
A .NET solution is slightly more tricky since we need to marshal data between managed and unmanaged code for the Win32 MAPI calls. Here's a class file you can use. I will update the article to include this along with a sample C# test application.

using System;
using System.Runtime.InteropServices;
using System.IO;

namespace SendFileTo
{
class MAPI
{
private const int MAPI_LOGON_UI = 0x00000001;
private const int MAPI_DIALOG = 0x00000008;

public static int SendMail(string strAttachmentFileName, string strSubject)
{
IntPtr session = new IntPtr(0);
IntPtr winhandle = new IntPtr(0);

MapiMessage msg = new MapiMessage();
msg.subject = strSubject;

int sizeofMapiDesc = Marshal.SizeOf(typeof(MapiFileDesc));
IntPtr pMapiDesc = Marshal.AllocHGlobal(sizeofMapiDesc);

MapiFileDesc fileDesc = new MapiFileDesc();
fileDesc.position = -1;
int ptr = (int)pMapiDesc;

string path = strAttachmentFileName;
fileDesc.name = Path.GetFileName(path);
fileDesc.path = path;
Marshal.StructureToPtr(fileDesc, (IntPtr)ptr, false);

msg.files = pMapiDesc;
msg.fileCount = 1;

return MAPISendMail(session, winhandle, msg, MAPI_LOGON_UI | MAPI_DIALOG, 0);
}

[DllImport("MAPI32.DLL")]
private static extern int MAPISendMail(IntPtr sess, IntPtr hwnd, MapiMessage message, int flg, int rsv);

}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiMessage
{
public int reserved;
public string subject;
public string noteText;
public string messageType;
public string dateReceived;
public string conversationID;
public int flags;
public IntPtr originator;
public int recipCount;
public IntPtr recips;
public int fileCount;
public IntPtr files;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiFileDesc
{
public int reserved;
public int flags;
public int position;
public string path;
public string name;
public IntPtr type;
}
}
You can use it something like this.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SendFileTo;

namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load_1(object sender, EventArgs e)
{
MAPI.SendMail("c://documents//menu.doc", "Here's the lunch menu");
}
}
}



-- modified at 10:14 Tuesday 12th December, 2006
GeneralRe: Anyone know how to do this with C#
c-a-b-
11:31 12 Dec '06  
Thanks. I'll give this a shot and see what happens.
GeneralRe: Anyone know how to do this with C#
Sriharsha R
2:19 19 Jan '07  
Can you please tell me how to repeat the same for multiple files..

Sriharsha R


Last Updated 18 Jan 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010