【问题标题】:C# : Workbook's Close methods doesn't work for Workbook opened on OneDriveC#:工作簿的关闭方法不适用于在 OneDrive 上打开的工作簿
【发布时间】:2021-08-20 14:40:54
【问题描述】:

我正在开发一个 VSTO 加载项,它在当前工作簿上调用 Microsoft.Office.Interop.Excel.Workbook.Close 方法,以允许另一个进程在该文件上写入。

如果文件存储在本地,则该方法有效地关闭工作簿并从锁定文件的 EXCEL.EXE 中释放句柄。

但是,当文件保存在 OneDrive 上时,Close 方法似乎不起作用。那是一个错误吗?有什么想法吗?

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
     this.Application.WorkbookOpen += new Excel.AppEvents_WorkbookOpenEventHandler(Application_WorkbookOpen);
}
        
void Application_WorkbookOpen(Excel.Workbook Wb)
{
     Wb.Close(); // doesn't work for oneDrive files ?!
}

【问题讨论】:

  • 如果您在Close 之后等待/睡眠 10 秒,这会改变什么吗?作为用户,我注意到 OneDrive 同步似乎需要额外的时间......
  • 我尝试在使用 Thread.Sleep(10000) 关闭前后等待 10 秒,但没有结果。句柄仍然存在,并且工作簿不会关闭。该文件仍由 EXCEL.EXE 持有
  • 是的,您可能需要设置一个计时器或在单独的线程中休眠,以便活动线程可以泵送窗口消息,最终一切都可以关闭。 SysInternals 曾经有一个名为“handle.exe”(或者可能是 Win SDK?)的实用程序,它可以提供打开的句柄?如果你只是打电话给Close() 然后等待,句柄最终会关闭吗???在活动线程上调用睡眠可能不会给进程时间来关闭事物,因为活动线程上的睡眠会停止一切。只是想法......
  • 好主意!我使用handle.exe 来验证关闭的块。它确实是 Excel 本身。因此,使用新线程是解决方案的一部分。好像不需要 Thread.Sleep ,也可能是句柄在主线程中存活了这么短的时间。

标签: c# excel com vsto excel-addins


【解决方案1】:

除了延迟之外,尝试运行垃圾收集器以扫除堆并释放关闭工作簿后保持不变的未使用的 COM 引用。注意,你需要调用 GC 两次:

GC.Collect
GC.WaitForPendingFinalizers
GC.Collect
GC.WaitForPendingFinalizers

When to release COM objects in Office add-ins developed in .NET 文章中了解更多信息。

您也可能会发现 Why doesn’t Excel quit? 文章很有帮助。

【讨论】:

  • 谢谢!那+约瑟夫的想法解决了这个问题。我会发布代码来回答这个问题。非常感谢!
【解决方案2】:

@Joseph (Thread + Timing idea) 的回答和@Eugene (CG idea) 的回答让我找到了解决方案!代码如下:

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
     this.Application.WorkbookOpen += new Excel.AppEvents_WorkbookOpenEventHandler(Application_WorkbookOpen);
}
        
void Application_WorkbookOpen(Excel.Workbook Wb)
{
     // Wb.Close(); // DOESN'T WORK
     Thread t = new Thread(() => this.ReleaseWorkbook(ref Wb)); // ReleaseWorkook doesn't work in the current thread
     t.Start();
}

void ReleaseWorkbook(ref Excel.Workbook Wb)
{
    Wb.Close(false, Type.Missing, Type.Missing); 
    Wb = null; 
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

【讨论】:

  • 不调用 GC 代码,你也许可以逃脱:System.Runtime.InteropServices.Marshal.ReleaseComObject(Wb); 但是,当从单独的线程调用时,这可能不起作用。 IOW,你也许可以让Wb.Close(); System.Runtime.InteropServices.Marshal.ReleaseComObject(Wb) 工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多