locked
How do I bring up the user's email program, with an attachment attached? RRS feed

  • Question

  • I notice that Microsoft Word has a "save and send" feature, where you can save your document and simultaneously open up a new email message in the user's email program, with the document as an attachment to the email.

    How would I do that in vb.net?

    Thanks

    Friday, June 5, 2015 2:20 PM

Answers

  • Keep in mind that a huge number of people now use web based email clients like gmail/outlook/yahoo, etc..

    However if your code will be running in an environment where you know they will have a local client program for email, like outlook, then you can invoke the MAPI32.DLL.

    Here is a class I use to do this:

    Imports System
    Imports System.Runtime.InteropServices
    Imports System.IO
    Imports System.Collections.Generic
    Imports System.Windows.Forms
    
    Public Class MAPI
        Public Function AddRecipientTo(ByVal email As String) As Boolean
            Return AddRecipient(email, howTo.MAPI_TO)
        End Function
    
        Public Function AddRecipientCC(ByVal email As String) As Boolean
            Return AddRecipient(email, howTo.MAPI_TO)
        End Function
    
        Public Function AddRecipientBCC(ByVal email As String) As Boolean
            Return AddRecipient(email, howTo.MAPI_TO)
        End Function
    
        Public Sub AddAttachment(ByVal strAttachmentFileName As String)
            m_attachments.Add(strAttachmentFileName)
        End Sub
    
        Public Function SendMailPopup(ByVal strSubject As String, ByVal strBody As String) As Integer
            Return SendMail(strSubject, strBody, MAPI_LOGON_UI Or MAPI_DIALOG)
        End Function
    
        Public Function SendMailDirect(ByVal strSubject As String, ByVal strBody As String) As Integer
            Return SendMail(strSubject, strBody, MAPI_LOGON_UI)
        End Function
    
    
        <DllImport("MAPI32.DLL")> _
         Private Shared Function MAPISendMail(ByVal sess As IntPtr, ByVal hwnd As IntPtr, ByVal message As MapiMessage, ByVal flg As Integer, ByVal rsv As Integer) As Integer
        End Function
    
        Private Function SendMail(ByVal strSubject As String, ByVal strBody As String, ByVal how As Integer) As Integer
            Dim msg As MapiMessage = New MapiMessage()
            msg.subject = strSubject
            msg.noteText = strBody
    
            msg.recips = GetRecipients(msg.recipCount)
            msg.files = GetAttachments(msg.fileCount)
    
            m_lastError = MAPISendMail(New IntPtr(0), New IntPtr(0), msg, how, 0)
            If m_lastError > 1 Then
                'YOU CAN DISPLAY ERROR MESSAGE HERE IF DESIRED
                'MessageBox.Show("MAPISendMail failed! " + GetLastError(), "MAPISendMail")
            End If
    
            Cleanup(msg)
            Return m_lastError
        End Function
    
        Private Function AddRecipient(ByVal email As String, ByVal howTo As howTo) As Boolean
            Dim recipient As MapiRecipDesc = New MapiRecipDesc()
    
            recipient.recipClass = CType(howTo, Integer)
            recipient.name = email
            m_recipients.Add(recipient)
    
            Return True
        End Function
    
        Private Function GetRecipients(ByRef recipCount As Integer) As IntPtr
            recipCount = 0
            If m_recipients.Count = 0 Then
                Return IntPtr.Zero
            End If
    
            Dim size As Integer = Marshal.SizeOf(GetType(MapiRecipDesc))
            Dim myIntPtr As IntPtr = Marshal.AllocHGlobal(m_recipients.Count * size)
    
            Dim ptr As Integer = CType(myIntPtr, Integer)
            Dim mapiDesc As MapiRecipDesc
            For Each mapiDesc In m_recipients
                Marshal.StructureToPtr(mapiDesc, CType(ptr, IntPtr), False)
                ptr += size
            Next
    
            recipCount = m_recipients.Count
            Return myIntPtr
        End Function
    
        Private Function GetAttachments(ByRef fileCount As Integer) As IntPtr
            fileCount = 0
            If m_attachments Is Nothing Then
                Return IntPtr.Zero
            End If
    
            If (m_attachments.Count <= 0) Or (m_attachments.Count > maxAttachments) Then
                Return IntPtr.Zero
            End If
    
            Dim size As Integer = Marshal.SizeOf(GetType(MapiFileDesc))
            Dim myIntPtr As IntPtr = Marshal.AllocHGlobal(m_attachments.Count * size)
    
            Dim mapiFileDesc As MapiFileDesc = New MapiFileDesc()
            mapiFileDesc.position = -1
            Dim ptr As Integer = CType(myIntPtr, Integer)
    
            Dim strAttachment As String
            For Each strAttachment In m_attachments
                mapiFileDesc.name = Path.GetFileName(strAttachment)
                mapiFileDesc.path = strAttachment
                Marshal.StructureToPtr(mapiFileDesc, CType(ptr, IntPtr), False)
                ptr += size
            Next
    
            fileCount = m_attachments.Count
            Return myIntPtr
        End Function
    
        Private Sub Cleanup(ByRef msg As MapiMessage)
            Dim size As Integer = Marshal.SizeOf(GetType(MapiRecipDesc))
            Dim ptr As Integer = 0
    
            If msg.recips <> IntPtr.Zero Then
                ptr = CType(msg.recips, Integer)
                Dim i As Integer
                For i = 0 To msg.recipCount - 1 Step i + 1
                    Marshal.DestroyStructure(CType(ptr, IntPtr), GetType(MapiRecipDesc))
                    ptr += size
                Next
                Marshal.FreeHGlobal(msg.recips)
            End If
    
            If msg.files <> IntPtr.Zero Then
                size = Marshal.SizeOf(GetType(MapiFileDesc))
    
                ptr = CType(msg.files, Integer)
                Dim i As Integer
                For i = 0 To msg.fileCount - 1 Step i + 1
                    Marshal.DestroyStructure(CType(ptr, IntPtr), GetType(MapiFileDesc))
                    ptr += size
                Next
                Marshal.FreeHGlobal(msg.files)
            End If
    
            m_recipients.Clear()
            m_attachments.Clear()
            m_lastError = 0
        End Sub
    
        Public Function GetLastError() As String
            If m_lastError <= 26 Then
                Return errors(m_lastError)
            End If
            Return "MAPI error [" + m_lastError.ToString() + "]"
        End Function
    
        ReadOnly errors() As String = New String() {"OK [0]", "User abort [1]", "General MAPI failure [2]", "MAPI login failure [3]", "Disk full [4]", "Insufficient memory [5]", "Access denied [6]", "-unknown- [7]", "Too many sessions [8]", "Too many files were specified [9]", "Too many recipients were specified [10]", "A specified attachment was not found [11]", "Attachment open failure [12]", "Attachment write failure [13]", "Unknown recipient [14]", "Bad recipient type [15]", "No messages [16]", "Invalid message [17]", "Text too large [18]", "Invalid session [19]", "Type not supported [20]", "A recipient was specified ambiguously [21]", "Message in use [22]", "Network failure [23]", "Invalid edit fields [24]", "Invalid recipients [25]", "Not supported [26]"}
    
        Dim m_recipients As New List(Of MapiRecipDesc)
        Dim m_attachments As New List(Of String)
        Dim m_lastError As Integer = 0
    
        Private Const MAPI_LOGON_UI As Integer = &H1
        Private Const MAPI_DIALOG As Integer = &H8
        Private Const maxAttachments As Integer = 20
    
        Enum howTo
            MAPI_ORIG = 0
            MAPI_TO
            MAPI_CC
            MAPI_BCC
        End Enum
    
    End Class
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Class MapiMessage
        Public reserved As Integer
        Public subject As String
        Public noteText As String
        Public messageType As String
        Public dateReceived As String
        Public conversationID As String
        Public flags As Integer
        Public originator As IntPtr
        Public recipCount As Integer
        Public recips As IntPtr
        Public fileCount As Integer
        Public files As IntPtr
    End Class
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Class MapiFileDesc
        Public reserved As Integer
        Public flags As Integer
        Public position As Integer
        Public path As String
        Public name As String
        Public type As IntPtr
    End Class
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Class MapiRecipDesc
        Public reserved As Integer
        Public recipClass As Integer
        Public name As String
        Public address As String
        Public eIDSize As Integer
        Public enTryID As IntPtr
    End Class
    
    

    Usage:

    'CREATE NEW EMAIL WITH ATTACHMENT OBJECT
    Dim myEmail As New MAPI
    'ATTACH FILE
    myEmail.AddAttachment("C:\somefile.pdf")
    'CREATE EMAIL POPUP
    If myEmail.SendMailPopup("Email Subject", "Email Body.") <> 0 Then
        MessageBox.Show("Could not create email for attachment. You may not have an email client configured on your computer, or the email client you use may not be compatible. Try saving the file to disk, and attaching the file to your email manually.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End If


    Matt Kleinwaks - MSMVP MSDN Forums Moderator - www.zerosandtheone.com

    • Marked as answer by malikmeister Friday, June 5, 2015 4:02 PM
    • Unmarked as answer by malikmeister Saturday, June 6, 2015 10:22 AM
    • Marked as answer by malikmeister Saturday, June 6, 2015 9:01 PM
    Friday, June 5, 2015 2:37 PM
  • What is your default EMail client ?

    The code that kleinma posted works fine here, takes a few seconds and brings up a new Outlook EMail with the chosen file attached.

    • Marked as answer by malikmeister Saturday, June 6, 2015 9:01 PM
    Saturday, June 6, 2015 2:55 PM

All replies

  • Keep in mind that a huge number of people now use web based email clients like gmail/outlook/yahoo, etc..

    However if your code will be running in an environment where you know they will have a local client program for email, like outlook, then you can invoke the MAPI32.DLL.

    Here is a class I use to do this:

    Imports System
    Imports System.Runtime.InteropServices
    Imports System.IO
    Imports System.Collections.Generic
    Imports System.Windows.Forms
    
    Public Class MAPI
        Public Function AddRecipientTo(ByVal email As String) As Boolean
            Return AddRecipient(email, howTo.MAPI_TO)
        End Function
    
        Public Function AddRecipientCC(ByVal email As String) As Boolean
            Return AddRecipient(email, howTo.MAPI_TO)
        End Function
    
        Public Function AddRecipientBCC(ByVal email As String) As Boolean
            Return AddRecipient(email, howTo.MAPI_TO)
        End Function
    
        Public Sub AddAttachment(ByVal strAttachmentFileName As String)
            m_attachments.Add(strAttachmentFileName)
        End Sub
    
        Public Function SendMailPopup(ByVal strSubject As String, ByVal strBody As String) As Integer
            Return SendMail(strSubject, strBody, MAPI_LOGON_UI Or MAPI_DIALOG)
        End Function
    
        Public Function SendMailDirect(ByVal strSubject As String, ByVal strBody As String) As Integer
            Return SendMail(strSubject, strBody, MAPI_LOGON_UI)
        End Function
    
    
        <DllImport("MAPI32.DLL")> _
         Private Shared Function MAPISendMail(ByVal sess As IntPtr, ByVal hwnd As IntPtr, ByVal message As MapiMessage, ByVal flg As Integer, ByVal rsv As Integer) As Integer
        End Function
    
        Private Function SendMail(ByVal strSubject As String, ByVal strBody As String, ByVal how As Integer) As Integer
            Dim msg As MapiMessage = New MapiMessage()
            msg.subject = strSubject
            msg.noteText = strBody
    
            msg.recips = GetRecipients(msg.recipCount)
            msg.files = GetAttachments(msg.fileCount)
    
            m_lastError = MAPISendMail(New IntPtr(0), New IntPtr(0), msg, how, 0)
            If m_lastError > 1 Then
                'YOU CAN DISPLAY ERROR MESSAGE HERE IF DESIRED
                'MessageBox.Show("MAPISendMail failed! " + GetLastError(), "MAPISendMail")
            End If
    
            Cleanup(msg)
            Return m_lastError
        End Function
    
        Private Function AddRecipient(ByVal email As String, ByVal howTo As howTo) As Boolean
            Dim recipient As MapiRecipDesc = New MapiRecipDesc()
    
            recipient.recipClass = CType(howTo, Integer)
            recipient.name = email
            m_recipients.Add(recipient)
    
            Return True
        End Function
    
        Private Function GetRecipients(ByRef recipCount As Integer) As IntPtr
            recipCount = 0
            If m_recipients.Count = 0 Then
                Return IntPtr.Zero
            End If
    
            Dim size As Integer = Marshal.SizeOf(GetType(MapiRecipDesc))
            Dim myIntPtr As IntPtr = Marshal.AllocHGlobal(m_recipients.Count * size)
    
            Dim ptr As Integer = CType(myIntPtr, Integer)
            Dim mapiDesc As MapiRecipDesc
            For Each mapiDesc In m_recipients
                Marshal.StructureToPtr(mapiDesc, CType(ptr, IntPtr), False)
                ptr += size
            Next
    
            recipCount = m_recipients.Count
            Return myIntPtr
        End Function
    
        Private Function GetAttachments(ByRef fileCount As Integer) As IntPtr
            fileCount = 0
            If m_attachments Is Nothing Then
                Return IntPtr.Zero
            End If
    
            If (m_attachments.Count <= 0) Or (m_attachments.Count > maxAttachments) Then
                Return IntPtr.Zero
            End If
    
            Dim size As Integer = Marshal.SizeOf(GetType(MapiFileDesc))
            Dim myIntPtr As IntPtr = Marshal.AllocHGlobal(m_attachments.Count * size)
    
            Dim mapiFileDesc As MapiFileDesc = New MapiFileDesc()
            mapiFileDesc.position = -1
            Dim ptr As Integer = CType(myIntPtr, Integer)
    
            Dim strAttachment As String
            For Each strAttachment In m_attachments
                mapiFileDesc.name = Path.GetFileName(strAttachment)
                mapiFileDesc.path = strAttachment
                Marshal.StructureToPtr(mapiFileDesc, CType(ptr, IntPtr), False)
                ptr += size
            Next
    
            fileCount = m_attachments.Count
            Return myIntPtr
        End Function
    
        Private Sub Cleanup(ByRef msg As MapiMessage)
            Dim size As Integer = Marshal.SizeOf(GetType(MapiRecipDesc))
            Dim ptr As Integer = 0
    
            If msg.recips <> IntPtr.Zero Then
                ptr = CType(msg.recips, Integer)
                Dim i As Integer
                For i = 0 To msg.recipCount - 1 Step i + 1
                    Marshal.DestroyStructure(CType(ptr, IntPtr), GetType(MapiRecipDesc))
                    ptr += size
                Next
                Marshal.FreeHGlobal(msg.recips)
            End If
    
            If msg.files <> IntPtr.Zero Then
                size = Marshal.SizeOf(GetType(MapiFileDesc))
    
                ptr = CType(msg.files, Integer)
                Dim i As Integer
                For i = 0 To msg.fileCount - 1 Step i + 1
                    Marshal.DestroyStructure(CType(ptr, IntPtr), GetType(MapiFileDesc))
                    ptr += size
                Next
                Marshal.FreeHGlobal(msg.files)
            End If
    
            m_recipients.Clear()
            m_attachments.Clear()
            m_lastError = 0
        End Sub
    
        Public Function GetLastError() As String
            If m_lastError <= 26 Then
                Return errors(m_lastError)
            End If
            Return "MAPI error [" + m_lastError.ToString() + "]"
        End Function
    
        ReadOnly errors() As String = New String() {"OK [0]", "User abort [1]", "General MAPI failure [2]", "MAPI login failure [3]", "Disk full [4]", "Insufficient memory [5]", "Access denied [6]", "-unknown- [7]", "Too many sessions [8]", "Too many files were specified [9]", "Too many recipients were specified [10]", "A specified attachment was not found [11]", "Attachment open failure [12]", "Attachment write failure [13]", "Unknown recipient [14]", "Bad recipient type [15]", "No messages [16]", "Invalid message [17]", "Text too large [18]", "Invalid session [19]", "Type not supported [20]", "A recipient was specified ambiguously [21]", "Message in use [22]", "Network failure [23]", "Invalid edit fields [24]", "Invalid recipients [25]", "Not supported [26]"}
    
        Dim m_recipients As New List(Of MapiRecipDesc)
        Dim m_attachments As New List(Of String)
        Dim m_lastError As Integer = 0
    
        Private Const MAPI_LOGON_UI As Integer = &H1
        Private Const MAPI_DIALOG As Integer = &H8
        Private Const maxAttachments As Integer = 20
    
        Enum howTo
            MAPI_ORIG = 0
            MAPI_TO
            MAPI_CC
            MAPI_BCC
        End Enum
    
    End Class
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Class MapiMessage
        Public reserved As Integer
        Public subject As String
        Public noteText As String
        Public messageType As String
        Public dateReceived As String
        Public conversationID As String
        Public flags As Integer
        Public originator As IntPtr
        Public recipCount As Integer
        Public recips As IntPtr
        Public fileCount As Integer
        Public files As IntPtr
    End Class
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Class MapiFileDesc
        Public reserved As Integer
        Public flags As Integer
        Public position As Integer
        Public path As String
        Public name As String
        Public type As IntPtr
    End Class
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Class MapiRecipDesc
        Public reserved As Integer
        Public recipClass As Integer
        Public name As String
        Public address As String
        Public eIDSize As Integer
        Public enTryID As IntPtr
    End Class
    
    

    Usage:

    'CREATE NEW EMAIL WITH ATTACHMENT OBJECT
    Dim myEmail As New MAPI
    'ATTACH FILE
    myEmail.AddAttachment("C:\somefile.pdf")
    'CREATE EMAIL POPUP
    If myEmail.SendMailPopup("Email Subject", "Email Body.") <> 0 Then
        MessageBox.Show("Could not create email for attachment. You may not have an email client configured on your computer, or the email client you use may not be compatible. Try saving the file to disk, and attaching the file to your email manually.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End If


    Matt Kleinwaks - MSMVP MSDN Forums Moderator - www.zerosandtheone.com

    • Marked as answer by malikmeister Friday, June 5, 2015 4:02 PM
    • Unmarked as answer by malikmeister Saturday, June 6, 2015 10:22 AM
    • Marked as answer by malikmeister Saturday, June 6, 2015 9:01 PM
    Friday, June 5, 2015 2:37 PM
  • I tried your code, and it gave an overflow at the following statement:

      Dim ptr As Integer = CType(myIntPtr, Integer)

    Why would that happen?


    Saturday, June 6, 2015 10:21 AM
  • What is your default EMail client ?

    The code that kleinma posted works fine here, takes a few seconds and brings up a new Outlook EMail with the chosen file attached.

    • Marked as answer by malikmeister Saturday, June 6, 2015 9:01 PM
    Saturday, June 6, 2015 2:55 PM
  • A little bit to make Devon's message more strict, 

    MS Office Outlook Email, there are many programs which have that name Outlook Email.


    Success
    Cor

    Saturday, June 6, 2015 3:35 PM
  • Oh, I have windows live mail, that was the problem.
    Saturday, June 6, 2015 9:01 PM