Click here to Skip to main content
Click here to Skip to main content

Programmatically adding attachments to emails

By , 18 Jan 2006
 

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.

  • 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

David M Brooks
Web Developer
United Kingdom United Kingdom
Member
When Dave isn't trying to play drums, fly helicopters or race cars, he can be found trying to be CTO at www.eventility.co.uk He must try harder!
 
You can read Dave's ramblings in his blog Aliens Ate My GUI
 
Or, if you can be bothered, he twitters on BoomerDave

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionNice, works now with wxwidgets as wellmemberMember 90858329 Jul '12 - 8:12 
i just changed a bit, so it works with wxWidgets, thanks for this easy solution.
Questionsendto with attachment solutionmembermanywebbs9 Sep '11 - 18:14 
how do I implement this solution in outlook?, I downloaded the code but do not know where to add it or place it. Many thanks
GeneralMy vote of 5memberitz_faraz5 May '11 - 19:31 
Excellent rating goes to David Brooks for converting this into C# in the comments section below!
GeneralI hate to nag but this code sample is bad...memberdc_200025 Apr '11 - 17:46 
I appreciate the author taking time to post this code sample (and I understand that it is somewhat old), but there are several major issues with it:
 
1. Even though the author attempted to put Unicode specific types (TCHAR instead of char, etc.), he clearly didn't try compiling it under Unicode. Within that specification the code has a bunch of holes... so be aware!
 
2. The code has an obvious memory leak when the mapi library is loaded with LoadLibrary() call but never unloaded.
 
3. Also why posting what is already available in description in the download link? Why not create a small compilable project?
QuestionCan I use this solution for my problem....memberdkperez21 Jan '10 - 12:16 
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 UNSAFEmembersashasawchai1 Dec '08 - 22:21 
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 | :WTF:
 
Thanks be to Microsoft. Mad | :mad:
 
You have been warned...
GeneralRe: WARNING = THIS CODE PROBABLY UNSAFEmemberDavid M Brooks27 Aug '09 - 5:14 
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]memberArielR2 Jul '08 - 10: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

GeneralRe: to send more than 1 mail addressmemberpine_le11 Apr '10 - 4:28 
recipient[0].ulRecipClass = MAPI_TO;
recipient[0].lpszName = ArrayEmail.GetBuffer(0);
recipient[0].lpszAddress = "";
recipient[0].ulEIDSize = 0;
recipient[0].lpEntryID = NULL;
here, ArrayEmail = "user1@test.com; user2@test.com; ...."
Questionwhat for ?memberkilt25 Jun '08 - 19:59 
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 lpszFileNamemembertiggerlily29 Nov '07 - 23:08 
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.

Questionwhy it saves the draft into inbox folder?memberxuxiaohui7 Nov '07 - 22:37 
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)membercgalpin1 Nov '07 - 4:59 
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)memberVarchas R S4 Nov '07 - 23:03 
Same problem with me too!!!! Frown | :(
GeneralRe: Newbie problem - can't compile (get errors in mapi.h)membercgalpin6 Nov '07 - 7:42 
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 emailmemberkryzzozz7810 Oct '07 - 9:09 
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 emailmemberMember 216347031 Mar '08 - 4:56 
I have the same problem!
Generalprofile problem with MAPImemberrob010329 Jul '07 - 23:48 
this code works great !!! thaaanksBig Grin | :-D
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 MAPImemberBhushanKalse27 Aug '07 - 20:28 
Have a look at this, it might help you:
http://support.microsoft.com/kb/180233
Generalthanks!membercronky7812 Jul '07 - 20:17 
this really helped
Generalsending problemmember133mmxtr11 Jun '07 - 2:20 
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]memberajitatif angajetor24 Jan '07 - 2:49 
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 ??memberhannahb21 Mar '07 - 3:22 
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 ??memberajitatif angajetor27 Mar '07 - 6:21 
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 & sourcemembershital_harode@rediffmail.com17 Nov '06 - 23:24 
Plz send me the source & demo
as early as possible
 
Thank u.
 
Shital
 
dfgsd

GeneralRe: wants demo & sourcememberprathuraj5 Jun '07 - 23:10 
what u want
GeneralRe: wants demo & sourcememberGeorge Zorba21 Mar '12 - 21:29 
he wants a sample html page calling it i guess !
GeneralAnyone know how to do this with C#memberc-a-b-24 Aug '06 - 8:49 
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]memberDavid Broooks12 Dec '06 - 4:01 
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#memberc-a-b-12 Dec '06 - 10:31 
Thanks. I'll give this a shot and see what happens.
GeneralRe: Anyone know how to do this with C#memberSriharsha R19 Jan '07 - 1:19 
Can you please tell me how to repeat the same for multiple files..
 
Sriharsha R

AnswerRe: Anyone know how to do this with C#membergoondoo277 Feb '07 - 10:52 
Here's the code. The only code I'm a little concerned with is:
 
int ptr = (int)ptrMapiDesc + (iFilePtr * Marshal.SizeOf(typeof(MapiFileDesc)));
 
This may not be safe. But I tested it and it works.
 
Good luck,
John
 

 
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[] aAttachments, string sSubject, string sBody)
{
IntPtr ptrSession = new IntPtr(0);
IntPtr ptrWinHandle = new IntPtr(0);
int iFiles = 0;
int iFilePtr = 0;
 
// Create a message
MapiMessage msg = new MapiMessage();
msg.subject = sSubject;
msg.noteText = sBody;
 
// Get the number of files
iFiles = aAttachments.GetLength(0);
MapiFileDesc[] fileDesc = new MapiFileDesc[iFiles];
 
int iSizeofMapiDesc = Marshal.SizeOf(typeof(MapiFileDesc)) * iFiles;
IntPtr ptrMapiDesc = Marshal.AllocHGlobal(iSizeofMapiDesc);

foreach(string sAttachment in aAttachments)
{
fileDesc[iFilePtr] = new MapiFileDesc();
fileDesc[iFilePtr].position = -1;
int ptr = (int)ptrMapiDesc + (iFilePtr * Marshal.SizeOf(typeof(MapiFileDesc)));
string sPath = sAttachment;
fileDesc[iFilePtr].name = Path.GetFileName(sPath);
fileDesc[iFilePtr].path = sPath;
Marshal.StructureToPtr(fileDesc[iFilePtr], (IntPtr)ptr, false);
 
iFilePtr++;
}
 
msg.files = ptrMapiDesc;
msg.fileCount = iFiles;
 
return(MAPISendMail(ptrSession, ptrWinHandle, msg, MAPI_LOGON_UI | MAPI_DIALOG, 0));
}
 
[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;
}
}
GeneralRe: Anyone know how to do this with C#memberitz_faraz5 May '11 - 19:32 
Most excellent ! Thumbs Up | :thumbsup: Cool | :cool:
GeneralRe: Anyone know how to do this with C#memberDavid Broooks8 Feb '07 - 4:11 
I just did a follow up here www.codeproject.com/cs/internet/SendFileToNET.asp for C# and VB.NET
GeneralAnti Virus issuesmemberDavid Broooks23 May '06 - 11:34 
I've been using this for a while now and suddenly Outlook 2003 is freezing when the email arrives in my inbox. It turns out that the version of McAfee I'm using (VirusScan Enterprise v 8.0.0) has some sensitivities to emails with attachement generated like this. The causes are \ or / in the display filename and MIME type settings.
 
So take care when setting the filename in MapiFileDesc. After all it's only for display purposes in the email.
 
MapiFileDesc fileDesc;
fileDesc.lpszFileName = "something with out slashes";
 
Also, force the email to be text/plain by putting something in the note text This will force the email to be plain text regardless of the users preference settings (HTML/RTF/text).
 
MapiMessage message;
message.lpszNoteText = "blah";
 
For examples of how you might want to default these values, try selecting a file, right clicking and selecting Send To -> Mail recipient. Notice how Outlook sets the default values.
 
Let me know if you spot any other AV issues.
GeneralRe: Anti Virus issues - there's more....memberDavid Broooks23 May '06 - 11:45 
I fixed the code like this:
 
MapiFileDesc fileDesc;
::ZeroMemory(&fileDesc, sizeof(fileDesc));
fileDesc.nPosition = (ULONG)-1;
fileDesc.lpszPathName = szPath;
fileDesc.lpszFileName = ::PathFindFileName(szFileName); // Remove the path
 
It simply removes the path and just leaves the filename.
 
Sorted.
GeneralAutomatically Send and Multiple AttachmentsmemberJeffBilkey21 May '06 - 14:40 
Is there any way to automatically Send the email without pressing the Send button
 
And
 
How do attach two or more files?

GeneralRe: Automatically Send and Multiple AttachmentsmemberRishabh Agrawal18 Sep '06 - 19:05 
For multiple attachments use the following code:
 
bool SendMail(HWND hWndParent, const std::vector &vFiles)
{
if (vFiles.size() <= 0)
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;
 
const int iNumFiles = vFiles.size();
char szSubject[_MAX_PATH];
::StrCpy(szSubject, "");
 
MapiFileDesc *fileDesc = new MapiFileDesc[iNumFiles];
LPSTR *szFileName = new LPSTR[iNumFiles];
for(int i = 0; i < iNumFiles; ++i)
{
szFileName[i] = new char[_MAX_PATH];
::StrCpy(szFileName[i], CT2CA(vFiles[i]) );
 
::ZeroMemory(&fileDesc[i], sizeof(MapiFileDesc));
fileDesc[i].nPosition = (ULONG)-1;
fileDesc[i].lpszPathName = szFileName[i];
fileDesc[i].lpszFileName = szFileName[i];
}
 
MapiMessage message;
::ZeroMemory(&message, sizeof(message));
message.lpszSubject = szSubject;
message.nFileCount = iNumFiles;
message.lpFiles = fileDesc;
 

int nError = SendMail(0, (ULONG_PTR)hWndParent, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
for(int i = 0; i < iNumFiles; ++i)
{
delete[] szFileName[i];
}
delete[] szFileName;
delete[] fileDesc;
 
if (nError != SUCCESS_SUCCESS && nError != MAPI_USER_ABORT && nError != MAPI_E_LOGIN_FAILURE)
return false;
 
return true;
}
 
Usage:
 
void Test()
{
std::vector vFiles;
vFiles.push_back(_T("C:\\Test1.pfd"));
vFiles.push_back(_T("C:\\Test2.bmp"));
vFiles.push_back(_T("C:\\Test1.txt"));
 
SendMail(AfxGetMainWnd()->GetSafeHwnd(), vFiles);
}
 
Note*
In the vector, add full path of the files to be attached.
GeneralUnicode MFC compatible Multiple Attachmentmemberhannahb21 Mar '07 - 5:11 
#ifndef __SENDFILETO_H__
#define __SENDFILETO_H__
 
#include <mapi.h>
#include
 
#define MAX_MESSAGE 2048
 
class CSendFileTo
{
public:
bool SendMail(HWND hWndParent, CString const &strAttachmentFileName, CString const &strSubject=_T("")
, CString messageBody=_T(""))
{
CStringArray strArray;
strArray.Add(strAttachmentFileName);
return (SendMail(hWndParent, strArray, strSubject, messageBody));
}
 
bool SendMail(HWND hWndParent, CStringArray const &strAttachmentFileNames, CString const &strSubject=_T(""),
CString messageBody=_T(""))
{
if (strAttachmentFileNames.GetSize()<=0)
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;
 
USES_CONVERSION;
char szSubject[_MAX_PATH];
int iNumFiles=strAttachmentFileNames.GetSize();
 
::strcpy(szSubject, W2A(strSubject.GetString()));
 
MapiFileDesc *fileDesc = new MapiFileDesc[iNumFiles];
LPSTR *szFileName = new LPSTR[iNumFiles];
for(int i = 0; i < iNumFiles; ++i)
{
szFileName[i] = new char[_MAX_PATH];
::strcpy(szFileName[i], W2A(strAttachmentFileNames[i]) );
 
::ZeroMemory(&fileDesc[i], sizeof(MapiFileDesc));
fileDesc[i].nPosition = (ULONG)-1;
fileDesc[i].lpszPathName = szFileName[i];
fileDesc[i].lpszFileName = szFileName[i];
}
 
MapiMessage message;
::ZeroMemory(&message, sizeof(message));
message.lpszSubject = szSubject;
message.nFileCount = iNumFiles;
message.lpFiles = fileDesc;
LPSTR messageBodyArray = new char[MAX_MESSAGE];
strncpy (messageBodyArray, W2A(messageBody.GetString()), MAX_MESSAGE);
message.lpszNoteText = messageBodyArray;
 
int nError = SendMail(0, (ULONG_PTR)hWndParent, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
 
for(int i = 0; i < iNumFiles; ++i)
{
delete[] szFileName[i];
}
delete[] szFileName;
delete[] fileDesc;
delete messageBodyArray;

if (nError != SUCCESS_SUCCESS && nError != MAPI_USER_ABORT && nError != MAPI_E_LOGIN_FAILURE)
return false;
 
return true;
}
};
 
#endif
QuestionTwo Questions?memberSstar1 May '06 - 17:13 
I have got the code Working with my MFC project. However, I have 2 questions.
 
1. You mentioned in the article, "To remove the Modeness, simply change the hWnd parameter to HWND_DESKTOP". I tried to put both "HWND_DESKTOP" and "NULL" as the hWnd, but in both cases, mail dialog appears still be Application Modal to my main program. And clicking on the "Minimize" button on mail dialog simply fails to minimize the dialog. Do you have any idea on this?
 
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.
 
Thank you in advance if you could help on either of the problem.
AnswerRe: Two Questions?memberxuxiaohui7 Nov '07 - 22:05 
I have the same question about your question2.Confused | :confused:
who can help us?
Thank you!
AnswerRe: Two Questions?membernovta20 Nov '07 - 4:02 
I have fixed Outlook modal behaver with a new thread started in place of SendMail function call before. Function called by thread SendMailBackground call MAPISendMail. Message is not modal any more in my application but it is still modal in Outlook.
 
code sample:
 
MapiFileDesc* mapi_files;
mapi_files = new MapiFileDesc[num_of_files];
MapiFileDesc* current_file = mapi_files;
 
for (i=0; inPosition = (ULONG)-1;
current_file->lpszPathName = localPaths[i];
current_file->lpszFileName = localPaths[i];
current_file++;
}
MapiMessage* message = new MapiMessage;
ZeroMemory(message, sizeof(MapiMessage));
message->lpszSubject = szSubject);
message->nFileCount = num_of_files;
message->lpFiles = mapi_files;
//SendMail(0, (unsigned long)HWND_DESKTOP, message, API_LOGON_UI|MAPI_DIALOG, 0);
DWORD dwThreadId = 0;
CreateThread(NULL, 0,
SendMailBackroung, (LPVOID)message, 0, &dwThreadId);
// end of message sending in primary thread
 
// SendMailBackground function
DWORD static WINAPI SendMailBackroung(LPVOID ptr)
{
MapiMessage* message = (MapiMessage*)ptr;
ULONG (PASCAL *SendMail)(LHANDLE, ULONG, MapiMessage*, FLAGS, ULONG);
DWORD isOK = 1;
int i = 0;
HINSTANCE hMAPI = LoadLibrary(_T("MAPI32.DLL"));
 
if(hMAPI)
{
(FARPROC&)SendMail = GetProcAddress(hMAPI, "MAPISendMail");
 
if (SendMail)
isOK = SendMail(0, (ULONG)HWND_DESKTOP, message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
FreeLibrary(hMAPI);
}
for (i=0; inFileCount; i++)
{
delete message->lpFiles->lpszFileName;
}
delete message->lpFiles;
delete message->lpszSubject;
delete message->lpszNoteText;
delete message;
return isOK;
}

GeneralC++ Builder VersionmemberJeffBilkey26 Apr '06 - 14:33 
Has anyone done this in Borland C++?
GeneralRe: C++ Builder VersionmemberJeffBilkey27 Apr '06 - 11:25 
I have got it going now with C++ Builder sort of. I have set the following
#define TCHAR char
#define _T LPCSTR
#define CString char
plus a few other minor changes.
This starts outlook express and everything appears correct however the New Message Window is locked up apart from being able to type into the TO: edit box. So you can't send the mail unless you enter Control N for a new window, then delete new window then the original window works fine as it should. Any ideas?
GeneralRe: C++ Builder VersionmemberJeffBilkey21 May '06 - 14:23 
Figured it out. Use Hide() to give Outlook Express control followed by a Show() to give it back to the application.
GeneralRe: C++ Builder VersionmemberOniko1126 May '06 - 0:24 
Can you post the whole list of changes for borland c++ builder please?
GeneralRe: C++ Builder VersionmemberJeffBilkey29 May '06 - 16:14 
This Borland C++ version is a simple function that sends attachments, subject and body. The only thing missing I would like is an automatic SEND funcion
 
// EmailTo.cpp
//---------------------------------------------------------------------------
 
#include
#include
#include <mapidefs.h>
#include <mapi.h>
#pragma hdrstop
 
#include "EmailTo.h"
 
//---------------------------------------------------------------------------
 
#pragma package(smart_init)
 
int EMailTo(TCustomForm *MainForm, HWND hWndParent, string strAddress, string strName, string strAttachmentFileName,
string strSubject, string strBody)
{ // Send an email with attachments
// Typical call:
// EMailTo(this, m_hWnd, "sales@mycompany.com", "sales", "c:\\Devel_Test_Email\\attachment.txt",
// "Here's the subject", "Here is the body");
//
// by JLB 22 May 2006
//
// This was an adaption of an MFC routine
// There were several routines to SetFocus and other things that I have had real bad experiences with
// so they are all commented out and the Hide() and Show() commands take care of them all
// The original MFC commmands and the equivalent BCB commands commented out as follows:
// The MFC commands are commented out with //MFC// and the equivalent
// Borland commands are commented out with //BCB//
// If a command does both thenit is commented out with //BCB&MFC//
//
HINSTANCE hMail = NULL;
hMail = ::LoadLibraryA("MAPI32.DLL");
 
if (hMail == NULL)
{
//MessageBox(AFX_IDP_FAILED_MAPI_LOAD);
Application->MessageBox("Failed to load MAPI32.DLL","Email Error - Send Failed",
MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
return 1;
}
assert(hMail != NULL);
 
ULONG (PASCAL *lpfnSendMail)(ULONG, ULONG, MapiMessage*, FLAGS, ULONG);
(FARPROC&)lpfnSendMail = GetProcAddress(hMail, "MAPISendMail");
if (lpfnSendMail == NULL)
{
//MessageBox(AFX_IDP_INVALID_MAPI_DLL);
Application->MessageBox("Invalid MAPI32.DLL","Email Error - Send Failed",
MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
return 1;
}
assert(lpfnSendMail != NULL);
 
TCHAR szOriginator[_MAX_PATH];
TCHAR szAddress[_MAX_PATH];
TCHAR szName[_MAX_PATH];
TCHAR szFileName[_MAX_PATH];
TCHAR szPath[_MAX_PATH];
TCHAR szSubject[_MAX_PATH];
//TCHAR szBody[_MAX_PATH];
int iMaxPath = _MAX_PATH; // _MAX_PATH seems to be 260 so increase it as it doesn't seem to do any damage
char szBody[1024]; // as _MAX_PATH seems to be a minimum rather than a maximum and
// // lpszNoteText is a LPTSTR anyway so this could be increased if required
// Set the attachments
//
strcpy(szAddress, "SMTP:"); // Start with SMTP:
strcat(szAddress, strAddress.c_str()); // Now append the actual email address
strcpy(szName, strName.c_str()); // Name in To: box eg Authorization
strcpy(szPath, strAttachmentFileName.c_str()); // Attachment file name
strcpy(szFileName, strAttachmentFileName.c_str()); // If NULL then taken from szPath
strcpy(szSubject, strSubject.c_str()); // Subject
if(strBody.size() > 1023)
{ // See note just above about increasing body size
Application->MessageBox("Text Body Too Large","Email Error - Text Body > 1023 characters",
MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
return 1;
}
strcpy(szBody, strBody.c_str()); // Body separated by "\n" for carriage returns
//
// Set the file description
//
MapiFileDesc fileDesc;
::ZeroMemory(&fileDesc, sizeof(fileDesc));
fileDesc.nPosition = (ULONG)-1;
fileDesc.lpszPathName = szPath;
fileDesc.lpszFileName = szFileName;
//
// make a recipient
//
MapiRecipDesc recipDesc;
::ZeroMemory(&recipDesc, sizeof(recipDesc));
recipDesc.lpszName = szName; // Name in To: box eg Authorization
recipDesc.ulRecipClass = MAPI_TO; // As in MAPI_CC or MAPI_BC etc
recipDesc.lpszAddress = szAddress; // Actual email address prepended with SMTP:
//
//
// make an originator - Not used
//
strcpy(szOriginator, "originator@mycompany.com");
MapiRecipDesc origDesc;
::ZeroMemory(&origDesc, sizeof(origDesc));
origDesc.lpszName = szName; //
origDesc.ulRecipClass = MAPI_ORIG; // As in MAPI_CC or MAPI_BC etc
origDesc.lpszAddress = szOriginator; // Actual email address prepended with SMTP:
//
// prepare the message
//
// Set the Subject
MapiMessage message;
::ZeroMemory(&message, sizeof(message));
message.lpszSubject = szSubject;
message.nFileCount = 1;
message.lpFiles = &fileDesc;
message.nRecipCount = 1;
//message.lpOriginator = &origDesc;
message.lpRecips = &recipDesc;
message.lpszNoteText = szBody;
 
// prepare for modal dialog box
//MFC// AfxGetApp()->EnableModeless(FALSE);
//BCB// HRESULT STDMETHODCALLTYPE EnableModeless(FALSE);
/////HWND hWndTop;
/////HWND* pParentWnd = HWND::GetSafeOwner(NULL, &hWndTop);
// some extra precautions are required to use
// MAPISendMail as it tends to enable the parent
// window in between dialogs (after the login
// dialog, but before the send not dialog).
//MFC//pParentWnd->SetCapture();
//BCB// SetCapture(pParentWnd);
//BCB&MFC// ::SetFocus(NULL);
//MFC// pParentWnd->m_nFlags |= WF_STAYDISABLED;
 
MainForm->Hide();
 
int nError = lpfnSendMail(0, (ULONG_PTR)hWndParent, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
 
// after returning from the MAPISendMail call,
// the window must be re-enabled and focus
// returned to the frame to undo the workaround
// done before the MAPI call.
//BCB&MFC// ::ReleaseCapture();
//MFC// pParentWnd->m_nFlags &= ~WF_STAYDISABLED;
 
MainForm->Show();
 
//MFC// pParentWnd->EnableWindow(TRUE);
//BCB&MFC// ::SetActiveWindow(NULL);
//MFC// pParentWnd->SetActiveWindow();
//MFC// pParentWnd->SetFocus();
//MFC// if (hWndTop != NULL)
//MFC// ::EnableWindow(hWndTop, TRUE);
//MFC// AfxGetApp()->EnableModeless(TRUE);
 
if (nError != SUCCESS_SUCCESS && nError != MAPI_USER_ABORT && nError != MAPI_E_LOGIN_FAILURE)
{
// AfxMessageBox(AFX_IDP_FAILED_MAPI_SEND);
Application->MessageBox("Failed to send email","Email Error - Send Failed",
MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
return 1;
}
::FreeLibrary(hMail);
 
return 0;
}
=======================================================================
// EmailTo.h
//---------------------------------------------------------------------------
#include
 
#ifndef EmailToH
#define EmailToH
//---------------------------------------------------------------------------
using namespace std;
 
int EMailTo(TCustomForm *MainForm, HWND hWndParent, string strAddress, string StrName, string strAttachmentFileName,
string strSubject, string strBody);
#endif

GeneralSending to Outlook 2003memberCorey W15 Mar '06 - 8:13 
Hi All,
 
I tried to send an e-mail and attachment out via Outlook 2003. The e-mail dialog comes up with the right recipient and I 'Send' it. My backend mailer is Exchange Server.
 
However, a couple of seconds later, I get an undeliverable message error.
 
Your message did not reach some or all of the intended recipients.
 
Subject: Error report for xxx.exe
Sent: 15-Mar-06 12:21
 
The following recipient(s) could not be reached:
 
'Software Support' on 15-Mar-06 12:21
None of your e-mail accounts could send to this recipient.
 
When I looked at the OutBox copy of the message there is no 'Sender' and when I dbl-click on the 'To' address, I see a questionable E-mail type - it's the recipient e-mail address. I would have expected it to be 'SMTP', shouldn't I?
 
If anyone has an idea what is going on here, please let me know.
 
Thanks in Advance!
Corey.
 

 
-- modified at 14:23 Wednesday 15th March, 2006
GeneralRe: Sending to Outlook 2003memberDavid Broooks22 Mar '06 - 23:47 
Sounds like a malformed string somewhere that causing Exchange Server to barf. Send me the code (fragement) and I'll have a look.
GeneralRe: Sending to Outlook 2003memberCorey W4 Apr '06 - 18:13 
Thanks David,
 
here's my snippet.
 
ULONG nError = lpfnSendMail(0, (ULONG)hWnd, &message, MAPI_LOGON_UI | MAPI_DIALOG, 0);
 
'message' is (from VC debugger).   Let me know if you can't read this:
 
-          &message     0x0011d868 {ulReserved=0 lpszSubject=0x0011d658 "Error report for XXXXX.exe" lpszNoteText=0x00000000 <Bad Ptr> ...}     MapiMessage *
          ulReserved     0     unsigned long
+          lpszSubject     0x0011d658 "Error report for XXXXX.exe"     char *
+          lpszNoteText     0x00000000 <Bad Ptr>     char *
+          lpszMessageType     0x00000000 <Bad Ptr>     char *
+          lpszDateReceived     0x00000000 <Bad Ptr>     char *
+          lpszConversationID     0x00000000 <Bad Ptr>     char *
          flFlags     0     unsigned long
+          lpOriginator     0x00000000 {ulReserved=??? ulRecipClass=??? lpszName=??? ...}     MapiRecipDesc *
          nRecipCount     1     unsigned long
-          lpRecips     0x0011dcc0 {ulReserved=0 ulRecipClass=1 lpszName=0x0011dab0 "dumps@wirun.homelinux.net" ...}     MapiRecipDesc *
          ulReserved     0     unsigned long
          ulRecipClass     1     unsigned long
+          lpszName     0x0011dab0 "XXXXX@XXXXX.net"     char *
+          lpszAddress     0x0011d8a0 "XXXXX@XXXXX.net"     char *
          ulEIDSize     0     unsigned long
          lpEntryID     0x00000000     void *
          nFileCount     1     unsigned long
-          lpFiles     0x0011e100 {ulReserved=0 flFlags=0 nPosition=4294967295 ...}     MapiFileDesc *
          ulReserved     0     unsigned long
          flFlags     0     unsigned long
          nPosition     4294967295     unsigned long
+          lpszPathName     0x0011def0 "e:\04-Apr-06.zip"     char *
+          lpszFileName     0x0011dce0 "04-Apr-06.zip"     char *
          lpFileType     0x00000000     void *
+          hWnd     0x000d0958 {unused=0 }     HWND__ *
          lpfnSendMail     0x61e074ac     unsigned long (unsigned long, unsigned long, MapiMessage *, unsigned long, unsigned long)*
          nError     3435973836     unsigned long

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 18 Jan 2006
Article Copyright 2006 by David M Brooks
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid