user773737
user773737

Reputation:

Powerpoint Process works in standalone console project, but not integrated project

I'm trying to run a local process using C# to call Powerpoint and convert a .pdf to a .ppt.

I made a standalone console app, in hopes of reproducing and isolating the problem. Unfortunately, it works for the standalone version, but not with the integrated version.

When it doesn't work, it throws no exceptions. It just silently fails to create the .pdf file.

NEW:

I'm getting an error on the event log:

Microsoft PowerPoint
PowerPoint can't do this because a dialog box is open. Please close the dialog box to continue.
P1: 400205
P2: 15.0.4420.1017

I don't see any sort of dialog box when running the console commands, the standalone console application, or running the integrated web project on my local machine.

The /pt command is supposed to be silent, as per the official documentation.

I can set the Identity of the ApplicationPool the project is running under to the user that I log in as, and I no longer get the above error in the event log. However, I get no other errors (that I can tell are related) from the event log.

It still doesn't work, however. Powerpoint or PDFCreator still crashes, and doesn't create the .pdf.

I also tried to run my working console app by calling it as a Process from my integrated issue, and that didn't work either.

The working console application:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
using System.Diagnostics;
using System.Text;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {

        var psi = new ProcessStartInfo();
        psi.FileName = "\"C:\\Program Files\\Microsoft Office\\Office15\\POWERPNT.exe\"";
        psi.Arguments = "/pt \"PDFCreator\" \"\" \"\" dd0e03ff-f386-4e65-b89d-72c7f1ee502d.pptx";
        psi.WorkingDirectory = "C:\\Temp";
        psi.CreateNoWindow = true;
        psi.ErrorDialog = true;
        psi.UseShellExecute = false;
        psi.WindowStyle = ProcessWindowStyle.Hidden;
        psi.RedirectStandardOutput = true;
        psi.RedirectStandardInput = false;
        psi.RedirectStandardError = true;
        try
        {
            using (Process exeProcess = Process.Start(psi))
            {
                exeProcess.PriorityClass = ProcessPriorityClass.High;
                var outString = new StringBuilder(100);
                exeProcess.OutputDataReceived += (s, e) => outString.AppendLine(e.Data);
                exeProcess.BeginOutputReadLine();
                var errString = exeProcess.StandardError.ReadToEnd();

                if (!string.IsNullOrEmpty(errString))
                {
                    Console.WriteLine("errors reported 1");
                }
            }
        }
        catch (Exception ex)
        {
          ex.ToString();
          Console.WriteLine("Errors reported 2 ");
          Console.WriteLine(ex.ToString());
        }

        Console.WriteLine(psi.FileName);
        Console.WriteLine(psi.Arguments);
        Console.WriteLine(psi.WorkingDirectory);
    }
}
}

It prints

"C:\Program Files\Microsoft Office\Office15\POWERPNT.exe"
/pt "PDFCreator" "" "" dd0e03ff-f386-4e65-b89d-72c7f1ee502d.pptx
C:\Temp

The not working integrated application:

using System;
using System.Diagnostics;
using System.Text;

namespace CT.Services.Helper
{
    public static class ExecutableRunner
    {
        public static ExecutableResult RunExeNamed(string exeFilename, string commandLineArgs)
    {
        return RunExeNamed(exeFilename, commandLineArgs, null);
    }

    public static ExecutableResult RunExeNamed(string exeFilename, string commandLineArgs, string workingDirectory)
    {
        var result = new ExecutableResult { WasSuccessful = true };

        var psi = new ProcessStartInfo();
        psi.FileName =  "\""+exeFilename+"\"";
        psi.Arguments = commandLineArgs;
        if(!string.IsNullOrEmpty(workingDirectory))
        {
            psi.WorkingDirectory = workingDirectory;
        }
        psi.CreateNoWindow = false;
        psi.ErrorDialog = true;
        psi.UseShellExecute = false;
        psi.WindowStyle = ProcessWindowStyle.Hidden;
        psi.RedirectStandardOutput = true;
        psi.RedirectStandardInput = false;
        psi.RedirectStandardError = true;
        try
        {
            // Start the process with the info we specified.
            // Call WaitForExit and then the using statement will close.
            using (Process exeProcess = Process.Start(psi))
            {
                exeProcess.PriorityClass = ProcessPriorityClass.High;
                var outString = new StringBuilder(100);
                // use ansynchronous reading for at least one of the streams
                // to avoid deadlock
                exeProcess.OutputDataReceived += (s, e) => outString.AppendLine(e.Data);
                exeProcess.BeginOutputReadLine();
                // now read the StandardError stream to the end
                // this will cause our main thread to wait for the
                // stream to close (which is when ffmpeg quits)
                var errString = exeProcess.StandardError.ReadToEnd();

                if (!string.IsNullOrEmpty(errString))
                {
                    result.WasSuccessful = false;
                    result.ErrorMessage = errString;
                }
            }
        }
        catch (Exception ex)
        {
            result.WasSuccessful = false;
            result.ErrorMessage = ex.ToString();
        }
        Debug.WriteLine(psi.FileName);
        Debug.WriteLine(psi.Arguments);
        Debug.WriteLine(psi.WorkingDirectory);
        return result;
    }
}

public class ExecutableResult
{
    public bool WasSuccessful { get; set; }

    public string ErrorMessage { get; set; }
}

}

it prints

"C:\Program Files\Microsoft Office\Office15\POWERPNT.exe"
/pt "PDFCreator" "" "" dd0e03ff-f386-4e65-b89d-72c7f1ee502d.pptx
C:\Temp

I would think that one of the strings I'm using as the file path or console command would be wrong, so I had them printed to the console, but you can see that those aren't the issue.

Upvotes: 2

Views: 1437

Answers (5)

Markus Safar
Markus Safar

Reputation: 6580

Last try ;-) You could use Interop, open the file and afterwards save it as pdf.

For this little example to work you need to reference the Microsoft.Office.Interop.PowerPoint assembly and the Microsoft.Office.Core namespace (found in the "Microsoft Office 14.0 Object Library" assembly which is located under the COM assemblies).

Little Example:

/// <summary>
/// Converts the specified source file and saves it as pdf with the
/// specified destination filename.
/// </summary>
/// <param name="sourceFilename">The filename of the file to be converted into a pdf.</param>
/// <param name="destinationFilename">The filename of the pdf.</param>
/// <exception cref="System.IO.FileNotFoundException">Is thrown if the specified source file does not exist.</exception>
/// <exception cref="System.Exception">Is thrown if something went wrong during the convertation process.</exception>
public static void SaveAsPDF(string sourceFilename, string destinationFilename)
{
    if (!File.Exists(sourceFilename))
    {
        throw new FileNotFoundException(string.Format("The specified file {0} does not exist.", sourceFilename), sourceFilename);
    }

    try
    {
        Microsoft.Office.Interop.PowerPoint.Application app = new Microsoft.Office.Interop.PowerPoint.Application();

        app.Presentations.Open(sourceFilename).SaveAs(destinationFilename, Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsPDF);
        app.Quit();
    }
    catch (Exception e)
    {
        throw new Exception(string.Format("Unable to convert {0} to {1}", sourceFilename, destinationFilename), e);
    }
}

Upvotes: 1

Markus Safar
Markus Safar

Reputation: 6580

Just another idea (not sure if it is a stupid one or not). As far as I remember powerpoint (at least since 2010) is able to save directly to pdf. For 2007 you can use a microsoft plugin called "Save as PDF" which can be found here 2007 Microsoft Office Add-in: Microsoft Save as PDF or XPS.

Using the provided Solution from Todd Main you could (theoretically) execute a macro at startup which could store the file directly as PDF (without printing and hopefully without dialog box).

Update:

I've tried it right now - problem is you have to store the macro in the presentation, therfore you have to use the pptm file format. Next you have to open the file once and activate the macro execution (is shown on the top of the editing screen). After that you can simply execute it from the command line like

POWERPNT.EXE /M Filename.pptm SaveAsPDF

The macro looks like this:

Sub SaveAsPDF()
    Dim ap As Presentation: Set ap = ActivePresentation
    ap.SaveAs ap.Path & "\" & ap.Name, ppSaveAsPDF
    PowerPoint.Application.Quit
End Sub

Upvotes: 1

Nanhydrin
Nanhydrin

Reputation: 4472

Odds are you are seeing this problem because you've never actually opened up Powerpoint under the user that is running the application in the environment where it's failing.
This will be the user that the application pool is running under and, as I see you've mentioned to OmegaMan, that user is LocalSystem.

When LocalSystem tries to open Powerpoint, then Powerpoint will be doing that thing that all Office applications do where they complete their install the first time a new user runs them.
You are not seeing this installation box in your console app or on your local system because you're running those under your own account and you've previously opened PowerPoint.

As OmegaMan rightly says Powerpoint is meant to be an interactive process so it is probably going to cause trouble even if you're very very careful. You'll end up with instances of Powerpoint staying open after they're supposed to have closed and loads of instances building up in the

That said, if you really want to get this running then you either need to get the application pool running as a user that has opened Powerpoint interactively, or change your code to use impersonation at the point where you need to run Powerpoint and then impersonate a user that has used it interactively to do what you need.

Upvotes: 0

ΩmegaMan
ΩmegaMan

Reputation: 31616

The integrated version of the code is part of a web application

  1. Provide access to the current working directory where this file is being created for the app pool user and verify it has Read/Write.
  2. Check the Temp directory on the server for the app-pool process user and verify that the process has read/write access to the temp directory.

Edit Upon seeing the event log.

Asp.net should not be using any application which is a user interactive process. You need to find a different application to create these powerpoint slides that is asp.net friendly or determine what Powerpoint is trying to do when it opens a dialog.

Upvotes: 0

Markus Safar
Markus Safar

Reputation: 6580

Could it be a problem with the permissions of the application pool? Maybe it does not have permissions to perform all the actions you want to - you could check that by executing your console application under the same context as the application pool is running.

Upvotes: 1

Related Questions