Excel自动化:closures事件丢失

另外大家好,

我正在通过C#中的Interop进行Excel自动化,并且我想在工作簿closures时通知。 但是,工作簿上没有closures事件,也没有应用程序上的退出事件。

有没有人做过? 我怎样才能写出一段代码,它对正在closures的工作簿起作用(只有在工作簿真的closures的情况下才会执行) 理想情况下,closures工作簿应该会发生,所以我可以依靠该文件来反映所有更改。

关于我到目前为止发现的细节:

有一个BeforeClose()事件,但如果有未保存的更改,则在用户被问及是否保存它们之前引发此事件,所以此刻我可以处理该事件,我没有最终文件,我不能释放COM对象,这两个我需要有/做的事情。 我甚至不知道工作簿是否会被closures,因为用户可能会select放弃closures。

然后有一个BeforeSave()事件。 因此,如果用户select“是”来保存未保存的更改,则 BeforeClose() 之后执行BeforeSave()。 但是,如果用户select“中止”,然后点击“文件 – >保存”,则执行完全相同的事件顺序。 此外,如果用户select“否”,则根本不执行BeforeSave()。 只要用户不点击这些选项中的任何一个,也是一样的。

我使用类似投票的方法创build了一个hack,它可以工作:

鉴于要观察的工作簿,我创build了一个线程,定期尝试在工作簿集合中find该工作簿。

(DisposableCom类是我目前正确清理COM对象的解决scheme。)

Excel.Application app = wbWorkbook.Application; string sWorkbookName = wbWorkbook.Name; Thread overseeWorkbooksThread = new Thread(new ThreadStart( delegate() { bool bOpened = false; Excel.Workbooks wbsWorkbooks = app.Workbooks; using (new DisposableCom<Excel.Workbooks>(wbsWorkbooks)) { while (true) { Thread.Sleep(1000); if (wbsWorkbooks.ContainsWorkbookProperly(sWorkbookName)) bOpened = true; else if (bOpened) // Workbook was open, so it has been closed. break; else { // Workbook simply not finished opening, do nothing } } // Workbook closed RunTheCodeToBeRunAfterWorkbookIsClosed(); } })); overseeWorkbooksThread.Start(); 

“ContainsWorkbookProperly”扩展方法如下所示:

 public static bool ContainsWorkbookProperly(this Excel.Workbooks excelWbs, string sWorkbookName) { Excel.Workbook wbTemp = null; try wbTemp = excelWbs.Item(sWorkbookName); catch (Exception) { // ignore } if (wbTemp != null) { new DisposableCom<Excel.Workbook>(wbTemp).Dispose(); return true; } return false; } 

如果有一个更简单或更好的解决scheme,我仍然会感兴趣。

这不是我的代码,但这对我来说是一种享受:

https://gist.github.com/jmangelo/301884

复制粘贴:

 using System; using Excel = Microsoft.Office.Interop.Excel; namespace Helpers.Vsto { public sealed class WorkbookClosedMonitor { internal class CloseRequestInfo { public CloseRequestInfo(string name, int count) { this.WorkbookName = name; this.WorkbookCount = count; } public string WorkbookName { get; set; } public int WorkbookCount { get; set; } } public WorkbookClosedMonitor(Excel.Application application) { if (application == null) { throw new ArgumentNullException("application"); } this.Application = application; this.Application.WorkbookActivate += Application_WorkbookActivate; this.Application.WorkbookBeforeClose += Application_WorkbookBeforeClose; this.Application.WorkbookDeactivate += Application_WorkbookDeactivate; } public event EventHandler<WorkbookClosedEventArgs> WorkbookClosed; public Excel.Application Application { get; private set; } private CloseRequestInfo PendingRequest { get; set; } private void Application_WorkbookDeactivate(Excel.Workbook wb) { if (this.Application.Workbooks.Count == 1) { // With only one workbook available deactivating means it will be closed this.PendingRequest = null; this.OnWorkbookClosed(new WorkbookClosedEventArgs(wb.Name)); } } private void Application_WorkbookBeforeClose(Excel.Workbook wb, ref bool cancel) { if (!cancel) { this.PendingRequest = new CloseRequestInfo( wb.Name, this.Application.Workbooks.Count); } } private void Application_WorkbookActivate(Excel.Workbook wb) { // A workbook was closed if a request is pending and the workbook count decreased bool wasWorkbookClosed = true && this.PendingRequest != null && this.Application.Workbooks.Count < this.PendingRequest.WorkbookCount; if (wasWorkbookClosed) { var args = new WorkbookClosedEventArgs(this.PendingRequest.WorkbookName); this.PendingRequest = null; this.OnWorkbookClosed(args); } else { this.PendingRequest = null; } } private void OnWorkbookClosed(WorkbookClosedEventArgs e) { var handler = this.WorkbookClosed; if (handler != null) { handler(this, e); } } } public sealed class WorkbookClosedEventArgs : EventArgs { internal WorkbookClosedEventArgs(string name) { this.Name = name; } public string Name { get; private set; } } } 

当我使用它时,我将它从工作簿的名称返回到对工作簿的引用。

你可以使用这两个事件吗? 在BeforeClose()设置一个标志,然后BeforeSave()看看是否设置了标志。 但是,如果BeforeClose()触发,而BeforeSave()不触发,则需要重置它。 不知道是否有其他的东西可以帮助。

编辑:看起来你已经用“完全相同的事件顺序执行”覆盖了这个。 但是,如果你可以find一种方法来重置它(另一个“取消”事件?)它可能工作。