はじめに:なぜブックの存在チェックが重要なのか
Excel VBAを使いこなす上で、特定のブックが開かれているかどうかを事前に確認する処理は、非常に頻繁に登場する場面です。例えば、
* 他のブックを参照するマクロを実行する前に、参照先のブックが開いているか確認し、開かれていなければユーザーに通知する。
* 特定のブックが既に開いている場合は、何もせず終了する。
* 複数のブックを操作する際に、重複して開かないように制御する。
といったシナリオが考えられます。このような際、「対象のブックが開かれているかどうかわからない」という状態は、予期せぬエラーや処理の誤動作を引き起こす原因となります。
この問題を解決するために、Excel VBAにはブックが開かれているかどうかを判定する便利な機能が用意されています。本記事では、この「特定のブックが開かれているかどうかのチェック」をマスターし、より堅牢で信頼性の高いVBAコードを作成するための方法を、具体的なサンプルコードと実務的なアドバイスを交えながら徹底解説します。
ブックの存在チェックの基本:Workbooksコレクションを活用する
Excel VBAにおいて、現在開かれている全てのブックは `Workbooks` コレクションという形で管理されています。この `Workbooks` コレクションは、開かれているブックのリストのようなものです。
特定のブックが開かれているかどうかを調べるには、この `Workbooks` コレクションの中に、目的のブック名が存在するかどうかを確認すれば良いのです。
具体的には、`Workbooks` コレクションをループ処理し、各ブックの `Name` プロパティ(ブックの名前)が目的のブック名と一致するかどうかを比較します。
ループ処理による判定方法
最も基本的なアプローチは、`For Each` ループを使って `Workbooks` コレクションを順番に見ていく方法です。
Sub CheckWorkbookExists_Loop()
Dim targetWorkbookName As String
Dim wb As Workbook
Dim found As Boolean
‘ チェックしたいブックの名前を指定します
targetWorkbookName = “MyTargetBook.xlsx” ‘ 例: “売上データ.xlsx” など
found = False ‘ 最初は「見つからなかった」状態に初期化
‘ 現在開かれている全てのブックをループで確認します
For Each wb In Workbooks
‘ ブックの名前と目的の名前を比較します(大文字・小文字は区別しません)
If StrComp(wb.Name, targetWorkbookName, vbTextCompare) = 0 Then
found = True ‘ 見つかったらフラグを立てます
Exit For ‘ 見つかったのでループを抜けます(効率化)
End If
Next wb
‘ 結果を表示します
If found Then
MsgBox targetWorkbookName & ” は開かれています。”, vbInformation
Else
MsgBox targetWorkbookName & ” は開かれていません。”, vbExclamation
End If
End Sub
このコードでは、`targetWorkbookName` 変数にチェックしたいブックの名前を設定します。そして、`For Each wb In Workbooks` で開かれているブックを一つずつ取り出し、`wb.Name` でそのブック名を取得します。
`StrComp` 関数は、2つの文字列を比較する関数です。`vbTextCompare` を指定することで、大文字・小文字を区別せずに比較することができます。比較結果が `0` であれば、文字列は一致したと判断できます。
`found` という `Boolean` 型の変数(True/Falseで結果を保持する変数)を用意し、ブックが見つかったら `True` に設定します。見つかった時点で `Exit For` でループを抜けることで、不要な処理を省き、コードの実行速度を向上させることができます。
最後に、`found` 変数の値によって、メッセージボックスで結果を表示します。
`Dir` 関数と組み合わせる方法(ファイルパス指定の場合)
もし、ブック名だけでなく、そのブックのフルパス(例: “C:\Users\YourName\Documents\MyTargetBook.xlsx”)が分かっている場合は、`Dir` 関数を組み合わせることで、より直接的にファイルが存在するかどうかをチェックできます。
ただし、`Dir` 関数は「ファイルが存在するかどうか」をチェックするものであり、「Excelで開かれているか」を直接チェックするものではありません。そのため、この方法は「指定したパスにファイルが存在し、かつ、それがExcelで開かれているか」という複合的なチェックには使えません。
「指定したパスにファイルが存在するか」のみをチェックしたい場合に有効です。
Sub CheckFileExists_Dir()
Dim targetFilePath As String
Dim fileName As String
‘ チェックしたいブックのフルパスを指定します
targetFilePath = “C:\Users\YourName\Documents\MyTargetBook.xlsx” ‘ 例: “D:\データ\月次集計.xlsm”
‘ Dir関数でファイルが存在するか確認します
fileName = Dir(targetFilePath)
If fileName <> “” Then
MsgBox targetFilePath & ” は存在します。”, vbInformation
‘ ここでさらに、Excelで開かれているかどうかのチェックを追加することも可能ですが、
‘ それには前述のWorkbooksコレクションを使った方法と組み合わせる必要があります。
Else
MsgBox targetFilePath & ” は存在しません。”, vbExclamation
End If
End Sub
この `Dir` 関数による方法は、ファイルシステム上の存在確認であり、Excelアプリケーション内のブックの開閉状態とは直接関係ありません。したがって、VBAで「開かれているブック」を判定する目的では、`Workbooks` コレクションを使う方法が主となります。
より効率的な方法:Workbooks.Count と Workbooks(ブック名) を使う
ループ処理は確実ですが、ブックの数が多い場合や、頻繁にチェックを行う場合には、少し非効率になることがあります。
より効率的に、かつ簡潔にブックの存在をチェックする方法として、`Workbooks.Count` プロパティや `Workbooks(ブック名)` というインデックスアクセスを利用する方法があります。
`On Error Resume Next` を活用した判定方法
`Workbooks(ブック名)` という形でブックにアクセスしようとした際に、もしそのブックが開かれていなければ、エラーが発生します。このエラーを `On Error Resume Next` ステートメントで捕捉し、エラーが発生したかどうかでブックの存在を判定する方法が、最も一般的で効率的な方法の一つです。
Sub CheckWorkbookExists_ErrorHandling()
Dim targetWorkbookName As String
Dim wb As Workbook
Dim found As Boolean
‘ チェックしたいブックの名前を指定します
targetWorkbookName = “MyTargetBook.xlsx” ‘ 例: “集計表.xlsx”
found = False ‘ 初期化
‘ エラーが発生しても処理を続行するように設定します
On Error Resume Next
‘ 指定した名前のブックにアクセスを試みます
‘ もしブックが開かれていれば、wbにはそのWorkbookオブジェクトが代入されます
‘ 開かれていなければ、エラーが発生し、wbはNothingになります
Set wb = Workbooks(targetWorkbookName)
‘ エラーが発生したかどうかを確認します
‘ Err.Number が 0 でなければエラーが発生したことになります
If Err.Number = 0 Then
‘ エラーが発生しなかった = ブックは開かれている
found = True
‘ オブジェクト変数を解放します(良い習慣です)
Set wb = Nothing
Else
‘ エラーが発生した = ブックは開かれていない
found = False
‘ エラーオブジェクトをクリアします
Err.Clear
End If
‘ エラーハンドリングを元に戻します(重要!)
On Error GoTo 0
‘ 結果を表示します
If found Then
MsgBox targetWorkbookName & ” は開かれています。”, vbInformation
Else
MsgBox targetWorkbookName & ” は開かれていません。”, vbExclamation
End If
End Sub
このコードのポイントは以下の通りです。
1. **`On Error Resume Next`**: このステートメント以降、VBAコードでエラーが発生しても、プログラムの実行を停止せずに次の行に進みます。
2. **`Set wb = Workbooks(targetWorkbookName)`**: ここで、指定した名前のブックにアクセスします。
* もし `targetWorkbookName` という名前のブックが開かれていれば、`wb` 変数にはそのブックへの参照(オブジェクト)が格納されます。
* もし開かれていなければ、Excel VBAは「指定されたインデックスが見つかりません」といったエラーを発生させます。しかし、`On Error Resume Next` のおかげで、プログラムは停止せず、次の行に進みます。このとき、`wb` 変数には何も代入されず、`Nothing` という値が保持されます。
3. **`If Err.Number = 0 Then`**: VBAでは、エラーが発生すると `Err` オブジェクトにエラー情報が格納されます。`Err.Number` はエラーコードを表し、エラーが発生しなかった場合は `0` になります。
* `Err.Number` が `0` なら、エラーは発生せず、`Workbooks(targetWorkbookName)` へのアクセスは成功した、つまりブックは開かれていると判断できます。
* `Err.Number` が `0` でなければ、エラーが発生した、つまりブックは開かれていないと判断できます。
4. **`Set wb = Nothing`**: オブジェクト変数は、不要になったら `Nothing` を代入して解放するのが良い習慣です。これにより、メモリの解放が促され、予期せぬ不具合を防ぐことができます。
5. **`Err.Clear`**: エラー情報をクリアしておきます。
6. **`On Error GoTo 0`**: エラーハンドリングの設定を元に戻します。これをしないと、以降のコードで発生したエラーもすべて無視されてしまい、デバッグが困難になります。**この行は非常に重要です。**
この `On Error Resume Next` を使う方法は、コードが短く、実行速度も速いため、非常に広く使われています。
ブック名だけでなく、ファイルパスで存在チェックしたい場合
前述した `Workbooks(ブック名)` による方法は、ブック名が正確に分かっている場合に有効ですが、ブック名が似ていたり、複数のブックが同じ名前で開かれていたり(通常はありえませんが)、あるいはファイルパスで管理したい場合には、少し工夫が必要です。
ファイルパスでブックを特定し、それが開かれているかを確認するには、`Workbooks` コレクションをループし、各ブックの `FullName` プロパティ(フルパス)と比較する方法が考えられます。
Sub CheckWorkbookExists_FullPath()
Dim targetFilePath As String
Dim wb As Workbook
Dim found As Boolean
‘ チェックしたいブックのフルパスを指定します
targetFilePath = “C:\Users\YourName\Documents\MyTargetBook.xlsx” ‘ 例: “D:\データ\月次集計.xlsm”
found = False ‘ 初期化
‘ 現在開かれている全てのブックをループで確認します
For Each wb In Workbooks
‘ ブックのフルパスと目的のパスを比較します(大文字・小文字、パス区切り文字の違いを考慮)
If StrComp(wb.FullName, targetFilePath, vbTextCompare) = 0 Then
found = True ‘ 見つかったらフラグを立てます
Exit For ‘ 見つかったのでループを抜けます
End If
Next wb
‘ 結果を表示します
If found Then
MsgBox targetFilePath & ” は開かれています。”, vbInformation
Else
MsgBox targetFilePath & ” は開かれていません。”, vbExclamation
End If
End Sub
このコードでは、`wb.FullName` プロパティを使います。これはブックのフルパスを返します。`StrComp` 関数で大文字・小文字を区別せずに比較することで、より堅牢なチェックが可能になります。
ただし、パスの区切り文字(`\` か `/` か)や、末尾のスラッシュの有無などで微妙に異なる場合があり、厳密な比較には注意が必要です。もしパスの正規化(例: 末尾に必ず `\` を付ける、`\` を `/` に統一するなど)を行ってから比較すると、より安全です。
実務アドバイス:より安全で使いやすいVBAコードのために
ここからは、実際の業務でVBAコードを作成する際に役立つ、いくつかの実務的なアドバイスをご紹介します。
1. ブック名やパスは変数で管理する
コードの中に直接ブック名やパスを記述するのではなく、変数として定義しましょう。
Const TARGET_BOOK_NAME As String = “売上データ_2023.xlsx” ‘ 定数として宣言
‘ または
Dim targetBookPath As String
targetBookPath = ThisWorkbook.Path & “\関連ファイル\設定.xlsx”
こうすることで、後からブック名やパスを変更する際に、コード全体を探し回る必要がなく、変数定義部分の修正だけで済みます。特に、ブック名が頻繁に変わる場合や、複数のブックを操作する場合には、この習慣が非常に重要になります。
`Const`(定数)を使うと、一度値を代入したら変更できなくなるため、誤ってコードの途中で変更してしまうことを防げます。
2. ユーザーに選択させる柔軟性を持たせる
常に同じブックをチェックするのではなく、ユーザーが実行時にチェックしたいブックを選択できるようにすると、汎用性が高まります。
Sub CheckUserSelectedWorkbook()
Dim fd As FileDialog
Dim selectedFile As Variant
Dim targetWorkbookName As String
Dim wb As Workbook
Dim found As Boolean
‘ ファイル選択ダイアログの設定
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
.Title = “開かれているか確認したいブックを選択してください”
.AllowMultiSelect = False ‘ 複数選択は不可
.Filters.Clear
.Filters.Add “Excelブック”, “*.xls;*.xlsx;*.xlsm”
‘ ダイアログを表示し、ユーザーがファイルを選択したら
If .Show = -1 Then ‘ -1 は「OK」ボタンが押されたことを示す
selectedFile = .SelectedItems(1) ‘ 選択されたファイルのパスを取得
‘ ファイルパスからブック名だけを抽出(例: C:\…\Book1.xlsx -> Book1.xlsx)
targetWorkbookName = Mid(selectedFile, InStrRev(selectedFile, “\”) + 1)
found = False
On Error Resume Next
Set wb = Workbooks(targetWorkbookName)
If Err.Number = 0 Then
found = True
Set wb = Nothing
Else
Err.Clear
End If
On Error GoTo 0
If found Then
MsgBox targetWorkbookName & ” は開かれています。”, vbInformation
Else
MsgBox targetWorkbookName & ” は開かれていません。”, vbExclamation
End If
Else
MsgBox “ファイル選択がキャンセルされました。”, vbExclamation
End If
End With
Set fd = Nothing ‘ オブジェクト解放
End Sub
このコードでは、`FileDialog` オブジェクトを使って、ユーザーにExcelブックを選択させます。選択されたファイルのパスからブック名だけを抽出し、そのブックが開かれているかをチェックしています。
3. エラーハンドリングを適切に行う
`On Error Resume Next` は便利ですが、多用するとデバッグが困難になります。ブックの存在チェックのように、エラーが発生することが「正常な状態」として期待できる場合に限定して使用し、それ以外の場所では `On Error GoTo 0` を忘れずに設定しましょう。
また、より複雑な処理を行う場合は、特定のエラーコードに対して個別の処理を行う `On Error GoTo ErrorHandler` のような、より高度なエラーハンドリングを検討することも重要です。
4. ブック名の大文字・小文字、拡張子の違いに注意する
Windows環境では、ファイル名の大文字・小文字は通常区別されませんが、VBAの文字列比較ではデフォルトで区別されます。`StrComp` 関数の `vbTextCompare` オプションを使うか、`UCase` 関数などで両方の文字列を大文字にしてから比較することで、この問題を回避できます。
また、`.xls`、`.xlsx`、`.xlsm` など、拡張子の違いも考慮する必要があるかもしれません。もし拡張子を区別せずにチェックしたい場合は、`Left` 関数や `InStrRev` 関数などを組み合わせて、拡張子部分を除いたファイル名で比較するなどの工夫が必要です。
例:拡張子を除いたファイル名で比較
Dim bookNameWithoutExt As String
Dim wbNameWithoutExt As String
bookNameWithoutExt = Left(targetWorkbookName, InStrRev(targetWorkbookName, “.”) – 1)
For Each wb In Workbooks
wbNameWithoutExt = Left(wb.Name, InStrRev(wb.Name, “.”) – 1)
If StrComp(wbNameWithoutExt, bookNameWithoutExt, vbTextCompare) = 0 Then
‘ … 処理 …
End If
Next wb
5. 複数ブックの存在チェックをまとめて行う
複数のブックが存在するかどうかをチェックする必要がある場合は、まとめて処理する関数を作成すると便利です。
Function IsWorkbookOpen(workbookName As String) As Boolean
Dim wb As Workbook
IsWorkbookOpen = False ‘ デフォルトは False (開かれていない)
On Error Resume Next
Set wb = Workbooks(workbookName)
If Err.Number = 0 Then
IsWorkbookOpen = True ‘ 開かれていれば True にする
Set wb = Nothing
Else
Err.Clear
End If
On Error GoTo 0
End Function
Sub MainProcess()
Dim book1 As String
Dim book2 As String
book1 = “設定ファイル.xlsx”
book2 = “マスターデータ.xlsx”
If IsWorkbookOpen(book1) Then
MsgBox book1 & ” は開かれています。”, vbInformation
‘ book1 が開かれている場合の処理
Else
MsgBox book1 & ” は開かれていません。処理を中断します。”, vbExclamation
Exit Sub ‘ 処理を中断
End If
If IsWorkbookOpen(book2) Then
MsgBox book2 & ” は開かれています。”, vbInformation
‘ book2 が開かれている場合の処理
Else
MsgBox book2 & ” は開かれていません。処理を中断します。”, vbExclamation
Exit Sub ‘ 処理を中断
End If
‘ 両方のブックが開かれている場合のメイン処理…
End Sub
このように、ブックの存在チェックを独立した関数(`IsWorkbookOpen`)にしておくことで、メインの処理コードがスッキリし、可読性が向上します。
まとめ:堅牢なVBAコード作成への第一歩
本記事では、Excel VBAで特定のブックが開かれているかどうかを調べるための、いくつかの方法を解説しました。
* `Workbooks` コレクションをループして `Name` プロパティを比較する方法。
* `On Error Resume Next` を使い `Workbooks(ブック名)` へのアクセスを試みる効率的な方法。
* `FullName` プロパティを使ってファイルパスで比較する方法。
これらの方法を理解し、状況に応じて使い分けることで、より信頼性が高く、エラーの発生しにくいVBAコードを作成することができます。特に `On Error Resume Next` を利用した方法は、その簡潔さと実行速度から、実務で最も頻繁に利用されるテクニックと言えるでしょう。
今回ご紹介したブックの存在チェックは、VBAプログラミングの基礎でありながら、非常に強力なテクニックです。これをマスターすることで、あなたのVBAスキルは一段と向上し、より高度で洗練された自動化処理を実現できるようになるはずです。ぜひ、日々のVBA開発に活用してみてください。
