检查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
我从这里改编了上面代码中的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; }
感谢您的关注。 问候