Click here to Skip to main content
15,884,099 members
Articles / Programming Languages / C++

SendTo Mail Recipient

Rate me:
Please Sign up or sign in to vote.
4.87/5 (25 votes)
31 Mar 20033 min read 396.7K   2K   45  
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.
// SendMail.cpp : Defines the entry point for the console application.
//

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

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

	// Constructor
public:
	CDataObject(LPTSTR szFiles)
	{
		Reset();
		
		if (!szFiles)
		{
			m_szFiles = NULL;
			return;
		}

		// 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;
	}

public:
	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 (see constructor)

		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 
		::GlobalUnlock(hglbCopy); 

		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;
		}
		else
			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,
            /* [length_is][size_is][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 __RPC_FAR *__RPC_FAR *ppenum)
	{
		return S_OK;
	}
};



// helper
//
// purpose:tells us whether the file exists
BOOL FileExists(LPTSTR szFilename)
{
	BOOL bFound = FALSE;

	WIN32_FIND_DATA findFile;
	HANDLE hContext;
	hContext=::FindFirstFile(szFilename, &findFile);
	if (  hContext != INVALID_HANDLE_VALUE )
	{
		::FindClose(hContext);
		bFound = TRUE;
	}
	return bFound;
}

int main(int argc, char* argv[])
{
	long nTotalLength = 0, i;

	// read cmdline, and make sure that files actually exist before they are sent
	for (i=1; i<argc; i++)
		nTotalLength += FileExists(argv[i]) ? (_tcslen(argv[i]) + 1) : 0;	

	if ( nTotalLength==0 )
		return 0; // good bye!

	LPTSTR szCmdLine = new TCHAR[nTotalLength+1];
	if (!szCmdLine)
		return 0;

	*szCmdLine = 0;

	for (i=1; i<argc; i++)
	{
		_tcscat(szCmdLine, argv[i]);	
		_tcscat(szCmdLine, "\n");
	}

	if ( szCmdLine )
	{

		// sendmail helper (requires either OE or Outlook)
		static const IID CLSID_SendMail = { 0x9E56BE60, 0xC50F, 0x11CF, 
		   { 0x9A, 0x2C, 0x00, 0xA0, 0xC9, 0x0A, 0x90, 0xCE } 
		};

 		CDataObject cdobj(szCmdLine);
		IDataObject *pDataObject = &cdobj;

		::CoInitialize(NULL);

		IDropTarget *pDropTarget = NULL;

		HRESULT hr;

		// sendmail object
		hr = ::CoCreateInstance(CLSID_SendMail, NULL, CLSCTX_ALL,
								IID_IDropTarget, 
								(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);

			::Sleep(6*1000);

			pDropTarget->Release();
		}

		::CoUninitialize();

	}

	delete [] szCmdLine;

	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
France France
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