【エクセルVBA】ワークブックを開くWorkbooks.Openの使い方とエラーの対処法
エクセルVBAで既存のワークブックを開くためには「Workbooks.Open」文を使います。
「Workbooks.Open」文は、単に使うだけなら簡単な一方で、何も考えずに使ってしまうと、次のようなエラーが発生しがちです。
- ファイルが存在していないためエラーになる
- 同一ファイルを既に開いていると、「abc.xlsxは既に開いています。2重に開くと、これまでの変更内容は破棄されます。abc.xlsxを開きますか?」というメッセージが出てしまう
- 同一名称の別ファイルを開いているため、正常に動作しない
そこで、このページでは「Workbooks.Open」文の使い方と、これらのトラブルの対処法を解説していきます。
この記事の目次
1. Workbooks.Open文の基本
Workbooks.Openは、次のように使います。
基本的な使用例
Sub OpenWorkbookTest()
Dim Filepath
Filepath = "C:¥temp¥test1.xlsx"
Dim targetWorkbook As Workbook
Set targetWorkbook = Workbooks.Open(Filepath)
' 対象ブックの 「Sheet1」シートの 「A1」セルの 値
targetWorkbook.Worksheets("Sheet1").Cells(1, 1).Value = "テスト書き込み"
End Sub
3行目で、開きたいブックのファイルパスを、変数「Filepath」に代入しています。
以下、変数「Filepath」を使って処理を書いていきます。
実際に、このプログラムを使うときは、3行目の「C:¥temp¥test1.xlsx」の部分を、エクセルブックのファイルパスに書き換えてください。
変数宣言+Workbooks.Openでブックを開く
5行目~6行目で、エクセルブックを開いています。
Dim targetWorkbook As Workbook
Set targetWorkbook = Workbooks.Open("C:¥temp¥test1.xlsx")
5行目で「ワークブックを操作」するための変数「targetWorkbook」を宣言しています。
6行目で「Workbooks.Open」文を使って、変数「Filepath」(=「C:¥temp¥test1.xlsx」)にあるエクセルブックを開いています。
そして、その「開いたエクセルブック」の情報を変数「targetWorkbook」に代入しています。
以降、「targetWorkbook」という変数を通じて、そのワークブックを操作することができます。
「Workbooks.Open」という文を、よく見ると「Workbooks.Open」というように「s」が付いていることに注意してください。
イメージ的には、《複数のWorkbookの管理をする「Workbooksオブジェクト」》に対して、新しいWorkbookを「Open」するように依頼する、と考えてみてください。
変数「targetWorkbook」を使って、ワークブックを操作する
' 対象ブックの 「Sheet1」シートの 「A1」セルの 値
targetWorkbook.Worksheets("Sheet1").Cells(1, 1).Value = "テスト書き込み"
このように、9行目で、
- 対象ワークブックの
- 「Sheet1」シートの
- 「A1」セルの「値」に
- 「テスト書き込み」という文字を
書き込んでいます。
2. Workbooks.Openでエラーが出る
このように「Workbooks.Open」文を使うだけなら、簡単です。
ただ、状況によっては様々なエラーが出るなど、うまく動かないケースもあります。
そこで、ここからは、Workbooks.Openでありがちなエラーなどの対処法を紹介していきます。
パターン1. 該当するファイルが存在しない
該当するファイルがないと「Workbooks.Open」はうまく動かず、エラーになってしまいます。
そこで「Workbooks.Open」を使う前に、ファイルが存在しているかチェックをかけましょう。
ファイルの存在チェックを追加
Sub OpenWorkbookTest_CheckBeforeOpen()
Dim Filepath
Filepath = "C:¥temp¥testNotExists.xlsx"
If Dir(Filepath) = "" Then
MsgBox "指定したファイルは存在していません"
Exit Sub
End If
Dim targetWorkbook As Workbook
Set targetWorkbook = Workbooks.Open(Filepath)
' 対象ブックの 「Sheet1」シートの 「A1」セルの 値
targetWorkbook.Worksheets("Sheet1").Cells(1, 1).Value = "テスト書き込み"
End Sub
さきほどのプログラムに、5行目~8行目の部分を追加しています。
Dir関数でファイルが存在しているかどうかを確認する
If Dir(Filepath) = "" Then
MsgBox "指定したファイルは存在していません"
Exit Sub
End If
ファイルが存在していないときには、Dir関数を使った結果が空欄になります。
そこでIf文を使って、ファイルが存在するかどうか確認を行い、ファイルがないときには、Msgboxでその旨を表示してプログラムの実行を中止します。
逆に、ファイルがあれば、10行目以降で実際にブックを開いて処理を行います(先ほどとまったく同じです)。
Dir関数の詳細
「Dir(Filepath)」という文は、次のような動作をします。
状況 | 結果 |
---|---|
ファイルが「ない」とき | (空欄) |
ファイルが「ある」とき | Filepathの内容がそのまま得られる |
たとえば、
- 「C:¥temp¥testNotExists.xlsx」は存在しない
- 「C:¥temp¥test1.xlsx」は存在している
というときに、イミディエイトウィンドウでDir関数を実行すると、次のようになります。
パターン2. 既に同じ「名前」のエクセルブックを開いている
エクセルの仕様で、既に同じ「名前」のエクセルブックを開いているときには、別のエクセルブックを開くことはできません。
そのため「Workbooks.Open」文を実行する前に、同名のエクセルブックを開いていないかを確認します。
「同名のエクセルブックが開けない」という制約は、ファイルパスは無関係に、ファイル名が同一かどうかだけで判定されます。
たとえば、下記のエクセルブックは、ファイルパスは違いますが、ファイル名が同一のため、すべて「同名」だと判定されます。
- 「C:¥test1.xlsx」
- 「C:¥temp¥test1.xlsx」
- 「D:¥temp¥test1.xlsx」
要は「test1.xlsx」の部分が同じなら、すべて「同じ名前」と判断されてしまうということです。
それでは、具体的なプログラムを見ていきます。
同名のファイルを開いていないかのチェックを追加
Sub OpenWorkbookTest_CheckBeforeOpen2()
Dim Filepath
Filepath = "C:¥temp¥test1.xlsx"
If Dir(Filepath) = "" Then
MsgBox "指定したファイルは存在していません"
Exit Sub
End If
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Dim Filename
Filename = FSO.GetFileName(Filepath)
'filenameと同名のエクセルブックを取得
Dim workbookWithSameName As Workbook
Set workbookWithSameName = getWorkbookByName(Filename)
'同名のエクセルブックがあるときはNothing「ではない」
If Not workbookWithSameName Is Nothing Then
MsgBox "同名のエクセルブックを開いているためファイルを開けません"
Exit Sub
End If
Dim targetWorkbook As Workbook
Set targetWorkbook = Workbooks.Open(Filepath)
' 対象ブックの 「Sheet1」シートの 「A1」セルの 値
targetWorkbook.Worksheets("Sheet1").Cells(1, 1).Value = "テスト書き込み"
End Sub
'以下は、https://www.excelspeedup.com/getworkbook/ からコピペ
Function getWorkbookByName(targetWorkbookName) As Workbook
Dim TempWorkbook As Workbook
For Each TempWorkbook In Workbooks
If TempWorkbook.Name = targetWorkbookName Then
Set getWorkbookByName = TempWorkbook
Exit Function
End If
Next
Set getWorkbookByName = Nothing
End Function
さきほどのプログラムに対して、10行目~24行目と34行目~を追加しています。
かなり、プログラムが長くなってしまいましたが、解説をしていきます。
ファイルパスからファイル名を取得
10行目~14行目では「ファイルパス」から「ファイル名」を抽出しています。
(例:「C:¥temp¥test1.xlsx」→「test1.xlsx」)
この抽出をするために「FilesystemObjectオブジェクト」の「GetFileNameメソッド」を使います。
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Dim Filename
Filename = FSO.GetFileName(Filepath)
- 10行目~11行目
-
FileSystemObjectを使える状態にしたものを、変数FSOに格納
- 13行目~14行目
-
変数FSO(=FileSystemObjectを使える状態にしたもの)の「GetFileNameメソッド」を呼び出し。
その結果、「変数Filepath」のファイルパスを変換して、ファイル名を「変数Filename」に代入。
これで、ファイルパスからファイル名を取得できました。
ファイル名と同名のワークブックを開いていないかを調べる
'filenameと同名のエクセルブックを取得
Dim workbookWithSameName As Workbook
Set workbookWithSameName = getWorkbookByName(Filename)
すでに、同名のワークブックを開いていないかを調べます。
18行目のgetWorkbookByNameプロシージャを実行すると、変数workbookWithSameNameには、次の値が代入されます。
同名のワークブックがある場合 | 該当するワークブックを操作するための情報 |
---|---|
同名のワークブックがない場合 | Nothing |
つまり、「変数workbookWithSameName」がNothingかどうかを判定すれば、同名のワークブックがあるかどうかがわかるのです。
18行目にある「getWorkbookByName(Filename)」で、34行目~45行目のFunctionプロシージャを呼び出しています。
そして、そのFunctionプロシージャの中で、同名のワークブックがないか検索する処理を行っています。
このFunctionプロシージャの具体的な動作は、下記ページで解説していますので、合わせてご覧ください。
同名のワークブックを開いているかどうかの判定
「変数workbookWithSameName」がNothingではない場合には、同名のエクセルブックを開いている状態だということがわかります。
そこで、If文を使って判定をします。
'同名のエクセルブックがあるときはNothing「ではない」
If Not workbookWithSameName Is Nothing Then
MsgBox "同名のエクセルブックを開いているためファイルを開けません"
Exit Sub
End If
21行目のIf文は「Not ... Is Nothing」と否定が2回入っているので読みにくいかもしれませんが、これで「Nothingではない」という条件を表しています。
- 「変数workbookWithSameName」がNothingでないとき
-
「Nothingではない」=「同名のエクセルブックを開いている」ということです。
この条件が成り立つときは、同名のエクセルブックを開いている旨メッセージを出して、プログラムの実行を終了します。
- 「変数workbookWithSameName」がNothingのとき
-
「Nothing」=「同名のエクセルブックを開いていない」ということです。
この場合は、「Workbooks.Open」をしてもエラーが出ないはずですから、今まで通り「Workbooks.Open」の処理を行っていきます。
これで、同名のワークブックを開いている場合には、処理を中断されるようになりました。
パターン3. 同一エクセルブックを開いているときは処理を継続したい
パターン2では「同名のエクセルブックを開いている」ときに、処理を中断しました。
でも、同名の中でも、処理をしようとしていたエクセルブックそのものを開こうとしているときは、処理を継続できたほうが便利そうです。
たとえば、「C:¥temp¥test1.xlsx」というエクセルブックを開きたいと思ったときには、次のような処理ができれば理想的です。
「C:¥test1.xlsx」 | →× 別ファイルなので中断 |
「C:¥temp¥test1.xlsx」 | →○ 開きたいエクセルブックそのものなので処理を継続 |
「D:¥temp¥test1.xlsx」 | →× 別ファイルなので中断 |
そこで、このような判断をするロジックを追加します。
処理対象ブックを既に開いているときは処理を継続する
Sub OpenWorkbookTest_CheckBeforeOpen3()
Dim Filepath
Filepath = "C:¥temp¥test1.xlsx"
If Dir(Filepath) = "" Then
MsgBox "指定したファイルは存在していません"
Exit Sub
End If
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Dim Filename
Filename = FSO.GetFileName(Filepath)
'filenameと同名のエクセルブックを取得
Dim workbookWithSameName As Workbook
Set workbookWithSameName = getWorkbookByName(Filename)
'同名のエクセルブックがあるときはNothing「ではない」
If Not workbookWithSameName Is Nothing Then
If workbookWithSameName.FullName <> Filepath Then
MsgBox "同名のエクセルブックを開いているためファイルを開けません"
Exit Sub
End If
End If
Dim targetWorkbook As Workbook
If workbookWithSameName Is Nothing Then
Set targetWorkbook = Workbooks.Open(Filepath)
Else
Set targetWorkbook = workbookWithSameName
End If
' 対象ブックの 「Sheet1」シートの 「A1」セルの 値
targetWorkbook.Worksheets("Sheet1").Cells(1, 1).Value = "テスト書き込み"
End Sub
'以下は、https://www.excelspeedup.com/getworkbook/ からコピペ
Function getWorkbookByName(targetWorkbookName) As Workbook
Dim TempWorkbook As Workbook
For Each TempWorkbook In Workbooks
If TempWorkbook.Name = targetWorkbookName Then
Set getWorkbookByName = TempWorkbook
Exit Function
End If
Next
Set getWorkbookByName = Nothing
End Function
先ほどのプログラムに22行目、25行目、30行目、32行目~34行目を付け加えています。
WorkbookオブジェクトのFullNameプロパティでワークブックのファイルパスを取得
If workbookWithSameName.FullName <> Filepath Then
MsgBox "同名のエクセルブックを開いているためファイルを開けません"
Exit Sub
End If
22行目で、次の2つの比較をしています。
workbookWithSameName.FullName | → | 既に開いている同名ワークブックの「ファイルパス」 |
Filepath | → | 開きたいワークブックの「ファイルパス」 |
そして、一致していないときだけ23~24行目のロジックを実行(=処理を中断)します。
対象ブックを既に開いているときは、そのブックを処理対象とする
If workbookWithSameName Is Nothing Then
Set targetWorkbook = Workbooks.Open(Filepath)
Else
Set targetWorkbook = workbookWithSameName
End If
- 変数「workbookWithSameName」がNothingのとき
-
同名のワークブックを開いていない状態なので、新しくワークブックを「Open」する処理を行います。
- 変数「workbookWithSameName」がNothingではないとき
-
変数「workbookWithSameName」に「既に開いているブック」の情報が格納されています。
そこで、Workbooks.Openをする代わりに33行目で、その情報を「targetWorkbook」にコピーします。
既に、同一ファイルを開いているときに「Workbooks.Open」を行わないのが、このプログラムのポイントです。
同一ファイルを既に開いている状態で、Workbooks.Openをしてしまうと、「test1.xlsxは既に開いています。2重に開くと、これまでの変更内容は破棄されます。test1.xlsxを開きますか?」というメッセージが表示される場合があります。
実際に、このマクロを動かしたときの様子です↓
3. まとめ
Workbooks.Open文は、そのまま使うと、次のような場合にエラーが発生します。
- 指定したファイルが存在していない
- 既に同名のファイルを開いている
適切な事前チェックをすれば、このような原因でのエラーを防ぐことができます。
面倒ですが、必要に応じて事前チェックを入れて、適切な対策を取りましょう。