Search code examples
c#.netwinformspowerpointoffice-interop

C# WinForms timer is stuck/hangs while creating my Office.Interop.PowerPoint


Creating PowerPoint presentations in C# Visual Studio, I realized that my WinForm timer hangs; I can't even move my Application around the screen. Is there a way to create my PowerPoint in the background?

To illustrate the issue, I have this code example in "C:\myPowerPointHangs\" for C# Windows Forms .NET

I have added from Reference Manager>COM>Type Libraries the assembly reference "Microsoft Office 16.0 Object Library" (Version 2.8), or also from NuGet: "Microsoft.Office.Interop.PowerPoint" (Version 15.0.4420.1018) added to my project...

I have created two Timers.

  • Timer1 will show 100ms steps counting in the caption of my WinForm, and it will start Timer2 at 2000 milliseconds then continue counting.

  • Timer2 runs once. It creates the slide and the matrix of boxes. This takes several seconds. Finally, it saves the presentation. After that, my WinForm is alive again.

// Project>Add Reference>COM>Type Libraries>Microsoft Office 16.0 Object Library
// Tools>NuGet Package Manager>Manage NuGet Packages for solution>Microsoft.Office.Interop.PowerPoint 15.0.4420.1018
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using Office = Microsoft.Office.Core;
using State = Microsoft.Office.Core.MsoTriState;
using System;
using System.Windows.Forms;

namespace myPowerPointHangs
{
    public partial class Form1 : Form
    {
        readonly float u = 72.0f; // Inches
        int t = 0;
        int t0 { get => (++t); set => t = 0; }
        string fileName = @"C:\myPowerPointHangs\mySlides.ppt";
        public Form1() { InitializeComponent(); timer1.Start(); }
        private void timer1_Tick(object sender, EventArgs e)
        { 
            this.Text = t0.ToString() + "00ms";
            if (t == 20) timer2.Start();
        }

        private void timer2_Tick(object sender, EventArgs e)
        {
            timer2.Stop();
            PowerPoint.Application pptApplication = new PowerPoint.Application();
            PowerPoint.Presentation ppt;
            PowerPoint.Slide slide;
            PowerPoint.Slides slides;
            Office.MsoAutoShapeType shape;
            ppt = pptApplication.Presentations.Add(State.msoTrue);
            slides = ppt.Slides;
            slides.Application.Caption = "mySlides";
            slide = slides.Add(1, PowerPoint.PpSlideLayout.ppLayoutBlank);
            shape = Office.MsoAutoShapeType.msoShapeFlowchartTerminator;

            float left = 6.61f * u;
            for (int col = 0; col < 5; col++)
            {
                float top = 0.1f * u;
                for (int row = 0; row < 20; row++)
                {
                    slide.Shapes.AddShape(shape, left, top, 0.77f * u, 0.23f * u);
                    int i = slide.Shapes.Count;
                    slide.Shapes[i].Fill.ForeColor.RGB = System.Drawing.ColorTranslator.ToWin32(System.Drawing.Color.White);
                    slide.Shapes[i].Line.ForeColor.RGB = System.Drawing.ColorTranslator.ToWin32(System.Drawing.Color.FromArgb(58, 58, 58));
                    slide.Shapes[i].TextFrame.TextRange.Font.Color.RGB = System.Drawing.ColorTranslator.ToWin32(System.Drawing.Color.FromArgb(19, 40, 79));
                    slide.Shapes[i].TextFrame.TextRange.Font.Size = 6.0f;
                    slide.Shapes[i].TextFrame.WordWrap = State.msoFalse;
                    slide.Shapes[i].TextFrame.TextRange.Text = col.ToString("00")+row.ToString("00");
                    top += slide.Shapes[i].Height + 0.02f * u;
                }
                left += slide.Shapes[slide.Shapes.Count].Width + 0.05f * u;
            }

            if (System.IO.File.Exists(fileName)) System.IO.File.Delete(fileName);
            ppt.SaveAs(fileName, PowerPoint.PpSaveAsFileType.ppSaveAsPresentation, Office.MsoTriState.msoCTrue);
            ppt.Close();
        }
    }
}

There are similar topics here and there, but do not address my question: how to run Office.Interop.PowerPoint in the background without hanging my WinForm application?

In addition, if for any reason we open a popup message in the PowerPoint App before or while creating the presentation, it generates an Exception as:

System.Runtime.InteropServices.COMException: 'Call was rejected by callee. (Exception from HRESULT: 0x80010001 

Opening_PowerPoint_Popup => Call_was_rejected_by_callee


Solution

  • You can write a simple void function and use multi-threading to create a new thread for power point process.

    Notice: First you need to create timer1 manually.

    // Project>Add Reference>COM>Type Libraries>Microsoft Office 16.0 Object Library
    // Tools>NuGet Package Manager>Manage NuGet Packages for solution>Microsoft.Office.Interop.PowerPoint 15.0.4420.1018
    using PowerPoint = Microsoft.Office.Interop.PowerPoint;
    using Office = Microsoft.Office.Core;
    using State = Microsoft.Office.Core.MsoTriState;
    using System;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace myPowerPointHangs
    {
        public partial class Form1 : Form
        {
    
            public Form1() { InitializeComponent(); timer1.Start(); }
            
            readonly float u = 72.0f; // Inches
            int t = 0;
            string fileName = @"C:\myPowerPointHangs\mySlides.ppt";
            int cnt = 0;
            Thread trd;
    
            private void timer1_Tick(object sender, EventArgs e)
            {
                Text=cnt.ToString() + "00ms";
                if (cnt++ >= 20) { cnt = 0; trd = new Thread(power_point); trd.Start(); timer1.Stop(); }
            }
    
            void power_point()
            {
                PowerPoint.Application pptApplication = new PowerPoint.Application();
                PowerPoint.Presentation ppt;
                PowerPoint.Slide slide;
                PowerPoint.Slides slides;
                Office.MsoAutoShapeType shape;
                ppt = pptApplication.Presentations.Add(State.msoTrue);
                slides = ppt.Slides;
                slides.Application.Caption = "mySlides";
                slide = slides.Add(1, PowerPoint.PpSlideLayout.ppLayoutBlank);
                shape = Office.MsoAutoShapeType.msoShapeFlowchartTerminator;
    
                float left = 6.61f * u;
                for (int col = 0; col < 5; col++)
                {
                    float top = 0.1f * u;
                    for (int row = 0; row < 20; row++)
                    {
                        slide.Shapes.AddShape(shape, left, top, 0.77f * u, 0.23f * u);
                        int i = slide.Shapes.Count;
                        slide.Shapes[i].Fill.ForeColor.RGB = System.Drawing.ColorTranslator.ToWin32(System.Drawing.Color.White);
                        slide.Shapes[i].Line.ForeColor.RGB = System.Drawing.ColorTranslator.ToWin32(System.Drawing.Color.FromArgb(58, 58, 58));
                        slide.Shapes[i].TextFrame.TextRange.Font.Color.RGB = System.Drawing.ColorTranslator.ToWin32(System.Drawing.Color.FromArgb(19, 40, 79));
                        slide.Shapes[i].TextFrame.TextRange.Font.Size = 6.0f;
                        slide.Shapes[i].TextFrame.WordWrap = State.msoFalse;
                        slide.Shapes[i].TextFrame.TextRange.Text = col.ToString("00") + row.ToString("00");
                        top += slide.Shapes[i].Height + 0.02f * u;
                    }
                    left += slide.Shapes[slide.Shapes.Count].Width + 0.05f * u;
                }
    
                if (System.IO.File.Exists(fileName)) System.IO.File.Delete(fileName);
                ppt.SaveAs(fileName, PowerPoint.PpSaveAsFileType.ppSaveAsPresentation, Office.MsoTriState.msoCTrue);
                ppt.Close();
                trd.Abort();
            }
    
        }
    }