none
Outlook Object Model / PIA: How to get an attachment of type olEmbeddedItem as anything useful? RRS feed

  • Question

  • We´re trying to create a send message handler in an Outlook plugin. If the user attached another mail to the current message, it is representet as an attachment of type olEmbeddedItem.

    It seems that the only thing we can do with that attachment is to save it as a ".msg" file, which is in Outlooks proprietary format. We´d prefer to get the contents of the attachment in the smtp/mime format the mail had while it was delivered over the (inter)net. Is there any way we can get the message in a useable format?

    (using MAPI is not an option - our plugin is written using .NET, and even if not, we could not rely on the fact that there is no other .NET plugin within the same process. MAPI and .NET within the same process are not supported, as it can cause heap corruption...)

    thanks,

    Markus

    Thursday, December 31, 2015 3:02 PM

Answers

  • Firstly, MAPI and .NET within the same process will NOT cause heap corruption... COM addins written in .Net run just fine within the outlook.exe address space.

    That being said, Outlook does not preserve the original MIME source, neither for the embedded message attachments, nor for the master messages. The best you can do is read the PR_TRANSPORT_MESSAGE_HEADERS property (DASL name http://schemas.microsoft.com/mapi/proptag/0x007D001F) using MailItem.PropertyAccessor.GetProperty. It will contain the MIME headers of the master message (but not the attachments) - take a look at a message with OutlookSpy - click IMessage button, select the PR_TRANSPORT_MESSAGE_HEADERS property. To look at the message attachment, go to the GetAttachmentTable tab, double click on the attachment, select the PR_ATTACH_DATA_OBJ property, right click, select IMAPIProp::OpenProperty, select IID_IMessage.


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    • Marked as answer by MarkusSchaber Sunday, January 3, 2016 5:09 PM
    Thursday, December 31, 2015 7:18 PM
  • Markus,

    specifically says that it's unsupported

    Yes, it is. That means if you face with any issue on the road nobody can help you. But in fact Extended MAPI works like a charm. Or you can use any wrappers around Extended MAPI such as Redemption. 

    Try using any low-level property explorers such as MFCMAPI or OutlookSpy to see what properties are set when you attach an Outlook item to the message. 

    • Marked as answer by MarkusSchaber Sunday, January 3, 2016 5:09 PM
    Thursday, December 31, 2015 6:16 PM

All replies

  • Hello Markus,

    The MSG file format is documented in the MSDN docs. See Outlook Item (.msg) File Format and .MSG File Format page for the details. So, you can use the GetProperty method of the PropertyAccessor class to get a value of the PR_ATTACH_DATA_BIN property (the DASL name is http://schemas.microsoft.com/mapi/proptag/0x37010102) which returns a byte array which represents the attached file. See Byte Stream from Mail Attachment MAPIOBJECT for the sample code. In case of  Extended MAPI you need to open the PR_ATTACH_DATA_BIN property as IStream.

    Note, you are free to use Extended MAPI in .Net applications and add-ins. At least nobody forbids doing so. See MAPI and .Net for clarifications. Or you may consider using any third-party wrappers that can be used safely in the managed code (for example, Redemption).

    Thursday, December 31, 2015 5:31 PM
  • Hi,

    Thanks to the pointers to the Msg format documentation, I'll see whether this is useful for us. (For specific reasons like signature verification etc, we need a byte stream which is as close to the original byte stream sent over the network as possible.)

    However, it seems I'll need to stay with the "SaveAs" method and a temporary file, as when calling the PropertyAccessor with the PR_ATTACH_DATA_BIN property, I get an exception:

    System.Runtime.InteropServices.COMException was caught
      HResult=-2147221233
      Message=The property "http://schemas.microsoft.com/mapi/proptag/0x37010102" is unknown or cannot be found.
      Source=Microsoft Outlook
      ErrorCode=-2147221233
      StackTrace:
           at Microsoft.Office.Interop.Outlook._PropertyAccessor.GetProperty(String SchemaName)
    [...]

    (Getting the byte array this way works fine for "normal" attachments, which have the type olByValue, but not for attached messages with the type olEmbeddedItem. I guess that Outlook creates the binary representation at a later time, after the ItemSend event is finished.)

    I'm still skeptical of using the MAPI APIs within our Code, as the "Mapi and .NET" article you refer to specifically says that it's unsupported, and the "Byte Stream from Mail Attachment MAPOIBJECT" article you point out says: "The MAPIOBJECT property is only useful to you if you are using Extended MAPI (not supported for use in managed code)"


    Thursday, December 31, 2015 5:48 PM
  • Markus,

    specifically says that it's unsupported

    Yes, it is. That means if you face with any issue on the road nobody can help you. But in fact Extended MAPI works like a charm. Or you can use any wrappers around Extended MAPI such as Redemption. 

    Try using any low-level property explorers such as MFCMAPI or OutlookSpy to see what properties are set when you attach an Outlook item to the message. 

    • Marked as answer by MarkusSchaber Sunday, January 3, 2016 5:09 PM
    Thursday, December 31, 2015 6:16 PM
  • Firstly, MAPI and .NET within the same process will NOT cause heap corruption... COM addins written in .Net run just fine within the outlook.exe address space.

    That being said, Outlook does not preserve the original MIME source, neither for the embedded message attachments, nor for the master messages. The best you can do is read the PR_TRANSPORT_MESSAGE_HEADERS property (DASL name http://schemas.microsoft.com/mapi/proptag/0x007D001F) using MailItem.PropertyAccessor.GetProperty. It will contain the MIME headers of the master message (but not the attachments) - take a look at a message with OutlookSpy - click IMessage button, select the PR_TRANSPORT_MESSAGE_HEADERS property. To look at the message attachment, go to the GetAttachmentTable tab, double click on the attachment, select the PR_ATTACH_DATA_OBJ property, right click, select IMAPIProp::OpenProperty, select IID_IMessage.


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    • Marked as answer by MarkusSchaber Sunday, January 3, 2016 5:09 PM
    Thursday, December 31, 2015 7:18 PM
  • So, that effectively means the original message is irreversibly destroyed at some point in time, and when the message is attached to another message, it is impossible to recover it? And even trying to come close to it requires a lot of work and fiddling? I'm somehow a bit disappointed...

    So, to get back to the other question, is there any way to get to the attached message as an LPMessage without saving it to Disk using the SaveAs method? (Thus, we could use the IConverterSession to create an EML, or at least, the closest possible approximation...)


    Friday, January 1, 2016 12:48 PM
  • OutlookSpy shows me that the Property PR_ATTACH_DATA_OBJ is present. However, when I try to access it via OOM, I get the following exception:

    System.Runtime.InteropServices.COMException was caught
      HResult=-2147221246
      Message=Object does not support property "http://schemas.microsoft.com/mapi/proptag/0x3701000D".
      Source=Microsoft Outlook
      ErrorCode=-2147221246
      StackTrace:
           at Microsoft.Office.Interop.Outlook._PropertyAccessor.GetProperty(String SchemaName)
          [...]

    That is a different exception than the one for _BIN above. So my guess is that I could use the MAPIOBJECT property of the Attachment to somehow get the MAPI object, and then read the property using this object, to obtain the LPMEssage for the IConverterSession. I'll try to go down that way...

    Thanks,

    Markus

    Friday, January 1, 2016 2:05 PM
  • I tried, but I cannot get the call to OpenProperty to succeed, getting a Binding Exception. It seems "dynamic" will not work here, and I don't have a fixed interface as no interop assembly for MAPI exists.

    Now, I'm stuck again. :-(

    Friday, January 1, 2016 4:51 PM
  • MIME is not a native Outlook format, so Outlook is under no obligation to preserve it. If a message is sent between 2 Exchange mailboxes, the message is never ever converted to MIME.

    OOM will not let you open the embedded message attachment as a message, you'd need Extended MAPI (C++ or Delphi) or Redemption for that - it exposes EmbeddedMsg property on its attachment objects. It also exposes SaveAs method that (among other formats) will let you save in the MIME format:

    set Session = CreateObject("Redemption.RDOSession")
    Session.MAPIOBJECT = Application.Session.MAPIOBJECT
    set OutlookItem = Application.ActiveExplorer.Selection(1)
    set RedemptionMsg = Session.GetRDOObjectFromOutlookObject(OutlookItem)
    for each attach in RedemptionMsg.Attachments
      if attach.Type = olEmbeddedItem Then
        set embMsg = attach.EmbeddedMsg
        embMsg.SaveAs "c:\temp\" & attach.FileName & ".eml", 1024 'olRfc822
      End If
    next


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Friday, January 1, 2016 7:13 PM
  • PropertyAccessor in OOM will not let you open PT_OBJECT properties.

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Friday, January 1, 2016 7:14 PM
  • I'm staying with the temporary file and using Applicaiton.Session.OpenSharedItem now - it works when the file actually has a .msg suffix, but it throws an exception when the file has a .tmp suffix, which is the reason I did not further consider it after my first failed experiment.
    Friday, January 1, 2016 7:29 PM
  • Also you may consider using the Process.Run method which allows to run the associated application (Outlook in case of .msg files) and open the passed file. Note, Outlook is a singleton, so the message will be opened in the same Outlook instance (if any).

    Saving attached messages on the disk is the only stable way.    

    Friday, January 1, 2016 8:17 PM