如何在Excel VSTO中检查工作表是否属于已关闭的工作簿?

5
如果我有一个对Worksheet的引用,并关闭它的父级Workbook,那么该引用不会消失。但我无法弄清楚如何检查以确保这些工作表不存在。检查null并不起作用。
示例:
Workbook book = Globals.ThisAddIn.Application.ActiveWorkbook;
Worksheet sheet = (Worksheet)book.Worksheets[1]; // Get first worksheet
book.Close(); // Close the workbook
bool isNull = sheet == null; // false, worksheet is not null
string name = sheet.Name; // throws a COM Exception

当我尝试访问该表格时,出现了以下异常:

System.Runtime.InteropServices.COMException was caught
  HResult=-2147221080
  Message=Exception from HRESULT: 0x800401A8
  Source=MyProject
  ErrorCode=-2147221080
  StackTrace:
       at Microsoft.Office.Interop.Excel._Worksheet.get_Name()
       at MyCode.test_Click(Object sender, RibbonControlEventArgs e) in c:\MyCode.cs:line 413
  InnerException: 

如果我可以检查工作簿删除事件,这个问题就不成问题了,但是Excel没有提供这样的事件(这真的很恼人)。

有没有一些方便的方法来确保我不使用这些工作表?

3个回答

3
我使用这个方法:

我使用这种方法:

        private void releaseObject(object obj)
    {
        try
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
            obj = null;
        }
        catch (Exception ex)
        {
            obj = null;
            MessageBox.Show("Exception Occured while releasing object " + ex.ToString());
        }
        finally
        {
            GC.Collect();
        }
    }

或者你可以尝试像这样的东西:
    static bool IsOpened(string wbook) 
{ 
    bool isOpened = true; 
    Excel.Application exApp; 
    exApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"); 
    try 
    { 
        exApp.Workbooks.get_Item(wbook); 
    } 
    catch (Exception) 
    { 
        isOpened = false; 
    } 
    return isOpened; 
} 

我怎么知道什么时候调用它呢?我有一些可能是打开的或者可能是关闭的工作表。如果它是打开的,我想对它做一些操作。如果它是关闭的,我就不想做任何操作。问题是我无法知道它是打开还是关闭。这种方法似乎只有在我知道它是关闭的情况下才有用。 - Kris Harper
我可以这样做,但我宁愿将其作为最后的手段。我一般认为异常不应该用于流程控制。 - Kris Harper
我同意,但我认为你不会经常使用这种方法,因此它不应影响你的应用程序性能。 另外,我尝试过 :) 希望你能想出更好的解决方案! - Andrew

3
如果其他解决方案失败,处理此问题的另一种方法是在工作簿打开后存储其名称,然后在引用工作表之前检查该名称是否存在于 Workbooks 集合中。按名称引用工作簿将起作用,因为每个Excel实例中只能有唯一命名的工作簿
public void Test()
{
    Workbook book = Globals.ThisAddIn.Application.ActiveWorkbook;
    string wbkName = book.Name; //get and store the workbook name somewhere
    Worksheet sheet = (Worksheet)book.Worksheets[1]; // Get first worksheet
    book.Close(); // Close the workbook
    bool isNull = sheet == null; // false, worksheet is not null
    string name;

    if (WorkbookExists(wbkName))
    {
        name = sheet.Name; // will NOT throw a COM Exception
    }
}

private bool WorkbookExists(string name)
{
    foreach (Microsoft.Office.Interop.Excel.Workbook wbk in Globals.ThisAddIn.Application.Workbooks)
    {
        if (wbk.Name == name)
        {
            return true;
        }
    }

    return false;
}

编辑:为了完整起见,这里提供一个辅助扩展方法:

public static bool SheetExists(this Excel.Workbook wbk, string sheetName)
{
    for (int i = 1; i <= wbk.Worksheets.Count; i++)
    {
        if (((Excel.Worksheet)wbk.Worksheets[i]).Name == sheetName)
        {
            return true;
        }
    }

    return false;
}

这个方法是可行的,但前提是我在那个时候已经有了“Workbook”对象。我的示例代码很简单,但实际上,我有几个方法只接受“Worksheet”对象。尽管如此,我还是会接受你的答案,因为我最终做了类似的事情。也就是说,我检查了Application.Workbooks.Count > 0。由于我只在没有打开工作簿时遇到了这个问题,所以这个检查就足够满足我的需求了。谢谢你的建议。 - Kris Harper

0

我没有尝试过这个,但你可以检查工作簿sheet.Parent是否存在于应用程序的Workbooks集合中。


不起作用。调用 sheet.Parent 会导致与调用 sheet.Name 相同的异常。不过想法不错。 - Kris Harper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接