Проверка, открыта ли рабочая книга Excel

Есть ли способ узнать, открыта (используется) рабочая книга Excel, скажем, DataSheet.xls? Я хотел бы закрыть эту книгу, если она открыта.


person Rabin    schedule 01.07.2010    source источник


Ответы (7)


Правильный способ — изучить объект 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

Другими словами, рабочие книги — это массив (или, в терминах VBA, коллекция) всех открытых рабочих книг.

В 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 самостоятельно.

person martin    schedule 06.07.2010
comment
Версия C # не работает для меня в этот день. Всегда вызывайте исключение и возвращайте false. Решение w69rdy помогло мне. - person Chris R.; 16.03.2017
comment
Что такое ссылка для Excel.Application? - person Devid; 16.02.2018
comment
COM -> Библиотека объектов Microsoft Excel xx.xx - person Andrey Baulin; 26.02.2018

Попробуй это:

try
{
   Stream s = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None);

   s.Close();

   return true;
}
catch (Exception)
{
   return false;
}

Это попытается открыть файл исключительно. Если файл уже открыт, он выдаст исключение, после чего вы сможете (попытаться) закрыть его и продолжить.

person Iain Ward    schedule 01.07.2010
comment
Это также вызовет ошибки по другим проблемам, не обязательно, что рабочая книга открыта. - person cjk; 01.07.2010
comment
Да, поэтому вы проверяете сообщение об ошибке, чтобы увидеть, является ли оно тем сообщением об ошибке, которое вы ищете, если оно не просто повторно выдает исключение! - person Iain Ward; 01.07.2010
comment
Это единственный способ сделать это, если у вас нет лучшего предложения, ск?? - person Iain Ward; 01.07.2010
comment
Если бы я делал это вручную, я бы попытался открыть файл в Excel, а затем посмотреть, появится ли диалоговое окно, сообщающее мне, что он открыт кем-то другим. Лучшим ответом было бы автоматизировать это. - person cjk; 01.07.2010
comment
ИМХО, это идеальный метод. Marshal.GetActiveObject получает только объекты, а что, если excel открыт в другом объекте? Отличное решение. +1 - person Sangram Nandkhile; 02.04.2013

Для всех, кто заинтересован в одном лайнере, который избегает использования попытки-уловить...

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

Или с полными именами...

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

Конечно, вы можете немного разделить это. Главное — использовать LINQ вместо try-catch для проверки существования рабочей книги.

Примечание 1. Marshal.GetActiveObject("Excel.Application") выдает ошибку, если ни один экземпляр Excel не открыт. Поэтому, если иное не гарантируется или не обрабатывается, это всегда должно быть в пределах try-catch.

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

Примечание 2. Marshal.GetActiveObject("Excel.Application") возвращает только один экземпляр Excel. Если вам нужно найти любой возможный экземпляр Excel, то приведенный ниже код может быть лучшей альтернативой.


Лучшая альтернатива

Если вы не возражаете против добавления вспомогательного класса, приведенный ниже код может быть лучшей альтернативой. Помимо возможности поиска в любом открытом экземпляре Excel, он также позволяет проверить полный путь и вернуть фактический объект книги, если он найден. Это также позволяет избежать выдачи ошибки, если экземпляр Excel не открыт.

использование будет таким...

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

or

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<RunningObject> GetRunningObjects()
    {
        // Get the table.
        List<RunningObject> roList = new List<RunningObject>();
        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<object> names = new List<object>();
        List<object> books = new List<object>();
        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() в приведенном выше коде из здесь. .

person u8it    schedule 21.07.2016

Это не особенно приятно — мы попробуем открыть файл и проверим исключение, если оно не сработает. Я не уверен, что у вас есть другие варианты на С#.

Однако важно обрабатывать только правильное исключение: в основном мы пытаемся открыть файл без разрешения на совместное использование. Если это не удается, И мы получаем правильный тип исключения И мы получаем правильное сообщение в исключении, то мы знаем, что оно открыто.

// 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. Вы можете дополнить такую ​​функциональность тестом, который намеренно блокирует файл, а затем вызывает его для проверки правильности обнаружения сообщения.

person Rob Levine    schedule 01.07.2010
comment
Это не работает за пределами английской среды !! - person Chris R.; 16.03.2017

Если у вас есть объекты рабочего листа и рабочей книги, вы можете выполнить родительскую проверку.

если (лист. Родитель == рабочая книга)

person Makatun    schedule 12.04.2015

Ответ Мартина не работает, если приложение Excel в данный момент не запущено. Вы можете изменить код следующим образом:

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

Спасибо за внимание. С уважением

person heliar    schedule 18.04.2017

Эта функция ниже вернет вам, открыт ли файл Excel или нет.

Вторая функция предоставит вам приложение Excel, рабочую книгу и лист, которые будут использоваться для вашего кода.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using wf = System.Windows.Forms;
using xl = Microsoft.Office.Interop.Excel;

public static class ExcelTest
{   
    public xl.Application xlApp = null;
    public xl.Workbook xlWb = null;
    public xl.Worksheet xlWs = null;

    public static bool IsXlFileOpen(string xlFileName)
    {       
        try
        {       
            if (!File.Exists(xlFileName))
            {
                wf.MessageBox.Show("Excel File does not exists!");
                return false;
            }

            try
            {
                xlApp = (xl.Application)Marshal.GetActiveObject("Excel.Application");
            }
            catch (Exception ex)
            {
                return false;
            }

            foreach (xl.Workbook wb in xlApp.Workbooks)
            {
                if (wb.FullName == xlFileName)
                {
                    xlWb = wb;
                    return true;
                }
            }

            return false;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    public static void GetXlSheet(string xlFileName,
                                    string xlSheetName)
    {
        try
        {
            if (!File.Exists(xlFileName))
            {
                wf.MessageBox.Show("Excel File does not exists!");
                return false;
            }

            xlApp = (xl.Application)Marshal.GetActiveObject("Excel.Application");
            foreach (xl.Workbook wb in xlApp.Workbooks)
            {
                if (wb.FullName == xlFileName)
                {
                    if (!xlWb
                        .Sheets
                        .Cast<xl.Worksheet>()
                        .Select(s => s.Name)
                        .Contains(xlSheetName))
                    {
                        wf.MessageBox.Show("Sheet name does not exist in the Excel workbook!");
                        return;
                    }
                    xlWs = xlWb.Sheets[xlSheetName];
                }
            }
        }
        catch (Exception ex)
        {
            // catch errors
        }
    }   
}
person Chandraprakash    schedule 31.10.2019