Hiya, having a spot of trouble getting information on how to attach a file to a Default Mail client under Windows.
The "mailto:" protocol does'nt look like it has this functionality. SDK's talk about OLE and MIME - and get all flustered
(I ca'nt make out what the hell they are saying and only god knows where to get the header and include files they whine about)
on the subject. Is there an idiot's guide to attaching a file to the body of a message - specifically OE6 if possible anyone ? This is
for strictly ligitimate purposes - just incase some-one was wondering. -> Creating a variable name .CSV file and need to send it via
default mail client to known e-mail addy daily. The users typically feel bogged down at present always having to browse and attach
the file themselves.
Thanx
Draakie
Hopefully, somebody else more knowledgeable will reply, but just in case nobody else answers, here's something I did last century in vb. Hopefully it will give you a clue as to the headers involved-
SMTPSend ("From: " + CurFromName$ + vbCrLf)
SMTPSend ("To: " + CurToName$ + vbCrLf)
SMTPSend ("Date: " + CurMsgDate$ + vbCrLf)
SMTPSend ("Subject: " + CurSubject$ + vbCrLf)
' Send the main MIME header
Boundary = "================_" & CDbl(Now) * 10000000000# & "==_" ' the boundary string
SMTPSend ("Mime-Version: 1.0" + vbCrLf)
SMTPSend ("Content-Type: multipart/mixed; boundary=""" + boundary + """" + vbCrLf)
SMTPSend (vbCrLf)
' Send attachment
SMTPSend (vbCrLf + vbCrLf + "--" + boundary + vbCrLf)
afname = LCase$(Dir(CurAttachment$))
SMTPSend ("Content-Type: application/ASC;" + " NAME=""" + afname + """" + vbCrLf)
SMTPSend ("Content-Type: text/plain; charset=""" + "us-ascii""" + vbCrLf)
SMTPSend (vbCrLf)
SMTPSend ("--" + boundary + vbCrLf)
SMTPSend ("Content-Type: text/plain; charset=""" + "us-ascii""" + vbCrLf)
SMTPSend ("Content-Disposition: attachment;" + vbCrLf + vbCrLf)
ch = FreeFile
Open CurAttachment$ For Binary As #ch
Do
Buffer = String$(1024, 0)
Get #ch, , Buffer
Pos = InStr(Buffer, Chr$(0))
If Pos > 0 Then Buffer = Left$(Buffer, Pos - 1)
If Len(Buffer) > 0 Then SMTPSend (Buffer)
Loop Until Len(Buffer) < 1024
Close #ch
' Send the ending boundary string
SMTPSend (vbCrLf)
SMTPSend ("--" + boundary + "--" + vbCrLf)
Dummy = Smtp.SendMail
There are several RFC's having to do with Mime format. Have a look at RFC1341.
You have two options: MIME or OLE (whether either will work is another matter :bdg)
So, MIME is the way 'attachments' are encoded into the body of an email - basically it's a way of creating a text-representation of a file, so it can be inserted into the email and sent along with the 'normal' text. This is what Jimg's example does, and full details are in the relevant RFC(s).
The only problem you might have with this is that you're not sending the email yourself - you're giving it to outlook to send. So chances are that outlook will take your mime'd text and insert it as normal text into the email. Which means when the email client recieves it at the other end, it will come up as garbage added to the end of the email, instead of being recognised as an attachment. If you do have the option of actually sending the email yourself, then this won't be a problem (but you'll probably have other problems to solve ::))
The other way might be through OLE - if outlook provides a way of controlling itself (I think it does - you can create emails through word?), then you should be able to instruct it to create the email with the attachment and send it all off. Details on how to do this are another matter, but I'm guessing the details are out there somewhere. If you find an example from another language (most likely VB or .net) then drag it over here and we might be able to kick it until it gives up and works :wink
[FROM MICROSOFT]
Simple MAPI is a set of functions and related data structures you can use to add messaging functionality to C, C++, or Visual Basic Windows applications. The Simple MAPI functions are available in C and C++ and Visual Basic versions.
[FROM a post by B. BRYANT via http://discuss.joelonsoftware.com]
#include <MAPI.h>
HWND hwndParent = NULL;
const char* pszAttachPathname = "c:\\temp\\test.txt";
const char* pszAttachFilename = "test.txt";
const char* pszTo = "someone@example.com";
const char* pszBody = "blah blah";
ULONG (PASCAL *lpfnMAPISendMail)(ULONG, ULONG, MapiMessage*, FLAGS, ULONG);
HMODULE hMAPI = LoadLibrary( "MAPI32.DLL" );
int nError = -1;
if ( hMAPI )
{
(FARPROC&)lpfnMAPISendMail = GetProcAddress(hMAPI,"MAPISendMail");
if ( lpfnMAPISendMail )
{
MapiMessage message;
MapiFileDesc filedesc;
MapiRecipDesc recip;
memset( &message, 0, sizeof(message) );
if ( pszAttachPathname )
{
ZeroMemory( &filedesc, sizeof(MapiFileDesc) );
filedesc.nPosition = (ULONG)-1;
filedesc.lpszPathName = pszAttachPathname;
filedesc.lpszFileName = pszAttachFilename;
message.nFileCount = 1;
message.lpFiles = &filedesc;
}
ZeroMemory( &recip, sizeof(MapiRecipDesc) );
recip.ulRecipClass = MAPI_TO;
recip.lpszName = pszTo;
message.nRecipCount = 1;
message.lpRecips = &recip;
message.lpszSubject = (char*)(LPCTSTR)csSubject;
message.lpszNoteText = pszBody;
// Bring up the send message dialog
nError = lpfnMAPISendMail(0, (ULONG)hwndParent, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
}
FreeLibrary( hMAPI );
}
return nError; // 0/SUCCESS_SUCCESS 1/MAPI_USER_ABORT, 11/MAPI_E_ATTACHMENT_NOT_FOUND
- would this do Tedd ?
Thanx again
Draakie
Aaah, MAPI - why didn't I think of that?! :lol
That example will do, as long as it does what you want :P
It uses the "MAPISendMail" function which allows you to specify all of the fields and have almost full control over the email created, you can even have it sent automatically. But, of course, more options means there's quite a bit to set up before you can use it (although you can avoid setting most things up and have the user fill in the "To:", "Subject:", etc.)
There's another function, "MAPISendDocuments", which doesn't require half as much set up, but the user has to fill in the all of fields. I thought this might be closer to what you require though - it just does the attachment, nothing else.
..et voila!
.586
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
;***************************************************************************************************
;;"MAPISendDocuments" <http://msdn2.microsoft.com/en-us/library/ms529061.aspx>
;;ULONG MAPISendDocuments(ULONG ulUIParam,LPTSTR lpszDelimChar,LPTSTR lpszFullPaths,LPTSTR lpszFileNames,ULONG ulReserved)
; composes an email, with the given file(s) attached, user must fill in fields
;***************************************************************************************************
.data
AppName db "Snail",0
mapiDll db "MAPI32.DLL",0
mapiSendDocs db "MAPISendDocuments",0
errDll db "Unable to load MAPI32.DLL",0
errFunc db "MAPISendMail function not found.",0
errCompose db "Compose failed",0
files db "C:\\temp\\report.txt",0 ;list of files to attach, delimited by char_sep (eg. "file1;file2;file3")
char_sep db ";"
nil db 0 ;so the filenames are extracted from their pathname
.data?
hMapiDll HMODULE ?
fSendDocs DWORD ? ;function pointer
.code
start:
invoke LoadLibrary, ADDR mapiDll
.IF (eax)
mov hMapiDll,eax
invoke GetProcAddress, eax,ADDR mapiSendDocs
.IF (eax)
mov fSendDocs,eax
push NULL ;ULONG ulReserved
push OFFSET nil ;LPTSTR lpszFileNames
push OFFSET files ;LPTSTR lpszFullPaths
push OFFSET char_sep ;LPTSTR lpszDelimChar
push 0 ;LONG ulUIParam
call DWORD PTR [fSendDocs]
.IF (eax != SUCCESS_SUCCESS)
;** check the error code and give more information
invoke MessageBox, NULL,ADDR errCompose,ADDR AppName,MB_OK or MB_ICONERROR
.ENDIF
.ELSE
invoke MessageBox, NULL,ADDR errFunc,ADDR AppName,MB_OK or MB_ICONERROR
.ENDIF
invoke FreeLibrary, hMapiDll
.ELSE
invoke MessageBox, NULL,ADDR errDll,ADDR AppName,MB_OK or MB_ICONERROR
.ENDIF
invoke ExitProcess, NULL
end start
If you really need the fields automatically filled then we can try using MAPISendMail :8)
[Edit: I'm SO smart I pushed the parameters in the wrong order - surprising it still worked though :dazzled:]
Hi Teddy [(wink) - LOL at PM.]
;----------------------
O.k here's the other example hardcoded into ASM. Seesh this was all easier than
I thought. Just another API really - so why was I stressed ? :P - I skipped the error
checking to make it readable - naughty really.
;----------------------
MapiLibStr db 'MAPI32.dll',0
MapiSndStr db 'MAPISendMail',0
MapiPath db 'C:\test.dat',0
MapiFile db 'test.dat',0
MapiRecip db 'a@b.co.za',0
MapiSubjct db 'Testing MAPI',0
MapiBody db 'Test Test 1 2 3 4 etc.',0
MMessage MapiMessage <?>
MFileDsc MapiFileDesc <?>
MRecipnt MapiRecipDesc <?>
SendMailProc proc hWnd:DWORD
LOCAL hMAPI,dll_SEND : DWORD
invoke LoadLibrary, offset MapiLibStr
mov hMAPI, eax
invoke GetProcAddress,hMAPI, offset MapiSndStr
mov dll_SEND, eax
invoke RtlZeroMemory,addr MMessage, sizeof MMessage
invoke RtlZeroMemory,addr MFileDsc, sizeof MFileDsc
invoke RtlZeroMemory,addr MRecipnt, sizeof MRecipnt
mov MFileDsc.nPosition , -1
mov MFileDsc.lpszPathName , offset MapiPath
mov MFileDsc.lpszFileName , offset MapiFile
mov MRecipnt.ulRecipClass , MAPI_TO
mov MRecipnt.lpszName , offset MapiRecip
mov MMessage.nFileCount , 1
mov MMessage.nRecipCount , 1
mov MMessage.lpFiles , offset MFileDsc
mov MMessage.lpRecips , offset MRecipnt
mov MMessage.lpszSubject , offset MapiSubjct
mov MMessage.lpszNoteText , offset MapiBody
push NULL
push hWnd
push offset MMessage
push MAPI_LOGON_UI or MAPI_DIALOG
push NULL
call dll_SEND
invoke FreeLibrary,hMAPI
ret
SendMailProc endp
Hi everybody,
SendMail works like a charm, with one caveat: While Outlook warns me that "a program is trying to automatically send e-mail" (fine for me), it refuses categorically to take notice of the MAPI_DIALOG flag. I tried several options, with and without MAPI_LOGON_UI and/or MAPI_NEW_SESSION, but no success - the damn thing gets (successfully) sent without user interaction.
A Google search reveils I am not the only one to complain (http://forge.novell.com/pipermail/gwmapi-dev/2005-March/000071.html), but no solutions offered. Does anybody observe the same behaviour?
Well msdn says:
QuoteA dialog box should be displayed to prompt the user for recipients and other sending options. When MAPI_DIALOG is not set, at least one recipient must be specified.
But it depends what you take 'should' to mean :bdg. I'm guessing if there's already a recipient then it's not bothering to wait (which is still contrary to what's implied by the above.)
One thing to check: I made a slight (as in huge) mistake in my previous example - I pushed the parameters in forward order (left-to-right), when they should go in reverse. I did test the code, and by some miracle it works. And Draakie just copied my mistake.
Anyway, I've fixed it now, so you might want to check you're pushing yours in the correct order, which could explain why the flags are being ignored.
Failing that, it's just messed up and you'll have to leave out the recipient address so it's forced to ask for details.
Quote from: Tedd on October 02, 2007, 12:14:49 PMI pushed the parameters in forward order (left-to-right), when they should go in reverse. I did test the code, and by some miracle it works. And Draakie just copied my mistake.
Tedd, how could you possibly do that ::) ??
Anyway, now it works as it should. And I discovered a delicate "feature" of Outlook: if you use the MAPI_BCC feature, it does add a bcc recipient that is NOT visible in the Outlook dialog unless you choose to click on the To: or CC: button to open the "Select names" dialog.
Another "feature" of Outlook: the Send button doesn't work; the user has to press Ctrl+Enter to send it off.
Thanks a lot - this was really helpful. Below the full listing with the "delicate" feature.
.data?
MMessage MapiMessage <?>
MFileDsc MapiFileDesc <?>
MRecipnt MapiRecipDesc <?>
MReciBCC MapiRecipDesc <?>
.data
MapiLibStr db 'MAPI32.dll',0
MapiSndStr db 'MAPISendMail',0
MapiBody db 'Please find attached a file',0
MapiTo db ';',0
MapiBCC db 'MySecretAccount@masm32.com',0
.code
invoke LoadLibrary, offset MapiLibStr
mov hMAPI, eax
invoke GetProcAddress,hMAPI, offset MapiSndStr
mov dll_SEND, eax
invoke RtlZeroMemory,addr MMessage, sizeof MMessage
invoke RtlZeroMemory,addr MFileDsc, sizeof MFileDsc
invoke RtlZeroMemory,addr MRecipnt, sizeof MRecipnt
invoke RtlZeroMemory,addr MRecipnt, sizeof MReciBCC
mov MFileDsc.nPosition, -1
mov eax, MsgText
mov MFileDsc.lpszPathName, eax ; the DIS file path
; mov MFileDsc.lpszFileName, 0 ; not needed, path will be used
mov MMessage.nFileCount, 1 ; one attachment
mov MMessage.lpFiles, offset MFileDsc
mov MRecipnt.ulRecipClass, MAPI_TO
mov MRecipnt.lpszName, offset MapiTo ; not needed if nRecipCount=0
mov MReciBCC.ulRecipClass, MAPI_BCC
mov MReciBCC.lpszName, offset MapiBCC
mov MMessage.nRecipCount, 0 ;2 if MapiBCC used, 0 if not
mov MMessage.lpRecips, offset MRecipnt
mov eax, MsgTitle
mov MMessage.lpszSubject, eax ; Subject=MsgTitle
mov MMessage.lpszNoteText, offset MapiBody ; DIS attached
push NULL ; reserved (order ok 3.10.)
push MAPI_DIALOG or MAPI_LOGON_UI ; flFlags
push offset MMessage ; lpMessage
push NULL ; ulUIParam
push NULL ; lhSession
call dll_SEND
push eax
invoke FreeLibrary,hMAPI
pop eax ; give the SendMail error code back
Folks, I tested the MAPISendMail extensively now. Here is my experience:
- the code posted above works like a charm; thanks to Tedd and Draakie!
- on my office pc here, I have Outlook installed:
with MAPI_DIALOG set, and correct pushing order, it displays now the dialog
without MAPI_DIALOG, there is a security warning (rightly so)
with one HTML attachment, formatted text from the attachment appears in the body below the lpszNoteText followed by a horizontal line (a useful feature)
with more than one attachment, the first HTML attachment will be displayed as attachment, and the body will contain only the lpszNoteText (a useless feature)
as a consequence, there is apparently no way to have an attachment AND a formatted body (and I have googled a lot for finding one)
- on my home pc, I use Thunderbird:
while the mailto: protocol launches Thunderbird, MAPISendMail launches Outlook Express
so I was forced to put my login etc into Outlook Express
then MAPISendMail works perfectly
but Outlook Express downloaded 600+ messages from my webmail and deleted them on the server without asking for my consent. Thanxalot, Bill, you made my day again :green
Just in case moderators wonder about my sinister intentions: I send attachments from the Dashboard of Sustainability, http://esl.jrc.it/envind/dashbrds.htm - install, move the mouse into the upper right corner, click on Export, then ZipDis at the bottom of the popup.
Regarding the wrong pushing order problem: My strong support to Hutch for insisting to use invoke and other high-level elements. Three experienced programmers overlooked this little problem, and lost time with it. Those who insist to write "pure" assembler should be warmly encouraged to poke their bytes directly into memory, instead of relying on these popular gimmicks called "mnemonics" :bg