检查Excel工作簿是否已打开

有没有办法查看Excel工作簿,比如说DataSheet.xls是否已打开(正在使用中)? 如果它被打开我想关闭该工作簿。

正确的方法是检查Application.Workbooks对象。 在VBA中你会写:

Dim wb as Workbook On Error Resume Next '//this is VBA way of saying "try"' Set wb = Application.Workbooks(wbookName) If err.Number = 9 then '//this is VBA way of saying "catch"' 'the file is not opened...' End If 

换句话说,Workbooks是所有打开的工作簿的数组(或VBA术语,Collection)。

在C#中,以下代码有效:

  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; } 

您可能希望自己将引用传递给Excel.Application。

试试这个:

 try { Stream s = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None); s.Close(); return true; } catch (Exception) { return false; } 

这将尝试独占打开文件。 如果文件已经打开,它将抛出exception,然后您可以(尝试)关闭它并继续。

对于任何对单线运输感兴趣的人,避免使用试用捕捉……

 bool wbOpened = ((Application)Marshal.GetActiveObject("Excel.Application")).Workbooks.Cast().FirstOrDefault(x => x.Name == "Some Workbook.xlsx") != null; 

或者使用完全合格的名字……

 bool wbOpened = ((Microsoft.Office.Interop.Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application")).Workbooks.Cast().FirstOrDefault(x => x.Name == "Some Workbook.xlsx") != null; 

当然,你可能想把它分开一点。 重点是使用LINQ而不是try-catch来检查工作簿的存在。

注1:如果没有打开Excel实例, Marshal.GetActiveObject("Excel.Application")将抛出错误。 因此,除非另有保证或处理,否则应始终在尝试中。

 bool wbOpened = false; try { wbOpened = ((Application)Marshal.GetActiveObject("Excel.Application")).Workbooks.Cast().FirstOrDefault(x => x.Name == "Some Workbook.xlsx") != null; } catch { ... } 

注意2: Marshal.GetActiveObject("Excel.Application")只返回一个Excel实例。 如果您需要搜索任何可能的Excel实例,那么下面的代码可能是更好的选择。


更好的选择

如果您不介意添加帮助程序类,则下面的代码可能是更好的选择。 除了能够搜索任何打开的Excel实例之外,它还允许您检查完整路径并返回实际的工作簿对象(如果找到)。 如果没有打开Excel实例,它还可以避免抛出错误。

用法就像这样……

 If (IsOpenedWB_ByName("MyWB.xlsx")) { .... } 

要么

 Workbook wb = GetOpenedWB_ByPath("C:\MyWB.xlsx") if (wb.obj == null) //If null then Workbook is not already opened { ... } 

 using System; using System.Collections.Generic; using System.Linq; using Microsoft.Office.Interop.Excel; using System.Runtime.InteropServices.ComTypes; public class WBHelper { public static bool IsOpenedWB_ByName(string wbName) { return (GetOpenedWB_ByName(wbName) != null); } public static bool IsOpenedWB_ByPath(string wbPath) { return (GetOpenedWB_ByPath(wbPath) != null); } public static Workbook GetOpenedWB_ByName(string wbName) { return (Workbook)GetRunningObjects().FirstOrDefault(x => (System.IO.Path.GetFileName(x.Path) == wbName) && (x.Obj is Workbook)).Obj; } public static Workbook GetOpenedWB_ByPath(string wbPath) { return (Workbook)GetRunningObjects().FirstOrDefault(x => (x.Path == wbPath) && (x.Obj is Workbook)).Obj; } public static List GetRunningObjects() { // Get the table. List roList = new List(); IBindCtx bc; CreateBindCtx(0, out bc); IRunningObjectTable runningObjectTable; bc.GetRunningObjectTable(out runningObjectTable); IEnumMoniker monikerEnumerator; runningObjectTable.EnumRunning(out monikerEnumerator); monikerEnumerator.Reset(); // Enumerate and fill list IMoniker[] monikers = new IMoniker[1]; IntPtr numFetched = IntPtr.Zero; List names = new List(); List books = new List(); while (monikerEnumerator.Next(1, monikers, numFetched) == 0) { RunningObject running; monikers[0].GetDisplayName(bc, null, out running.Path); runningObjectTable.GetObject(monikers[0], out running.Obj); roList.Add(running); } return roList; } public struct RunningObject { public string Path; public object Obj; } [System.Runtime.InteropServices.DllImport("ole32.dll")] static extern void CreateBindCtx(int a, out IBindCtx b); } 

我从这里改编了上面代码中的GetRunningObjects()方法。

这不是特别好 – 我们将尝试打开文件并检查exception是否失败。 我不确定你在C#中还有其他选择。

但是,重要的是只处理正确的exception:基本上我们尝试打开文件,不允许共享。 如果失败,我们得到正确的exception类型并在exception中得到正确的消息,然后我们就知道它是开放的。

 // open the file with no sharing semantics (FileShare.None) using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None)) { try { stream.ReadByte(); return false; } catch (IOException ex) { // catch ONLY the exception we are interested in, and check the message too if (ex.Message != null && ex.Message.Contains("The process cannot access the file")); { return true; } // if the message was incorrect, this was not the IOException we were looking for. Rethrow it. throw; } } 

显然,对于在.Net的未来版本中更改的exception消息,这种方法很脆弱。 您可能希望通过有意锁定文件的测试来补充此类function,然后调用此选项以检查它是否正确检测到该消息。

如果您有工作表和工作簿对象,则可以进行父检查

if(sheet.Parent == workbook)

如果Excel Application当前未运行,则martin的答案不起作用。 您可能希望修改代码如下:

 static bool IsOpened(string wbook) { bool isOpened = true; Excel.Application exApp; try { // place the following line here : exApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"); // because it throws an exception if Excel is not running. exApp.Workbooks.get_Item(wbook); } catch (Exception) { isOpened = false; } return isOpened; } 

感谢您的关注。 问候