Проверка, открыта ли книга Excel - PullRequest
11 голосов
/ 01 июля 2010

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

Ответы [ 6 ]

12 голосов
/ 07 июля 2010

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

9 голосов
/ 01 июля 2010

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

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

   s.Close();

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

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

1 голос
/ 21 июля 2016

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

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"))
{
   ....
}

или

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() в приведенном выше коде с здесь .

0 голосов
/ 18 апреля 2017

Ответ Мартина не работает, если приложение 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;
}

Спасибо за ваше внимание. Привет

0 голосов
/ 12 апреля 2015

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

if (sheet.Parent == workbook)

0 голосов
/ 01 июля 2010

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

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

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

...