none
Outlook 2010 - Removing and re-adding the attachments back in an RTF mail, makes all the attachments disappear! RRS feed

  • Question

  • Hi,

    We have a scenario where when we forward an incoming mail, containing around 80 doc attachments,  in RTFformat, the attachments are getting lost when we do some pre-processing on the documents. Below are the steps:

    1. Have a mail with around 80 doc attachments send in HTML format
    2. Forward the mail
    3. Have an Outlook plugin that does the following :
        a) Intercepts the Send
        b) Downloads all the attachments to TEMP (for some document tagging etc operations)
        c) Remove the existing attachments from MailItem
        d) Re-attach the downloaded (processed) attachments

    We are seeing, that whilst we are reattaching the documents, after around 46 docs, the attachment count gets reset to zero automatically and we are unable to add any further attachments. When the mail is finally sent, the count is still zero and no attachments are sent out.

    Below is the code in the Outlook plugin that we have:

    public void ProcessAttachments(Outlook.MailItem mailItem)
    {
        Dictionary<string, Outlook.Attachment> items = new Dictionary<string, Outlook.Attachment>();
        long count = mailItem.Attachments.Count;
    
        // download all the attachments to TEMP for local processing
        for (long lIndex = 1; lIndex <= count; lIndex++)
        {
            Outlook.Attachment attachment = mailItem.Attachments[lIndex];
            if (attachment.Type == Outlook.OlAttachmentType.olByValue) // i.e we can save to file.
            {
                string sPhysicalFile = Path.GetTempFileName();
                attachment.SaveAsFile(sPhysicalFile);
    
                string sFinalFile = Path.Combine(Path.GetDirectoryName(sPhysicalFile), attachment.DisplayName);
                File.Move(sPhysicalFile, sFinalFile);
    
                // -- any local processing of the downloaded file goes here --
    
                // we add the index as same file or similar filenames can be added multiple times
                items.Add(string.Format("{0}#{1}", sFinalFile, lIndex), attachment);
            }
        }
    
        // now replace them
        // Remove all the attachments first
        for (long index = count; index >= 0; index--)
        {
            mailItem.Attachments.Remove((int)index);
        }
        mailItem.Save();
    
        // Re-add them
        foreach (var item in items)
        {
            var oldAttachment = item.Value;
            var fileName = item.Key.Split(new char[] { '#' })[0];
    
            int position = (int)oldAttachment.Position + 1;
            mailItem.Attachments.Add(fileName, Outlook.OlAttachmentType.olByValue, position, oldAttachment.DisplayName);
            mailItem.Save(); // moving Save outside this loop does not helps either
        }
    }

    As seen in above code, when we are re-attaching the documents, the count automatically gets reset to zero after 46th attachment.

    Any inputs will be highly appreciated.

    Thanks
    Sandy


    Wednesday, November 14, 2012 12:37 PM

All replies

  • So is this HTML or RTF message?

    Is this against an Exchange store?

    Chances are you are opening too many objects and go over the RPC channel limit.

    Do not use multiple dot notation (such as mailItem.Attachments[lIndex] which is equivalent to mailItem.Attachments.Item(lIndex) or mailItem.Attachments.Add) and release all COM objects as soon as you are done with them in the loop using Marshal.ReleaseComObject().


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

    • Marked as answer by Quist ZhangModerator Friday, November 23, 2012 5:33 AM
    • Unmarked as answer by sppc42 Monday, November 26, 2012 5:12 PM
    Wednesday, November 14, 2012 5:09 PM
  • Its an RTF message that is failing working on an Exchange store.

    1. Any reasons why multiple dot notations should be a problem, and in particular, do you think that is the reason for this failure?

    2. Could you tell Which COM objects are you referring to in above code that need to be freed up?

    3. How can I avoid going above the RPC channel limit? I need to process that many attachments for the mail to be sent out.

    Please let me know your thoughts on the above.

    Thanks
    Sandy

    Monday, November 26, 2012 5:15 PM
  • 1. The fact that the problem manifests itself only after you process a fairy large number of objects is a strong indication of that.

    2. All of them. Each time you have a ".", a brand new COM object is returned. If yo uhave multiple dots, the compiler stores the intermediary in an implicit variable. You caannot access it to explicilty free it.

    Outlook.Attachments attachments = mailItem.Attachments;
    for (long lIndex = 1; lIndex <= count; lIndex++)
    {
            Outlook.Attachment attachment = attachments[lIndex];
            ...
            Marshal.ReleaseComObject(attachment);
    }
    Marshal.ReleaseComObject(attachments);

    3. Make sure you do not leave any objects alive for longer than absolutely necessary.


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

    Monday, November 26, 2012 5:29 PM
  • I made the recommended changes: not using double-dot notation, calling Marshal.ReleaseCOMObject for all objects and not keeping objects in memory for longer than required. But still, am still seeing the same issue for an RTFformat mail.

    Any more clues what might be going wrong here. The scenario is pretty straightforward here, as explained in the first post.

    Thanks
    Sandy

    Tuesday, November 27, 2012 3:02 PM
  • Hi,

    This relates to the previous forum query here.

    To summarize:

    1. Have an HTML formatted mail with 80+ attachments
    2. Fwd that mail and change the format to RTF
    3. Removing and re-adding the attachments back to the mail sets the attachment count to zero.

    So, is there any limit to number of attachments that can be added (on the fly during Item_Send) to a RTF based mail?

    Thanks
    Sandy

    Tuesday, December 4, 2012 2:00 PM
  • What is your latest code?

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

    Wednesday, December 5, 2012 2:46 PM
  • Hi Sandy,

    Thank you for posting in the MSDN Forum.

    I've tried to reproduce your scenario, but I've managed to send a mail with 90+ attachments in RTFormat.

    Could you please provide your latest code?

    I look forward to your reply.

    Best regards,


    Quist Zhang [MSFT]
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, December 6, 2012 8:24 AM
    Moderator
  • Hi Quist,

    The above posted code has the latest changes to reproduce the issue. Kindly note that it happens only on Outlook 2010. Below are the steps:

    1. Have an HTML formatted mail with 80+ attachments
    2. Fwd that mail and change the format to RTF
    3. Removing and re-adding the attachments back to the mail during send (in the plugin code above) sets the attachment count to zero.

    Thanks
    Sandy

    Tuesday, December 11, 2012 10:53 AM
  • I could reproduce this issue with a VSTOAddin, but dont know how to resolve this issue. Any help would be very much appreciated!

    All attachments are lost when forwarding a HTML mail as RTF with a large number of attachments (100). During the ‘ItemSend’ event an add-in ‘process’ the attachments are:

    - Saved as files
    - Deleted from the email
    - Add the files as new attachments

    The issue is only present when an HTML email is converted to RTF and processed by the add-in.

    Steps

    - Create a new HTML email
    - Add the all the files in ‘.\VSTOAddin\100 Test Documents’ as attachments. Use drag and drop since ‘Attach File’ seems to only add the first 58 files.
    - Add a recipient and send.
    - Open the item in you Sent Items folder
    - Select Forward
    - Convert to Rich Text (Format Text > Rich Text)
    - Ensure all 100 attachments appear in the RT body of the email.
    - Add a recipient and send.
    - Open the item from Sent Items
    - Error, the email does not contain any attachments!

    Observations

    - There are no exceptions thrown in the add-in.
    - All 100 attachments are added.
    - If we restrict the loop that adds the attachments back to only add the first 45 attachments then it works as expected, the sent email will have 45 attachments in the RT body. Increasing the number to 50 will result in all attachments being lost. The attachments count get set to 0 when adding an attachment somewhere between 45 and 50, in my testing this would be attachment 47.
    - In some cases where the attachments are lost the RT body is empty and in other cases the error text ’unloaded OLE object or mail attachment’ appears.
    - Issue reproduced in Outlook 2007 and 2010.

    VSTOAddin code

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml.Linq;
    using Outlook = Microsoft.Office.Interop.Outlook;
    using Office = Microsoft.Office.Core;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using System.Reflection;
    
    namespace VSTOAddin
    {
        #region VSTO add-in
    
        public partial class ThisAddIn
        {
            private void ThisAddIn_Startup(object sender, System.EventArgs e)
            {
                this.Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
            }
    
            void Application_ItemSend(object Item, ref bool Cancel)
            {
                Cancel = false;
                Outlook.Attachments attachments = null;
                Outlook.Attachment attachment = null;
                try
                {
                    var mailItem = Item as Outlook.MailItem;
                    if (mailItem.BodyFormat != Outlook.OlBodyFormat.olFormatRichText)
                    {
                        return;
                    }
    
                    using (var tmp = new TempFileManager())
                    {
                        // 1 - Save attachments to disk
                        attachments = mailItem.Attachments;
                        var count = attachments.Count;
                        for (long i = 1; i <= count; i++)
                        {
                            attachment = attachments[i];
                            if (attachment.Type == Outlook.OlAttachmentType.olByValue)
                            {
                                var tempFile = tmp.Add(attachment.DisplayName, attachment.Position);
                                System.Diagnostics.Trace.WriteLine(string.Format("Save attachment as file {0} {1} {2} {3}", i, attachment.DisplayName, attachment.Position, tempFile));
                                attachment.SaveAsFile(tempFile);
    
                            }
                            Marshal.ReleaseComObject(attachment);
                            attachment = null;
                        }
    
                        // 2 - Remove attachments
                        for (; count > 0; count--)
                        {
                            attachment = attachments[count];
                            if (attachment.Type == Outlook.OlAttachmentType.olByValue)
                            {
                                System.Diagnostics.Trace.WriteLine(string.Format("Delete attachment {0} {1} {2}", count, attachment.DisplayName, attachment.Position));
                                attachment.Delete();
                            }
                            Marshal.ReleaseComObject(attachment);
                            attachment = null;
                        }
                        mailItem.Save();
    
                        // 3 - Add attachments 
                        foreach (var tempFile in tmp.Get())
                        {
                            System.Diagnostics.Trace.WriteLine(string.Format("Add attachment {0} {1}", tempFile.DisplayName, tempFile.Position));
                            attachment = attachments.Add(tempFile.FileName, Outlook.OlAttachmentType.olByValue, tempFile.Position, tempFile.DisplayName);
                            Marshal.ReleaseComObject(attachment);
                            attachment = null;
                        }
                        mailItem.Save();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: " + ex.Message, "VSTOAddin", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
                    Cancel = true;
                }
                finally
                {
                    if (attachment != null)
                    {
                        Marshal.ReleaseComObject(attachment);
                        attachment = null;
                    }
    
                    if (attachments != null)
                    {
                        Marshal.ReleaseComObject(attachments);
                        attachments = null;
                    }
                }
            }
    
            private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
            {   
            }
    
            private void InternalStartup()
            {
                this.Startup += new System.EventHandler(ThisAddIn_Startup);
                this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
            }
        }
        #endregion
    
        #region Helper classes
        
        public class TempFile
        {
            public string DisplayName { get; set; }
            public string FileName { get; set; }
            public long Position { get; set; }
        }
        
        public class TempFileManager : IDisposable
        {
            private string _rootPath = String.Empty;
            private List<TempFile> _tempFiles = new List<TempFile>();
    
            public TempFileManager()
            {
                _rootPath = Path.Combine(Path.GetTempPath(), "VSTOAddin");
            }
    
            public void Dispose()
            {
                foreach (var tempFile in _tempFiles)
                {
                    File.Delete(tempFile.FileName);
                }
                Directory.Delete(_rootPath, true);
    
                GC.SuppressFinalize(this);
            }
    
            public List<TempFile> Get()
            {
                return _tempFiles;
            }
    
            public string Add(string displayName, long position)
            {
                var filename = GetFilename(displayName);
                _tempFiles.Add(new TempFile { DisplayName = displayName, 
                                              FileName = filename, 
                                              Position = position });
                return filename;
            }
    
            private string GetFilename(string displayName)
            {
                var file = Path.Combine(_rootPath, Path.GetRandomFileName());
                Directory.CreateDirectory(file);
                file = Path.Combine(file, displayName);
                if (File.Exists(file))
                {
                    File.Delete(file);
                }
                return file;
            }
        }
    
        #endregion
    }



    • Edited by Flymo75 Tuesday, January 29, 2013 6:05 PM
    • Proposed as answer by Patrick N. Welter Thursday, March 29, 2018 8:11 AM
    Tuesday, January 29, 2013 6:04 PM