views:

1278

answers:

3

Hello, I have a script that opens Powerpoint from my application and exports all slides. After that, I need the application to be closed.

I've tried without any luck. Could you please help?

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Powerpoint = Microsoft.Office.Interop.PowerPoint;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using System.Runtime.InteropServices;

namespace PresentrBuilder
{


class PowerpointConverter
{

    public static void Convert(String file, String safeFile)
    {
        Powerpoint.Application PP;
        Powerpoint.Presentation Presentation;

        PP = new Powerpoint.ApplicationClass();
        PP.Visible = MsoTriState.msoTrue;
        PP.WindowState = Microsoft.Office.Interop.PowerPoint.PpWindowState.ppWindowMinimized;
        Presentation = PP.Presentations.Open(file, MsoTriState.msoFalse, MsoTriState.msoTrue, MsoTriState.msoTrue);

            // Voor elke slide, exporteren
            String exportSlidesPath = Path.Combine(Properties.Settings.Default.CacheDir, @"presentatienaam1\slides");

            // Kijk of de directory bestaat
            if (!Directory.Exists(exportSlidesPath))
            {
                Directory.CreateDirectory(exportSlidesPath);
            }

                // Kijk of er al bestanden in de directory staan
                // Zo ja: verwijderen
                String[] files = Directory.GetFiles(exportSlidesPath, "*.png");
                if (files.Length > 0)
                {
                    foreach (string fileName in files)
                    {
                        File.Delete(Path.Combine(exportSlidesPath, fileName));
                    }
                }

            // Elke slide exporteren
            foreach (Slide slide in Presentation.Slides)
            {
                slide.Export(Path.Combine(exportSlidesPath, "slide_" + slide.SlideIndex + ".png"), "PNG", 1024, 768);
                Marshal.ReleaseComObject(slide);
            }


        GC.Collect();
        GC.WaitForPendingFinalizers();

        Marshal.ReleaseComObject(PP.Presentations);
        Marshal.ReleaseComObject(Presentation.Slides);

        Presentation.Close();
        Marshal.FinalReleaseComObject(Presentation);

        PP.Quit();
        Marshal.FinalReleaseComObject(PP);

    }

}
}
+1  A: 

See the discussion on the same topic here: http://stackoverflow.com/questions/1041266/c-and-excel-automation-ending-the-running-instance/1048473#1048473

It covers Excel, but the principles are exactly the same.

Summary: you need to "release" the Presentations, Slides and (multiple) Slide objects. BTW, I wouldn't bother setting the variables to null. That's not necessary or helpful.

Gary McGill
ell, I've tried to release all of the objects that have a reference to Powerpoint, nothing seems to help... I'm releasing all the Slide objects with Marshal.ReleaseComObject(slide);.
MysticEarth
Are you releasing the PP.Presentations and Presentation.Slides Object as well? People often forget about the collections the use.
Andre Kraemer
Yes, I'm releasing those as well..
MysticEarth
could you please edit your question and post your updated code?
Andre Kraemer
Done. I've read other threads about this, but no luck :(
MysticEarth
A: 

As an addition to Gary's answer: In Order to release the collections, you have to assign them to temporary variables. (I used slides and presentations as temp variables in the example below).

// removed using statements...

namespace PresentrBuilder
{


  class PowerpointConverter
  {

  public static void Convert(String file, String safeFile)
  {
    Powerpoint.Application PP;
    Powerpoint.Presentation Presentation;

    PP = new Powerpoint.ApplicationClass();
    // ...
    var presentations = PP.Presentations;
    Presentation = presentations.Open(file, MsoTriState.msoFalse, MsoTriState.msoTrue, MsoTriState.msoTrue);

        // Voor elke slide, exporteren
        // ...

        // Elke slide exporteren
        var slides = Presentation.Slides;
        foreach (Slide slide in slides)
        {
            slide.Export(Path.Combine(exportSlidesPath, "slide_" + slide.SlideIndex + ".png"), "PNG", 1024, 768);
            Marshal.ReleaseComObject(slide);
        }


    GC.Collect();
    GC.WaitForPendingFinalizers();

    Marshal.ReleaseComObject(presentations);
    Marshal.ReleaseComObject(slides);

    Presentation.Close();
    Marshal.FinalReleaseComObject(Presentation);

    PP.Quit();
    Marshal.FinalReleaseComObject(PP);

} } }
Andre Kraemer
I've edited my code with your example, but no luck either. I don't get it, because it seems every reference with Powerpoint is released.
MysticEarth
Just a guess: Did you try to call GC.Collect after releasing Presentation and PP?
Andre Kraemer
Just tried it; no succes...I'm giving up :(
MysticEarth
+1  A: 

If anyone else is struggling with this (not being able to close the PPT after iterating through the slides), even after doing all the garbage collection and releasing resources, I spent the better part of today scratching my head with this one. My solution was, instead of using a foreach to iterate through the slides, i did as follows:

        Microsoft.Office.Interop.PowerPoint.Application powerpoint;
        Microsoft.Office.Interop.PowerPoint.Presentation presentation;
        Microsoft.Office.Interop.PowerPoint.Presentations presentations;

        powerpoint = new Microsoft.Office.Interop.PowerPoint.ApplicationClass();
        powerpoint.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
        presentations = powerpoint.Presentations;

        presentation = presentations.Open(localPath, MsoTriState.msoFalse, MsoTriState.msoTrue, MsoTriState.msoTrue);


        //String presentationTitle = objPres.Name;
        Microsoft.Office.Interop.PowerPoint.Slides slides = presentation.Slides;
        **for (int i = 1; i <= slides.Count; i++)
        {
            Microsoft.Office.Interop.PowerPoint.Slide slide = slides[i];
            String slideName = slide.Name;
            releaseCOM(slide);
        }**

This is my releaseCOM method:

    private static void releaseCOM(object o)
    {
        try
        {
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(o);
        }
        catch { }
        finally
        {
            o = null;
        }
    }