ブックが開いているかのチェック方法は2種類
VBAでブックを開く処理を行うことがありますが、すでに開いている場合はエラーとなってしまいます。そのため事前に開いているかどうかの判定が必要になります。
チェック方法には2種類あります。
1つはここで紹介する高速で確実にチェックする方法です。こちらをおすすめします。
もう1つは一般的に紹介されているWorkbooksオブジェクトを使った方法です。一応こちらも紹介しますがおすすめしません。
高速で確実なチェック方法
ソースコード
以下のブックが開いているかチェックするマクロは、追記モードでテキストファイルとして開くとOpen済みの場合はエラーになることを利用しています。ブックのファイルサイズに関係ないため高速です。また、指定ファイルパスに対して直接チェックが働くため、Excelが複数起動していても問題なく動作します。
引数にファイルパスを指定し、戻り値はブックが開いていればTrue、開いてなければFalseを返します。
8行目の「Err.Number > 0」ですが、開いているブックがあるとエラー番号70が返るため「Err.Number = 70」でも構いません。70は書き込み出来ない場合のエラーになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Function IsBookOpened(a_sFilePath) As Boolean On Error Resume Next '// 保存済みのブックか判定 Open a_sFilePath For Append As #1 Close #1 If Err.Number > 0 Then '// 既に開かれている場合 IsBookOpened = True Else '// 開かれていない場合 IsBookOpened = False End If End Function |
事前チェック
ブックが開いているかをチェックする前に、2つのチェックを行っておくことをお勧めします。
1つはそのブックが存在しているかのチェックで、Openメソッドで「エラー76 パスが見つかりません。」の回避するために利用します。もう1つはブックが読み取り専用かどうかのチェックで、Openメソッドで「エラー75 パス名が無効です。」を回避するために利用します。
もし存在しないファイルや読み取り専用のファイルであった場合、上のIsBookOpened関数の判定が正しく行われません。
それぞれのチェックは以下をご参照ください。
書き方としてはこんな感じになります。各関数はリンク先のをそのまま使ってますので詳細はリンク先をご参照ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Sub CheckBookOpened() Dim sFilePath Dim ret As Boolean sFilePath = "C:\test\a.xlsx" '// ファイル存在チェック ret = IsExistFileDir(sFilePath) If (ret = False) Then Debug.Print "ファイルが存在しない" Exit Sub End If '// 読み取り専用チェック ret = IsReadOnly(sFilePath) If (ret = True) Then Debug.Print "読み取り専用である" Exit Sub End If '// 開いているかチェック ret = IsBookOpened(sFilePath) If (ret = True) Then Debug.Print "開いている" Exit Sub End If Debug.Print "開くことができるブックです" End Sub |
使い方
上の関数の使い方は以下のような感じになります。
例として、ブックを開く関数のOpenExcelBookを紹介します。
11行目でファイルパスを渡してブックが開いているかをチェックします。
14行目のIf文で取得したブックのオープン状態の判定を行います。
そのブックが開いていればそれを選択し、閉じていれば開きます。
GetFileName関数はファイルパスからファイル名を取得する関数でちょっと必要だったので書いてます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Sub OpenExcelBook(sFilePath) Dim sFileName Dim bOpened As Boolean Dim oBook As Workbook Dim sSheetName Dim sCell sSheetName = "Sheet1" sCell = "A1" bOpened = IsBookOpened(sFilePath) '// ファイルが既に開いている場合 If (bOpened = True) Then Call GetFileName(sFilePath, sFileName) Workbooks(sFileName).Activate Workbooks(sFileName).Sheets(sSheetName).Select Workbooks(sFileName).Sheets(sSheetName).Range(sCell).Select '// ファイルが開いていない場合 Else Set oBook = Workbooks.Open(Filename:=sFilePath, UpdateLinks:=0, ReadOnly:=False, IgnoreReadOnlyRecommended:=True) oBook.Activate oBook.Sheets(sSheetName).Select oBook.Sheets(sSheetName).Range(sCell).Select End If End Sub Sub GetFileName(a_sFilePath, a_sFileName) Dim v v = Split(a_sFilePath, "\") a_sFileName = v(UBound(v)) End Sub |
おすすめしないチェック方法
一般的に紹介されるコードは以下のものが多いですが、この方法はおすすめしません。
チェックするブックのファイルパスが、現在開いているワークブックの中に同じファイルパスがあるかをチェックする方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
funciton NotRecommend_IsBookOpened(sBookPath) Dim book As Workbook Dim ret As Boolean '// True:オープン済み、False:未オープン '// 初期値=未オープン ret = False '// ここで指定するWorkbooksはこのマクロを実行するExcelオブジェクトに '// 含まれるものに限定されるため、 '// 他に起動しているExcelオブジェクトがある場合(Excelを複数起動している場合)は '// 他方でブックが開いていても「開かれていない」と判定します。 For Each book In Workbooks '// ブックの名前が一致する場合は既に開かれているとみなす→一致しない間は無駄な処理 If sBookPath = book.FullName Then ret = True Exit For End If Next NotRecommend_IsBookOpened = ret End Sub |
ぱっと見た感じでは問題がないように思うため、よく紹介されているのでしょうけど、この方法は2つの問題があります。
問題1:開いているブックの数に比例して遅くなる
このチェック方法では処理速度が一定せず、開いているブックの数に比例して遅くなります。
複数のブックが開いている中で、ブックの名前を1つずつチェックしていくループ処理を行っていますが、一致しない関係ないファイルもチェックしていることになり無駄な処理をしていることになります。
問題2:正しくチェックできない場合がある
もう1つの問題はこのチェック方法にはチェックが効かないという致命的な欠陥があることです。
このチェック方法はExcelが1つしか起動していないことを前提としているため、Excelが複数起動している場合にチェック処理を行うExcelプロセスとは別のExcelでチェック対象のブックが開いていてもチェックが働きません。
開いているExcelプロセスを列挙することはできますが、ここまでくると面倒ですし本末転倒ですね。