web-dev-qa-db-ja.com

Excelワークブックが開いているかどうかを確認する

Excelワークブック(DataSheet.xlsなど)が開いている(使用中)かどうかを確認する方法はありますか?そのワークブックを開いたら閉じたいと思います。

14
Rabin

正しい方法は、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への参照を自分で渡すことをお勧めします。

12
martin

これを試して:

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

   s.Close();

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

これにより、ファイルが排他的に開かれます。ファイルがすでに開いている場合は、例外がスローされます。そこで、ファイルを閉じて続行できます(試行)。

9
Iain Ward

トライキャッチの使用を避けるワンライナーに興味のある人のために...

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

もちろん、これを少し分割することもできます。重要な点は、try-catchの代わりにLINQを使用して、ワークブックの存在を確認することです。

注1:Marshal.GetActiveObject("Excel.Application")は、Excelのインスタンスが開いていない場合、エラーをスローします。したがって、他の方法で保証または処理されない限り、これは常にトライキャッチ内にある必要があります。

_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のインスタンスを1つだけ返します。 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()メソッドを ここ から適応させました。

1
u8it

excelアプリケーションが現在実行されていない場合、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;
}

ご清聴ありがとうございました。よろしく

0
heliar

これは特に良いことではありません。ファイルを開いて、失敗した場合は例外を調べます。 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の将来のリリースで変更される例外メッセージに関しては脆弱です。このような機能を、意図的にファイルをロックし、これを呼び出してメッセージが正しく検出されることを確認するテストで補完することをお勧めします。

0
Rob Levine

ワークシートとワークブックオブジェクトがある場合は、親チェックを実行できます

if(sheet.Parent ==ワークブック)

0
Makatun

以下のこの関数は、Excelファイルが開いているかどうかを返します。

2番目の関数は、コードに使用する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
        }
    }   
}
0
Chandraprakash