0

I'm writting a VSTO which loops through all slides, through all shapes and sets the title to a value. I recognized that the memory consuption is going up after each run. So therefore I minimized my code and let it run a 100 time which ends up allocating about 20MB Memory for every 100 runs.

My code is executed from a sidebar-button, the presentation has about 30 slides with titles. My code looks like this for the button:

        private void button1_Click(object sender, EventArgs e)
    {
        SetTitle_Direct();

        Stopwatch watch = new Stopwatch();
        watch.Start();

        SetTitle_Direct();

        watch.Stop();
        //MessageBox.Show("Time spend: " + watch.Elapsed);

        AMRefreshProgress.Maximum = 100;
        AMRefreshProgress.Step = 1;
        AMRefreshProgress.UseWaitCursor = true;
        AMRefreshProgress.ForeColor = System.Drawing.ColorTranslator.FromHtml(ThisAddIn.amColor);


        for (int i = 1; i <= 100; i++)
        {
            SetTitle_Direct();
            AMRefreshProgress.PerformStep();
        }


        AMRefreshProgress.Value = 0;
        AMRefreshProgress.UseWaitCursor = false;


        Stopwatch watch2 = new Stopwatch();
        watch2.Start();

        SetTitle_Direct();

        watch2.Stop();
        MessageBox.Show("Time 1st run: " + watch.Elapsed + "\n Time 11th run: " + watch2.Elapsed);
    }

The SetTitle_Direct() loops through the slides:

        public void SetTitle_Direct()
    {
        PowerPoint.Presentation oPresentation = Globals.ThisAddIn.Application.ActivePresentation;

        foreach (PowerPoint.Slide oSlide in oPresentation.Slides)
        {

            if (oSlide.Shapes.HasTitle == OFFICECORE.MsoTriState.msoTrue)
            {
                oSlide.Shapes.Title.TextFrame.TextRange.Text = "Test Main Title";
            }



            for (int iShape = 1; iShape <= oSlide.Shapes.Count; iShape++)
            {
                if (oSlide.Shapes[iShape].Type == Microsoft.Office.Core.MsoShapeType.msoPlaceholder)
                {
                    if (oSlide.Shapes[iShape].PlaceholderFormat.Type == PowerPoint.PpPlaceholderType.ppPlaceholderSubtitle)
                    {
                        oSlide.Shapes[iShape].TextFrame.TextRange.Text = "Test Sub Title";
                    }
                }
            }
        }
    }

What causes the AddIn to allocate more and more memory - or how could this be avoided?

1 Answers1

0

When you develop a VSTO based add-in you typically deal with COM objects (PowerPoint is a COM server). With each property or method call which returns a COM object, an object reference is typically increased which leads to keeping objects in memory until the reference counter is decreased and equals zero. So, I'd recommend using the Marshal.ReleaseComObject method to decrease an object reference and let the runtime to keep memory and make objects lifetime shorter.

To be able to release every COM object you must split long lines of property and method calls. Declare each property or method call on a separate line of code. Thus, you will be able to release every object and debug the code efficiently if anything strange happens around.

You may take a look at the When to release COM objects in Office add-ins developed in .NET article for more information.

Another alternative way is to use the garbage collector:

GC.Collect
GC.WaitForPendingFinalizers
GC.Collect
GC.WaitForPendingFinalizers
Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
  • Thank you for the answer. In my case, how to implement this correctly? I added something like that in the SetTitle_Direct() Marshal.ReleaseComObject(oPresentation); But the memory usage still goes up, and every run is getting slower. How to prefent also this behavior? – Roger Heckly May 30 '20 at 11:51
  • Try using the garbage collector: ```GC.Collect GC.WaitForPendingFinalizers GC.Collect GC.WaitForPendingFinalizers``` – Eugene Astafiev May 30 '20 at 12:43
  • I have added the garbage collector as recommended - also the Marshal.ReleaseComObject(oPresentation) - but nothing helps. When I start Powerpoint and addin the memory is at 220MB usage (Process Memory) after 3 times running the above code for 33 slides, the "Process Memory" is at 320MB and running once through all slides is 5 times slower than the very first time. In the above code where is the correct place to insert the Marshal.ReleaseComObject and garbage collector? How long does it takes until the system releases memory? how to decrease obj lifetime? – Roger Heckly Jun 04 '20 at 16:00
  • It seems object pointers are still valid. Set them to `Nothing` before running the GC. – Eugene Astafiev Jun 04 '20 at 16:13
  • Do you mean with "Set them to Nothing": oPresentation = null; Marshal.ReleaseComObject(oPresentation); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); – Roger Heckly Jun 04 '20 at 17:30
  • I also read the following interesting article: https://stackoverflow.com/questions/37904483/as-of-today-what-is-the-right-way-to-work-with-com-objects There seem to be "rules" how to work with COM Objects. But also following them doesn't help here - my code is getting slower each iteration through all slides. I changed the foreach into for loop... and release the object each time in the for loop - but same effect - more and more memory allocated, code is getting slower each iteration. – Roger Heckly Jun 04 '20 at 17:33