none
Memory leaks in Range e.g. set Font when repeating the process RRS feed

  • Question

  • Correct me if I am wrong, but there seems to be memory leaks when using Range and it's properties. For instance, add reference to Microsoft.Office.Interop.Word and consider the following (create an empty docx at c:\test.docx):

                try
                {
                    var file = @"c:\test.docx";
                    var wordApp = new ApplicationClass { Visible = true };
                    object readOnly = false;
                    object missing = System.Reflection.Missing.Value;
                    object document = file;

                    var oDoc = wordApp.Documents.Open(ref document,
                                                     ref missing,
                                                     ref readOnly,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing);

                    oDoc.Activate();
                    const int t = 1000;
                    for (var i = 0; i < t; i++)
                    {
                        var rng = oDoc.Content;
                        rng.Text = string.Format("We are now merging {0} out of {1} document", i, t);
                        rng.Font.Name = "Arial";
                        rng.Font.Size = 11;

                        Marshal.ReleaseComObject(rng);
                        rng = null;
                    }
                    Console.WriteLine("Press any key...");
                    Console.ReadKey();
                    oDoc.Close();
                    wordApp.Quit();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    Console.ReadKey();
                }

    This simple iteration causes Word to increase quite heavily in memory and finally cause out of memory exception in my addin, and I have isolated the problem down to setting Ranges to various settings  e.g. font, size and so on as seen in the example above.

    Could anyone please either confirm that there is no way of getting around this problem, or point me in the right direction as I'm more or less running out of ideas.

    Monday, February 11, 2013 1:04 PM

Answers

  • Hi Andreas

    Word maintains "scratch" and "temp" files tracking actions in documents being edited. One purpose is to maintain the Undo/Redo list. If you're repeatedly applying "direct formatting" to ranges of text, this can overtax the capacity of this functionality. It's not a memory leak.

    If you need to repeatedly apply the same combination of formatting, it's better to create a style, then apply that style. Your code will be faster and you're less likely to hit this restriction.

    However, it is possible to work around the restriction by issuing the SAVE command for the document as well as UndoClear at regular intervals. This will usually let your code continue to run.

    If you still encounter problems, it's possible that the test machine has a damaged Normal.dotm template or that the document you're processing is damaged structurally.

    Please also note that working directly with the Open XML file format, rather than the "Interop", might be the more efficient process to accomplish your goal and certainly will not cause the issue you're experiencing.


    Cindy Meister, VSTO/Word MVP, my blog

    Wednesday, February 13, 2013 7:53 AM
    Moderator

All replies

  • Hi AndreasMakitalo,

    Thanks for posting in the MSDN Forum.

    I don't think there exists memory leak. Please confirm this sentence which I colored red:

                    var oDoc = wordApp.Documents.Open(ref document,
                                                     ref missing,
                                                     ref readOnly,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing,
                                                     ref missing);

    Can you ensure you haven't access Null value for the oDoc variable?

    As far as I known, it is null if you use normal setting for your DCOM. Please follow this reference to change your setting: http://stackoverflow.com/questions/10837437/interop-word-documents-open-is-null and try it again.

    Have a good day,

    Tom


    Tom Xu [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.

    Tuesday, February 12, 2013 8:33 AM
    Moderator
  • Thx for reply, opening the document works like a charm and everything else, it's when I run this loop:

                    for (var i = 0; i < t; i++)
                    {
                        var rng = oDoc.Content;
                        rng.Text = string.Format("We are now merging {0} out of {1} document", i, t);
                        rng.Font.Name = "Arial";
                        rng.Font.Size = 11;
    
                        Marshal.ReleaseComObject(rng);
                        rng = null;
                    }

    that I have an "heavy" increase in memory consumption by Word.exe as soon as I start setting properties of the Range(s) repeatedly.

    This memory consumption increases with the total number of different properties I set at each iteration, and thus causes finally Word to throw an exception.

    Tuesday, February 12, 2013 9:59 AM
  • Hi AndreasMakitalo,

    How to measure the "heavy"? would your please tell me the value of the "t" variable? I tried 1000 as you mentioned in original post. It works fine on my side.

    Have a good day,

    Tom


    Tom Xu [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.

    Wednesday, February 13, 2013 3:16 AM
    Moderator
  • Hi Andreas

    Word maintains "scratch" and "temp" files tracking actions in documents being edited. One purpose is to maintain the Undo/Redo list. If you're repeatedly applying "direct formatting" to ranges of text, this can overtax the capacity of this functionality. It's not a memory leak.

    If you need to repeatedly apply the same combination of formatting, it's better to create a style, then apply that style. Your code will be faster and you're less likely to hit this restriction.

    However, it is possible to work around the restriction by issuing the SAVE command for the document as well as UndoClear at regular intervals. This will usually let your code continue to run.

    If you still encounter problems, it's possible that the test machine has a damaged Normal.dotm template or that the document you're processing is damaged structurally.

    Please also note that working directly with the Open XML file format, rather than the "Interop", might be the more efficient process to accomplish your goal and certainly will not cause the issue you're experiencing.


    Cindy Meister, VSTO/Word MVP, my blog

    Wednesday, February 13, 2013 7:53 AM
    Moderator
  • Thanks Cindy and Tom, it is enough to simply open task manager and observe Word.exe process while running and simply notice that it only grows and grows in memory size. Memory profiling in this case doesn't give much more info than allocation by unmanaged code. I created this small example to demonstrate the issue we are experiencing as the total source is much more complex .. but problem is isolated to this tiny behavior. 

    Cindy, I totally agree with u that using Open XML would be the way to go - the background here is a simple addin that started off as something small doing minor mail merge operations, and of course as everything else popular, it grows during the years with new and more complex functionality - and at this point there's no way of re-designing the solution.

    I'll test your suggestions and get back at you during tomorrow, thx in advance!

    Wednesday, February 13, 2013 12:22 PM
  • Changes have been implemented with very promising result, using styles indeed did the trick and calling UndoClear every once in a while.

    Only 1 small limitation has been spotted, and thats - for instance - using different fonts/sizes/color on different columns, but this limitation have been adressed to customer and hopefully will be approved and accepted.

    Thanks for all the help - I believe that this is the best we can achieve at this point and with this type of solution, I'll mark u'r response as answer.

    Thursday, February 14, 2013 10:40 AM
  •                 for (var i = 0; i < t; i++)
                    {
                        var rng = oDoc.Content;
                        rng.Text = string.Format("We are now merging {0} out of {1} document", i, t);
                        rng.Font.Name = "Arial";
                        rng.Font.Size = 11;
    
                        Marshal.ReleaseComObject(rng);
                        rng = null;
                    }

    I think your problem is occurring when you are accessing the .Font instance.

    Try

        var fnt = rng.Font;

        fnt.Name = "Arial";

        fnt.Size = 11;

        Marshal.RealseComObject(fnt);

    If you were to do this (only as a test):

    var fnt = rng.Font;

    Console.WriteLine(Marshal.IUnknownFromObject(fnt);

    fnt = rng.Font;

    Console.WriteLine(Marshal.IUnknownFromObject(fnt);

    You will see that they are two different pointers, aka two different instances.

    (Do the same with say a Document instance and the pointers are always the same)

    Thus your code is leaking two font references on every iteration.

    Hope this helps

    Neal


    • Edited by N. A. _ Thursday, February 14, 2013 4:34 PM
    Thursday, February 14, 2013 4:32 PM