none
Cannot Access Attachment Data From Embedded Attachment

    Question

  • I am writing a VB.NET windows forms application, I need to read the binary data in all attachments for a given message. I have a Microsoft.Office.Interop.Outlook.MailItem object that contains the mail Item with the attachments. I get an error when I try to save the attachment to a file using Attachment.SaveAsFile if the attachment is embedded.

     

    Dim Att as Microsoft.Office.Interop.Outlook.Attachment

    For Each Att in MyMailItem.Attachments

        Att.SaveAsFile("c:\test\" & Att.FileName)

    Next

     

    The aboce code seems to work fine for any file attached to the MailItem, However if I insert a picture in the e-mail body the Att.SaveAsFile returns the error System.Runtime.InteropServices.COMException (0x80004005): Cannot save the attachment. Cannot add the attachment; no data source was provided.
       at Microsoft.Office.Interop.Outlook.Attachment.SaveAsFile(String Path)

     

    Thursday, June 10, 2010 6:03 PM

Answers

  • I didn't notice you were displaying the Inspector modally. That's generally not recommended. 

    The best point in the process for saving a copy of the entire message is after the item has been transmitted to the outbound server and arrives in the Sent Items folder. Complete information about the sender and transmission time and account won't be available until then. You might consider combining your current application with an Outlook add-in that can watch the Sent Items folder with the Folder.Items.ItemAdd event handler, as transmission may not occur until sometime after the user clicks Send. 

    Thursday, June 10, 2010 8:50 PM
    Moderator

All replies

  • I can't duplicate the problem here. What's the value of Att.FileName for the attachment in question? Does it duplicate the name of a file already in the target location? 
    Thursday, June 10, 2010 6:22 PM
    Moderator
  • Sue,

    The target location is a empty directory so it cannot be a duplicate, the value of Att.FileName is image003.jpg,

    Here is the code I am using

    In a windows form command button click event

           Dim ol As New OutlookInterface

            ol.ToAddress = "test@test.com"
            ol.Subject = "Test Subject"
            ol.Body = "Test Body"
            ol.BodyIsHTML = False
            ol.ShowSendMailDialog()
            If Not ol.MailSent Then Exit Sub

    In a seperate class

    Imports System.Diagnostics
    Imports System.Linq
    Imports System.Reflection
    Imports System.Runtime.InteropServices
    Imports Outlook = Microsoft.Office.Interop.Outlook

    Public Class OutlookInterface
        Private Shared OutlookApplication As Outlook.Application
        Private WithEvents MyMailItem As Outlook.MailItem
        Public ToAddress As String
        Public FromAddress As String
        Public Subject As String
        Public Body As String
        Public BodyHTML As String
        Public BodyIsHTML As Boolean
        Public MailSent As Boolean = False

        Private Sub Mail_Send(ByRef Cancel As Boolean) Handles MyMailItem.Send

            Try
                ToAddress = MyMailItem.To
                Subject = MyMailItem.Subject
                Select Case MyMailItem.BodyFormat
                    Case Outlook.OlBodyFormat.olFormatPlain
                        BodyIsHTML = False
                    Case Outlook.OlBodyFormat.olFormatHTML
                        BodyIsHTML = True
                    Case Outlook.OlBodyFormat.olFormatRichText
                        BodyIsHTML = True
                    Case Outlook.OlBodyFormat.olFormatUnspecified
                        BodyIsHTML = False
                End Select
                Body = MyMailItem.Body
                BodyHTML = MyMailItem.HTMLBody
                Dim Att As Outlook.Attachment
                For Each Att In MyMailItem.Attachments
                    Debug.Print("Attachment Index=" & Att.Index)
                    Debug.Print("Attachment FileName=" & Att.FileName)
                    Debug.Print("Attachment DisplayName=" & Att.DisplayName)
                    Debug.Print("Attachment PathName=" & Att.PathName)
                    Try
                        Att.SaveAsFile("c:\test\" & Att.FileName)
                    Catch ex As Exception
                        MsgBox("Error Saving Attachment ex=" & ex.ToString)
                    End Try

                Next
                MailSent = True
                Debug.Print("Mail Sent!")
            Catch ex As Exception
                MsgBox("Outlook Interface Error @ Mail_Send Ex=" & ex.ToString )
            End Try

        End Sub

        Private Sub Mail_Close(ByRef Cancel As Boolean) Handles MyMailItem.Close
            Debug.Print("Mail Close!")
        End Sub

        Private Shared Function LogOnOutlook() As Boolean
            Try
                'Check if there is an Outlook process running.
                If Process.GetProcessesByName("OUTLOOK").Count() > 0 Then

                    ' If so, use the GetActiveObject method to obtain the process and cast it to an Application object.
                    OutlookApplication = DirectCast(Marshal.GetActiveObject("Outlook.Application"), Outlook.Application)
                Else

                    ' If not, create a new instance of Outlook and log on to the default profile.
                    OutlookApplication = New Outlook.Application()
                    Dim ns As Outlook.NameSpace = OutlookApplication.GetNamespace("MAPI")
                    ns.Logon("", "", Missing.Value, Missing.Value)
                    ns = Nothing
                End If

                ' Return the Outlook Application object.
                Return True
            Catch ex As Exception
                MsgBox("Outlook Interface Error @ LogOnOutlook ex=" & ex.ToString)
                Return False
            End Try

        End Function

        Public Function ShowSendMailDialog() As Boolean

            If OutlookApplication Is Nothing Then
                If Not LogOnOutlook() Then Return False
            End If

            Try
                ' Create a new MailItem and set the To, Subject and Body properties.
                MyMailItem = DirectCast(OutlookApplication.CreateItem(Outlook.OlItemType.olMailItem), Outlook.MailItem)
                MyMailItem.To = ToAddress
                MyMailItem.Subject = Subject
                If BodyIsHTML Then
                    MyMailItem.BodyFormat = Outlook.OlBodyFormat.olFormatHTML
                    MyMailItem.HTMLBody = BodyHTML
                Else
                    MyMailItem.BodyFormat = Outlook.OlBodyFormat.olFormatPlain
                    MyMailItem.Body = Body
                End If
                MyMailItem.Display(True)
                Return MailSent
            Catch ex As Exception
                MsgBox("Outlook Interface Error @ ShowSendMailDialog ex=" & ex.ToString )
                Return False
            End Try

        End Function


    End Class


    When you click on the windows form button, a new mail item window is shown, change the mail format to HTML then insert a picture into the body then click send

    Thursday, June 10, 2010 7:48 PM
  • The problem may be that you're messing with the attachments during the Send event. What exactly are you trying to accomplish? Not the programming logic, but the business or productivity goal? Knowing that may help us give you ideas on a more reliable approach. 
    Thursday, June 10, 2010 8:01 PM
    Moderator
  • Sue,

    The goal here is to allow the user to send a e-mail message from our application, then we want to save a copy of the entire message including attachments in our database so that we can later display the message using a WebBrowser control and allow the user to retrieve the attachments.

    The reason I am accessing the MailItem properties durring the Send Event is because after the MyMailItem.Display(True) returns, the mail item is no longer accessable and returns the error "The item has been noved or deleted."

    Thursday, June 10, 2010 8:23 PM
  • I didn't notice you were displaying the Inspector modally. That's generally not recommended. 

    The best point in the process for saving a copy of the entire message is after the item has been transmitted to the outbound server and arrives in the Sent Items folder. Complete information about the sender and transmission time and account won't be available until then. You might consider combining your current application with an Outlook add-in that can watch the Sent Items folder with the Folder.Items.ItemAdd event handler, as transmission may not occur until sometime after the user clicks Send. 

    Thursday, June 10, 2010 8:50 PM
    Moderator
  • Hi,

    I am writing to check the status of the issue on your side. Could you please let me know if the suggestion works for you or not? If you have any questions or concerns, please feel free to let me know. I will be more than happy to be of assistance.

     

    Tim Li

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com

     


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Monday, June 14, 2010 9:34 AM
  • Hi,

    I am facing the exact same problem in C#.

    My business requirement is that I need to encrypt my emails(any material/content/attachments etc.) before really sending to the destination. So having limitations, that I have to do the the work only at sending event time.

    Also facing another problem, although I searched a lot, but did not find any solution, that I could not detect the attachment type, either it is regular attachment or embeded one. all attachments give Type property as olByValue. Is there any good way provided to find actual attachment type.

    Looking for the solution or any workaround to get the task done.

    Friday, December 07, 2012 6:47 AM
  • ItemSend is the only event that suits your needs (beside providing your own button for sendign mail). Cancel sending, copy message, do your encryption and send your message. As for attachment type, you can check PrAttachFlags http://schemas.microsoft.com/mapi/proptag/0x37140003 and ContentId http://schemas.microsoft.com/mapi/proptag/0x3712001E using  PropertytAccessor
    Friday, December 07, 2012 8:19 AM
  • Thanks DamianD,

    Sure, I am working on ItemSend event, but the problem is that I could encrypt the regular attachment files smoothly but unable to encrypt embedded attachments due to following problems:

    1. I could not detect attachment type. (BTW the links you provided are seems not working. still in question?)
    2. Even if I detect somehow, these attachments are not physically exists, so how could I encrypt these because for encryption I need Stream object loaded with attachment data)

    Wednesday, December 12, 2012 5:43 AM
  • ad 1. those are not links, they are ids that you provide in PropertyAccessor.Getproperty method, look at http://msdn.microsoft.com/en-us/library/office/bb207521(v=office.12).aspx

    ad 2. if your plugin should encrypt _only_ attachments and leave mail body intact, then simply omit those embedded attachments, they are usually used for in-place pictures and such stuff, encrypting them would only break visual look of mail's body.

    Wednesday, December 12, 2012 7:16 AM
  • Thanks DamianD for quick support. I am getting following error, while trying attachment.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x37140003")

    The property "http://schemas.microsoft.com/mapi/proptag/0x37140003" is unknown or cannot be found.

    while other property return empty string for attached files.

    Secondly, I also need to encrypt the body content, simple html content is encrypted but embedded pictures are creating the problem. Yes rightly said that it is breaking the visual look of the mail's body but for targeted email it is compromised because security is top concern in my scenario.

    Thursday, December 13, 2012 11:53 AM
  • you have to be prepared to catch exceptions for Getproperty, this is how it is. As for second part - if you need to enrypt everything, why don't you use builtin functionality in outlook for this? Outlook allows to create digitally signed and/or encrypted mails, provided you have target person certificate with public key imported into contacts or few other means. Is that acceptable for you? if not, thenb you should try to mimic standards as much as possible which means grabbing whole mail as is (attachments, body, everthing) and storing it into single encrypted attachments which you can decrypt on other side. Depending on your skills in programming, you can do it from MAPI level (copying MAPI properties/streams from enccypted attachment) or you can simply same whole mail as msg, encrypt it, create empty new mail, add recipients and subject and send that encrypted msg as attachment and on the other side reverse whole process, adding msg file to inbox.

    Thursday, December 13, 2012 2:27 PM
  • Thanks a lot DamianD.

    OK, properties are seems working in my scenario (with catching exceptions), but I suggests there should be some better reliable method for this purpose by Microsoft.

    For some business requirements I have to follow my custom encryption methods and outlook built-in functionality does not fit in this scenario. Any how Your suggestion is quite practical that I have to mimic the standards and encrypt the whole message as single entity and then reverse the process for other side.

    Thanks again for your support.


    Monday, December 17, 2012 6:55 AM