调用Application.Quit()方法时报"This operation cannot be performed in this event handler."错误的一种解决方法

前几天有个同事问我一个问题,就是如何使用子线程打开PPTX文件。开始时我并没有觉得这是个什么大问题。因为我觉得这个主要的难点在于注意线程上下文的切换上。不过开始做个Demo后感觉有点困惑了,主要集中在Application.Quit()方法上。

因为需求里希望打开PPT文件进行播放幻灯片,当幻灯片结束时需要关闭PPT文件。这个需求很合理,因为我们的程序其实就是个打开幻灯片的外壳,如果幻灯片结束后PPT程序还挂在任务栏上,那直接使用PPT不就行了嘛,呵呵。

直入主题啦。使用的程序集就是Microsoft.Office.Interop.PowerPoint.dll,从MSDN的文档上找到了两个事件可以解决关闭的时机,SlideShowEnd和PresentationClose事件。

SlideShowEnd事件:一个幻灯片播放结束后发生,最后一次SlideShowNextSlide事件发生后立即发生。(Occurs after a slide show ends, immediately after the last SlideShowNextSlide event occurs.)

PresentationClose事件:任何打开的ppt关闭之前立即发生,当ppt从Presentations集合移除时发生。(Occurs immediately before any open presentation closes, as it is removed from the Presentations collection.)

从说明中选择在SlideShowEnd事件里关闭PPT文件。

var _application = new Microsoft.Office.Interop.PowerPoint.Application();
_application.SlideShowEnd += pres=>
{
    pres.Close();
    _application.Quit();
};
第一次打开一个文件并结束幻灯片时一切顺利,但是重复几次同样的操作,就会报出"Application (unknown member) : Invalid request.  This operation cannot be performed in this event handler."的COMException异常。先后进行了多次修改调用的位置(不使用子线程和使用子线程调用),但是总是运行到Quit方法时报错,从Google上也没有找到明确的答案,只是说COM对象是出于STA模式下。

后来突然看到了Open方法的参数说明:

Name

Required/Optional

Data Type

Description

FileName

Required

String

The name of the file to open.

ReadOnly

Optional

MsoTriState

Specifies whether the file is opened with read/write or read-only status.

Untitled

Optional

MsoTriState

Specifies whether the file has a title.

WithWindow

Optional

MsoTriState

Specifies whether the file is visible.

其中WithWindow开始使用的是默认值,即MsoTriState.msoTrue。这样在关闭幻灯片时,如果不调用Quit方法会留下PPT窗口。而当WithWindow设置为MsoTriState.msoFalse,则关闭幻灯片不会留下窗口,这样就不用调用Quit方法了。

var pres = _application.Presentations.Open(path.ToString(),
           MsoTriState.msoTrue,
           MsoTriState.msoFalse,
           MsoTriState.msoFalse);
if (pres.Slides.Count > 0)
{
    pres.SlideShowSettings.Run();
}
修改后的事件代码:

var _application = new Microsoft.Office.Interop.PowerPoint.Application();
_application.SlideShowEnd += pres=>
{
    pres.Close();    
};
当我的程序关闭时再调用Application.Quit()方法,这样能保证即关闭了PPT程序,同时也不会因为打开几次PPT文件而报错了。

至于为什么不允许在事件中调用Quit,暂时还不清楚,也希望高人指点一下啦,呵呵。同时也希望能够帮助到其他人。

Demo


你可能感兴趣的:(Office,DotNet)