最近写了一个文档转换项目,其功能是将PPT文件转换成视频文件,word文件转换成PNG图片,PDF文件转换为PNG图片。采用的方案是调用Office的组件和Adobe Acrobat的组件进行转换。
系统运行的先行条件是:
1. 在windows 平台下运行。
2. 安装了Microsoft Office 2010 或以上版本。
3. 安装了Adobe Acrobat 9或以上(其他版本为测试过)。
项目添加的引用如下图所示:
将PPT转换为WAV视频文件,核心代码为:
PowerPoint.Application pptApplication = new PowerPoint.Application();
PowerPoint.Presentation ppt = pptApplication.Presentations.Open(fileName,
Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
PowerPoint.PpSaveAsFileType format = PowerPoint.PpSaveAsFileType.ppSaveAsWMV;
ppt.SaveAs(pathwmv, format, Microsoft.Office.Core.MsoTriState.msoFalse);
ppt.Close();
pptApplication.Quit();
PDF转换首先是判断文件是否为word文件,如果是,则调用Offic组件进行转换为PDF文件,核心代码是:
Word.Application wordApplication = new Word.Application();
wordApplication.Documents.Open(fileName);
Word.WdSaveFormat format = Word.WdSaveFormat.wdFormatPDF;
wordApplication.ActiveDocument.SaveAs2(pathpdf, format);
PDF转换为图片的过程是调用Adobe Acrobat组件进行转换的,核心代码为:
Acrobat.CAcroPDPage pdfPage;
Acrobat.CAcroRect pdfRect;
Acrobat.CAcroPoint pdfPoint;
int pageCount = pdfDoc.GetNumPages();
for (int i = 0; i < pageCount; i++){
pdfPage = (Acrobat.CAcroPDPage)pdfDoc.AcquirePage(i);
pdfPoint = (Acrobat.CAcroPoint)pdfPage.GetSize();
pdfRect = (Acrobat.CAcroRect) Microsoft.VisualBasic.Interaction.CreateObject("AcroExch.Rect", "");
pdfRect.Left = 0;
pdfRect.right = pdfPoint.x;
pdfRect.Top = 0;
pdfRect.bottom = pdfPoint.y;
pdfPage.CopyToClipboard(pdfRect, 0, 0, 100);
DataObject loClipboardData = Clipboard.GetDataObject();
if (loClipboardData.GetDataPresent(DataFormats.Bitmap)) {
Bitmap pdfBitmap = (Bitmap)loClipboardData.GetData(DataFormats.Bitmap);
int loImgWidth = pdfPoint.x;
int loImgHeight = pdfPoint.y;
Bitmap loTemplateBitmap = new Bitmap(pdfPoint.x, pdfPoint.y);
Image loPdfImage = pdfBitmap.GetThumbnailImage(loImgWidth, loImgHeight, null, IntPtr.Zero);
Bitmap thumbnailBitmap = new Bitmap(loImgWidth + 7, loImgHeight + 7, System.Drawing.Imaging.PixelFormat.Format32bppArgb); loTemplateBitmap.MakeTransparent();
using (Graphics poGraphics = Graphics.FromImage(thumbnailBitmap)) {
poGraphics.DrawImage(loPdfImage, 2, 2, loImgWidth, loImgHeight);
poGraphics.DrawImage(loTemplateBitmap, 0, 0);
//保存图象文件
string lcSaveFileName = lcOutPutFile + "_" + i.ToString() + ".png";
thumbnailBitmap.Save(lcSaveFileName, System.Drawing.Imaging.ImageFormat.Png);
}
loTemplateBitmap.Dispose();
loPdfImage.Dispose();
thumbnailBitmap.Dispose();
}
Marshal.ReleaseComObject(pdfPage);
Marshal.ReleaseComObject(pdfRect);
}
创建Windows 服务守护转换进程:
为保证PPT转视频应用程序(DocumentConvert)一直运行,原先设计是创建一个Windows服务来保护该进程,具体设计流程如下:
创建一个Windows Service, 在服务中添加定时器,在定时器事件处理中,查找名为“DocumentConvert”的进程,若此进程不在,则新建一个进程开启在该软件安装目录下的DocumentConvert.exe程序;若此程序有多个运行进程,则只保留一个进程,杀掉其他进程。
服务创建结果是,该服务可以正常安装,启动和关闭,卸载。并且调起的进程可以正常访问数据库,可以正常转换PDF文件,但是不能正常启动Office打开PPT和Word,在使用其Open方法时抛出异常。
调研结果大概是,windows服务的“窗口站”不同于用户的“窗口站”。而ppt应用程序只能在用户窗口站打开。并且PowerPoint是一个用户应用程序,不应该在服务中被使用。设置该服务与用户界面交互是一个糟糕的选择,及时这样工作了也会带来其他更深层次的问题。
参考微软Office组件使用特别说明:http://support.microsoft.com/default.aspx?scid=kb;en-us;257757
Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.
遇到同样问题的参考:http://stackoverflow.com/questions/729609/powerpoint-interop-fails-in-a-windows-service-but-works-fine-in-a-windows-form-a
创建用户守护进程:
守护服务创建失败,我们转而求其次,创建一个用户守护进程,来保证DocumentConvert一直运行。具体过程如下:程序每隔一段时间(在配置文件中设置),查找名为“DocumentConvert”的进程,若此进程不在,则新建一个进程,开启在该软件安装目录下的DocumentConvert.exe程序;若此程序有多个运行进程,则只保留一个进程,杀掉其他进程。
主要代码为:
public bool OnTimedEvent() {
bool result = true;
//判断进程是否正在进行
Process[] pros = Process.GetProcessesByName(this.process_name);
try {
//如果该进程有多个副本在运行,则杀掉多个进程,只保留一个
if (pros.Length > 1) {
for (int i = 1; i < pros.Length; i++) {
pros[i].Kill();
}
} else if (pros.Length < 1) { //程序不在进行中,开启进程
Process process = new Process();
process.StartInfo.FileName = this.process_address;
process.Start();
}
} catch (Exception ex) {
result = false;
}
return result;
}