前幾天有個同事問我一個問題,就是如何使用子線程打開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. |
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,暫時還不清楚,也希望高人指點一下啦,呵呵。同時也希望能夠幫助到其他人。