我正在尝试使用C#运行本地进程来调用Powerpoint并将.pdf转换为.ppt。
我制作了一个独立的控制台应用程序,希望能够重现和隔离问题。 不幸的是,它适用于独立版本,但不适用于集成版本。
当它不起作用时,它不会抛出异常。它只是默默地无法创建.pdf文件。
我在事件日志中收到错误:
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
在运行控制台命令,独立控制台应用程序或在本地计算机上运行集成Web项目时,我看不到任何类型的对话框。
根据the official documentation,/ pt命令应该是静默的。
我可以将项目正在运行的Identity
的{{1}}设置为我登录的用户,并且我不再在事件日志中收到上述错误。但是,我从事件日志中得到的其他错误(我可以说是相关的)。
我还试图通过从我的集成问题中将其称为ApplicationPool
来运行我的工作控制台应用程序,但这也不起作用。
Process
打印
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);
}
}
}
"C:\Program Files\Microsoft Office\Office15\POWERPNT.exe"
/pt "PDFCreator" "" "" dd0e03ff-f386-4e65-b89d-72c7f1ee502d.pptx
C:\Temp
}
打印
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; }
}
我认为我用作文件路径或控制台命令的其中一个字符串是错误的,所以我将它们打印到控制台,但你可以看到那些不是问题。
答案 0 :(得分:1)
应用程序池的权限是否有问题?也许它没有权限执行您想要的所有操作 - 您可以通过在应用程序池运行的相同上下文中执行控制台应用程序来检查它。
答案 1 :(得分:1)
只是另一个想法(不确定它是否是一个愚蠢的想法)。据我所知,powerpoint(至少从2010年开始)能够直接保存到pdf。对于2007年,您可以使用名为“另存为PDF”的微软插件,可在此处找到2007 Microsoft Office Add-in: Microsoft Save as PDF or XPS。
使用提供的Solution from Todd Main你可以(理论上)在启动时执行一个宏,它可以直接将文件存储为PDF(无需打印,希望没有对话框)。
<强>更新强>
我现在已经尝试过 - 问题是您必须将宏存储在演示文稿中,因此您必须使用pptm文件格式。接下来,您必须打开文件一次并激活宏执行(显示在编辑屏幕的顶部)。之后,您只需从命令行执行它,如
POWERPNT.EXE /M Filename.pptm SaveAsPDF
宏看起来像这样:
Sub SaveAsPDF()
Dim ap As Presentation: Set ap = ActivePresentation
ap.SaveAs ap.Path & "\" & ap.Name, ppSaveAsPDF
PowerPoint.Application.Quit
End Sub
答案 2 :(得分:1)
最后一次尝试;-) 您可以使用Interop,打开文件,然后将其另存为pdf。
要使用这个小例子,您需要引用Microsoft.Office.Interop.PowerPoint程序集和Microsoft.Office.Core命名空间(位于COM程序集下的“Microsoft Office 14.0对象库”程序集中)
小例子:
/// <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);
}
}
答案 3 :(得分:0)
代码的集成版本是Web应用程序的一部分
编辑看到事件日志。
Asp.net不应该使用任何用户交互过程的应用程序。您需要找到一个不同的应用程序来创建这些asp.net友好的powerpoint幻灯片,或者确定Powerpoint在打开对话框时尝试做什么。
答案 4 :(得分:0)
您可能已经看到了这个问题,因为您从未真正在其失败的环境中运行应用程序的用户下打开Powerpoint。 这将是运行应用程序池的用户,正如我在OmegaMan中所提到的那样,该用户是LocalSystem。
当LocalSystem尝试打开Powerpoint时,Powerpoint将在新用户第一次运行它们时执行所有Office应用程序完成安装的操作。
您没有在控制台应用程序或本地系统上看到此安装框,因为您在自己的帐户下运行这些安装框,并且之前已经打开过PowerPoint。
正如OmegaMan正确地说Powerpoint意味着是一个互动过程,所以即使你非常小心,它也可能会带来麻烦。你最终会看到Powerpoint的实例在他们应该关闭之后保持打开状态并在
中构建大量实例也就是说,如果你真的想要让它运行,那么你需要让应用程序池作为以交互方式打开Powerpoint的用户运行,或者更改代码以在需要运行Powerpoint的位置使用模拟然后模仿用户以交互方式使用它来做你需要的事情。