Click here to Skip to main content
14,930,722 members
Articles / Programming Languages / C++
Posted 21 Mar 2003


45 bookmarked

SendTo Mail Recipient

Rate me:
Please Sign up or sign in to vote.
4.85/5 (24 votes)
31 Mar 20033 min read
Programmatically use the SendTo mail recipient shortcut
This article provides a code snippet in order to programmatically send one or more files to the mail recipient, mimicking the SendTo mail recipient shell extension. I have heard many people searching for this feature, and actually I thought that, as there doesn't seem to be such source code posted on the net, I could just as well post it.

Introducing the SendTo Mail Shortcut

You may skip this section if you are not interested in the r.e. technique.

The SendTo mail shortcut is a shell extension. See Mike Dunn's complete idiot guide for further information.

The trick is to find out that the SendTo mail recipient shortcut is actually essentially an empty file with .MAPIMAIL as (hidden) extension name. Then, by looking up file type association in the registry (HKCR\.MAPIMAIL), it's straight forward to figure out that it is targeting a COM object with clsid = {9E56BE60-C50F-11CF-9A2C-00A0C90A90CE}. This object is sendmail.dll, a COM helper which takes advantage of either Outlook Express or Outlook to send e-mail file attachments. Next, looking up this CLSID in OLE View clearly showed that the sendmail COM component also implements the IDropTarget, IShellExtInit and IPersistFile interfaces, just like any drop handler.

Ok, basically I need to prepare a bag with dropped filenames, make sure they can be retrieved by implementing the IDataObject interface, a simple communication interface, and then mimic a standard drag and drop sequence, with two check points : DragEnter(IDataObject*) and Drop(IDataObject*).

One of the interesting points is to start implementing the IDataObject interface starting with contract, i.e., the methods it is supposed to expose. And then cowardly insert a breakpoint into any default method implementation only to get to know whether it's called or not, and in what order.

If you are interested in mimicking other SendTo shortcuts, don't hesitate to check out this registry key: HKLM\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved.

IDataObject Implementation

The following code implements the IDataObject interface. In fact, only a few methods are required to be implemented. Those are, in order:

  • EnumFormatEtc() called by the sendmail helper to know what content formats the IDataObject is holding
  • IEnumFORMATETC::Next(),Reset() called to list all formats. We are expected to let the sendmail helper know that we do hold the CF_HDROP clipboard format (standard used for file drag-and-drop support), even if in fact we are not using the clipboard at all.
  • GetData() called to actually get the list of files we are willing to send.

Because on Windows file drag-and-drop operations rely on the CF_HDROP / DROPFILES structure, we simply prepare such a structure to play with. Here is the code :

#include <windows.h>
#include <ole2.h>   // IDataObject
#include <shlobj.h> // DROPFILES
#include <tchar.h>  // TCHAR

class CDataObject : public IDataObject, IEnumFORMATETC
  // Members
  BOOL m_bReset;
  LPTSTR m_szFiles;
  int m_nLen;

    // Constructor
  CDataObject(LPTSTR szFiles)

    if (!szFiles)
      m_szFiles = NULL;

    // replace \n chars with \0 chars
    m_nLen = _tcslen(szFiles)+1;
    m_szFiles = new TCHAR[m_nLen];
    memcpy(m_szFiles, szFiles, m_nLen * sizeof(TCHAR));

    LPTSTR szTmp = m_szFiles;
    while ( szTmp=_tcschr(szTmp,'\n') )
      *szTmp++ = '\0';

  virtual ~CDataObject()
    delete [] m_szFiles;

  HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject)
    *ppvObject = (IDataObject*) this;
    return S_OK;

  ULONG __stdcall AddRef()
    return 1;
  ULONG __stdcall Release()
    return 0;

  // IDataObject implementation
  HRESULT __stdcall GetData(FORMATETC* pFormatetc, STGMEDIUM* pmedium)
    if (pFormatetc->cfFormat != CF_HDROP  || !pmedium)
      return S_FALSE;

    if (!m_szFiles)
      return S_FALSE; // make sure to set the files before

    pmedium->tymed = TYMED_HGLOBAL;

    // set DROPFILES structure
    HGLOBAL hglbCopy = ::GlobalAlloc(GMEM_MOVEABLE, 
                 sizeof(DROPFILES) + (m_nLen + 2) * sizeof(TCHAR)); 
    LPDROPFILES pDropFiles = (LPDROPFILES) ::GlobalLock(hglbCopy);
    pDropFiles->pFiles = sizeof(DROPFILES);
    pDropFiles->pt.x = pDropFiles->pt.y = 0;
    pDropFiles->fNC = TRUE;
    pDropFiles->fWide = FALSE; // ANSI charset
    LPTSTR lptstrCopy = (LPTSTR) pDropFiles;
    lptstrCopy += pDropFiles->pFiles;
    memcpy(lptstrCopy, m_szFiles, m_nLen * sizeof(TCHAR)); 
    lptstrCopy[m_nLen] =  '\0';    // null character 
    lptstrCopy[m_nLen+1] = '\0';    // null character 

    pmedium->hGlobal = hglbCopy;
    pmedium->pUnkForRelease = NULL;

    return S_OK;

  HRESULT __stdcall GetDataHere(FORMATETC* pFormatetc, STGMEDIUM* pmedium)
    return S_OK;

  HRESULT __stdcall QueryGetData(FORMATETC* pFormatetc)
    return S_OK;

  HRESULT __stdcall GetCanonicalFormatEtc(FORMATETC* pFormatetcIn, 
                                          FORMATETC* pFormatetcOut)
    return S_OK;

  HRESULT __stdcall SetData(FORMATETC* pFormatetc, 
                   STGMEDIUM* pmedium, BOOL fRelease)
    return S_OK;

  HRESULT __stdcall EnumFormatEtc(DWORD dwDirection, 
                        IEnumFORMATETC** ppenumFormatetc)
    if (dwDirection==DATADIR_GET)
      *ppenumFormatetc = this;
      return S_OK;
      return S_FALSE;

  HRESULT __stdcall DAdvise(FORMATETC* pFormatetc, 
                            DWORD advf, 
                            IAdviseSink* pAdvSink, 
                            DWORD* pdwConnection)
    return S_OK;

  HRESULT __stdcall DUnadvise(DWORD dwConnection)
    return S_OK;

  HRESULT __stdcall EnumDAdvise(IEnumSTATDATA** ppenumAdvise)
    return S_OK;

  // IEnumFORMATETC implementation
  HRESULT __stdcall Next( 
            /*[in]*/ ULONG celt,
            /*[out]*/ FORMATETC __RPC_FAR* rgelt,
            /*[out]*/ ULONG __RPC_FAR* pceltFetched)
    if (!m_bReset) return S_FALSE;

    m_bReset = FALSE;

    FORMATETC fmt;
    fmt.cfFormat = CF_HDROP;
    fmt.dwAspect = DVASPECT_CONTENT;
    fmt.lindex = -1;
    fmt.ptd = NULL;
    fmt.tymed = TYMED_HGLOBAL;
    *rgelt = fmt; // copy struct
    if (pceltFetched) *pceltFetched = 1;

    return S_OK;
  HRESULT __stdcall Skip(/*[in]*/ ULONG celt)
    return S_FALSE;
  HRESULT __stdcall Reset()
    m_bReset = TRUE;
    return S_OK;
  HRESULT __stdcall Clone( 
            /* [out] */ IEnumFORMATETC** ppenum)
    return S_OK;

Using It

And here is how to use it to send c:\346.jpg and c:\tmp\


CDataObject cdobj("c:\\346.jpg\nC:\\tmp\\");
IDataObject *pDataObject = &cdobj;

IDropTarget *pDropTarget = NULL;

// create an instance, and mimic drag-and-drop
hr = ::CoCreateInstance( CLSID_SendMail,
                       NULL, CLSCTX_ALL,
                       (void **)&pDropTarget);
if (SUCCEEDED(hr))
  POINTL pt = {0,0};
  DWORD dwEffect = 0;
  pDropTarget->DragEnter(pDataObject, MK_LBUTTON, pt, &dwEffect);
  pDropTarget->Drop(pDataObject, MK_LBUTTON, pt, &dwEffect);




To compile this code, you only need WIN32.

Why SendTo is Better than the mailto Trick

You could tell me that shell-executing a mailto URL is just as fine, and much simpler code in practice. Yes and no. Yes, it does so, and it can't be simpler since that's only one line of code (just remember to escape ASCII chars in the body with hex replacements of the form %0D, %20, ...).

No, it does not provide support for file attachment(s), which is why the SendTo shortcut comes handy. And there is more to it. The truth is that ::ShellExecute() cannot handle parameter strings over 2048 bytes, which means your e-mail body size cannot go beyond 2048 bytes. If you try to send a large e-mail, it will result in a GPF. At this point, you could replace ::ShellExecute with a well thought ::WinExec call, using the actual mailto command line declared in the registry and target the current e-mail client (for instance, "%ProgramFiles%\Outlook Express\msimn.exe" /mailurl:%1). But then the limitation is 32 KB. As a conclusion, there is no way to send e-mails larger than 32KB using the mailto protocol. The SendTo shortcut explained in this article is definitely the way to go !


  • 22nd March, 2003: First release
  • 1st April, 2003: Updated - Removal of the clipboard use (Chris Guzak)


This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


About the Author

Addicted to reverse engineering. At work, I am developing business intelligence software in a team of smart people (independent software vendor).

Need a fast Excel generation component? Try xlsgen.

Comments and Discussions

QuestionHow can I use this method to use feature SendTo MailRecipient?? Pin
Member 121828615-Mar-16 0:38
MemberMember 121828615-Mar-16 0:38 
GeneralThe most simple solution Pin
Lord_Crow10-Jul-09 1:57
MemberLord_Crow10-Jul-09 1:57 
GeneralFabulous - but would like a small change Pin
StroudPete14-May-09 3:27
MemberStroudPete14-May-09 3:27 
This is a great utility. Having written an big system in dotnet for a small company who could not handle an smtp server, I was annoyed to find no way in .net of simulating mailto but with attachments. Surely there must be lots of people wanting to send (say) an invoice from an app as an email attachment - getting the installed email client's new email window with attachment done is the perfect way, allowing the rest of the info to be typed. This is perfect for that - launch SendTo with a Process.Start command having set the appropriate parameters (including WindowStyle = ProcessWindowStyle.Hidden) works perfectly.

However I would like a small change, the ability to omit the destination (-to) and NOT get the default"". Without a destination address, the email can't be sent, but with a default an inattentive user can send it without entering the correct address. This is what happens with Send To - Mail Recipient. Smile | :)
GeneralHelp with other send to methods Pin
WillRGV2-Nov-07 5:20
MemberWillRGV2-Nov-07 5:20 
GeneralRe: Help with other send to methods Pin
WillRGV2-Nov-07 6:57
MemberWillRGV2-Nov-07 6:57 
GeneralRe: Help with other send to methods Pin
jkjhcjbhj12-Feb-08 3:25
Memberjkjhcjbhj12-Feb-08 3:25 
GeneralSendTo.exe Pin
jbergetun5-Mar-07 1:01
Memberjbergetun5-Mar-07 1:01 
GeneralUnhandled exception in ntdll.dll Pin
Ben Kial17-Oct-05 14:05
MemberBen Kial17-Oct-05 14:05 
QuestionNo way to specify recipient ? Pin
Alexis Bietti15-Sep-05 6:28
MemberAlexis Bietti15-Sep-05 6:28 
AnswerRe: No way to specify recipient ? Pin
Stephane Rodriguez.19-Sep-05 2:28
MemberStephane Rodriguez.19-Sep-05 2:28 
GeneralRe: No way to specify recipient ? Pin
Alexis Bietti19-Sep-05 3:39
MemberAlexis Bietti19-Sep-05 3:39 
GeneralRe: No way to specify recipient ? Pin
Stephane Rodriguez.20-Sep-05 1:08
MemberStephane Rodriguez.20-Sep-05 1:08 
Generaloutlook express message window is closed Pin
Anton Heidelwalder22-Aug-05 4:01
MemberAnton Heidelwalder22-Aug-05 4:01 
GeneralDoesn't work :( Pin
Andrew Ovcharov16-Jul-05 13:00
MemberAndrew Ovcharov16-Jul-05 13:00 
GeneralSame Problem Here Pin
idliiyer3-Nov-05 10:03
Memberidliiyer3-Nov-05 10:03 
QuestionRe: Same Problem Here Pin
Ben Kial19-Apr-07 11:17
MemberBen Kial19-Apr-07 11:17 
Generalsend mail failure --; Pin
chunghansu13-Feb-05 21:43
Memberchunghansu13-Feb-05 21:43 
GeneralRe: send mail failure --; Pin
Stephane Rodriguez.26-Feb-05 9:38
MemberStephane Rodriguez.26-Feb-05 9:38 
GeneralC# source code Pin
babalula25-Jan-05 8:35
Memberbabalula25-Jan-05 8:35 
GeneralRe: C# source code Pin
rinofarina11-Aug-10 5:03
Memberrinofarina11-Aug-10 5:03 
Generalmessage body Pin
Sarfraz Anwar15-Jun-04 21:41
MemberSarfraz Anwar15-Jun-04 21:41 
GeneralRe: message body Pin
Stephane Rodriguez.16-Jun-04 1:06
MemberStephane Rodriguez.16-Jun-04 1:06 
GeneralRe: message body Pin
Sarfraz Anwar17-Jun-04 2:18
MemberSarfraz Anwar17-Jun-04 2:18 
GeneralRe: message body Pin
Stephane Rodriguez.17-Jun-04 5:07
MemberStephane Rodriguez.17-Jun-04 5:07 
GeneralRe: message body Pin
Andrew Foss29-Aug-04 18:57
MemberAndrew Foss29-Aug-04 18:57 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.