和風スパゲティのレシピ

日本語でコーディングするExcelVBA

ブックを現在開いているかチェックする関数

ある名前のブックを現在開いているかを確認する関数を紹介します。

Worksbooks(ブック名)でブックを指定するときに、
「インデックスが有効範囲にありません」エラーを回避するために使いますね。

やり方はとても単純です。
ブックを1つずつ見ていき、目的のブック名と一致しているブックがあるか調べます。

ソースコード

Function Isブックを開いている(判定ブック名 As String) As Boolean

    ' 開いているすべてのブックを走査
    Dim wb As Workbook
    For Each wb In Workbooks
    
        ' ブック名が一致したらTrueを返してExit
        If wb.Name = 判定ブック名 Then
            Isブックを開いている = True
            Exit Function
        End If
        
    Next
    
End Function

使い方

    If Isブックを開いている("商品マスタ.xlsx") = False Then
        MsgBox ("処理に必要なマスタが開かれていません。")
        Exit Sub
    End If

コードの解説

教本のような「コレクションに対するFor Each」ですね。


For Each で開いているすべてのブックを走査し、
ブック名が判定ブック名と一致したらTrueを返してExitします。

Exitせずにループが終わったら、一致したブックがなかったということで、
Booleanの初期値であるFalseが返るしくみです。


もし、この初期値の利用がイメージしづらかったら、関数の最後を

    Next

    Isブックを開いている = False
End Function

と書くと理解しやすいです。
For Each ~ Next をExitせずに突破出来たら、FalseにしてEndです。

エラーを利用した別の判定方法

お忙しい方は、↑の関数をコピペして早速使ってください。

お時間に余裕がある方は、↓のテクニックも読んでみてください。


※ 内容はワークシートが存在するかチェックする関数で解説したものと同じです。
 すでにこちらを読まれている方は、ここから先を読む必要はありません。

エラーが発生するかどうかで判定を行う

冒頭の関数では、単純なループ処理で丁寧に判定しましたが、
実はループ処理を使わずに書くこともできます。

目的の「Workbooks(判定ブック名)」を関数内で指定してしまって、
それがエラーになるかどうかを調べる方法です。

Function Isブックを開いている(判定ブック名 As String) As Boolean
    On Error Resume Next ' この関数内ではエラーをスキップ
    
    ' 適当な変数に、目的のブックをセットしてみる
    Dim tmp As Workbook
    Set tmp = Workbooks(判定ブック名) ' ブックがなければこの文はスキップされる

    ' 変数へのセットがスキップされていないかで判定する
    If Not tmp Is Nothing Then
        Isブックを開いている = True
    End If
        
End Function

ブックが存在しない場合は、変数へのSetが「インデックスが有効範囲にありません」エラーになります。
それをOn Error Resume Nextでスキップしているわけですね。


スキップされた場合は、tmpは初期値であるNothingのままなので、
次のIf文でそこを判定しています。


なお、On Error Resume Next は、その関数の中だけで有効です。
「この関数を呼んだ後は、呼び出し元のエラーも無視されるようになる」ことはありません。

ただし、On Error Resume Nextを使った関数の中で、別の関数をさらに呼び出す場合は、その呼び出した関数でもエラーが無視されるので気を付けてください。


子でOn Error Resume Nextしても、親でOn Error Resume Nextされませんが、
親でOn Error Resume Nextすると、子もOn Error Resume Nextされます。


On Error Resume Next で、スキップしながら目的の処理をやってみて、
エラーが出るかどうかで状態を判定するのは、結構便利です。

いろいろな場面で活躍するので、テクニックとして覚えておきましょう。

さらに短く書ける

先ほどはエラーの活用を説明するために、丁寧な関数にしましたが、
せっかくループしなくて済むOn Error Resume Nextを使うのですから、
もっと短い行数で終わらせてみましょう。


まず、分かりやすい様に

    If Not tmp Is Nothing Then
        Isブックを開いている = True
    End If

こう書きましたが、これは

Isブックを開いている = Not tmp Is Nothing

こう書くこともできます。

「条件を満たすなら、結果はTrue」は、
「結果 = 条件を満たすかどうか」と書けますからね。

中身が1本しかないIf文は、慣れたらこっちで書きましょう。
単純な処理はなるべく短い行数で書いて、その分複雑な処理を丁寧に書くと、コードが読みやすくなります。


あとは、一時変数tmpも消すと、最終的にはこのコードになります。

Function Isブックを開いている(判定ブック名 As String) As Boolean
    On Error Resume Next
    Isブックを開いている = Not Workbooks(判定ブック名) Is Nothing
End Function

短い!

どっちの書き方がいいの?

どちらも一長一短で、ケースバイケースです。


1万回呼び出す関数なら、行数が短い方が、処理が速くていいかもしれません。

でも、今回の「ブック名」の判定は、マクロのスタート時にIf文で1回使う程度のことが多いので、速度は考えない方が良いですね。


あとで関数を書き替えたり、コピペして別の関数に使いたい場合は、
丁寧にループ処理を書いておいた方が便利です。


例えば、ブック名には、「日報0401」「日報0402」や、「売上データ202001」など、
識別用のパラメータ(日付とか)がついているやつも結構ありますよね?

この「シート名の部分一致を判定する」関数は、
「丁寧なループで作った関数」のIf ~ Then部分を、

If wb.Name = 判定ブック名 Then
' ↓書き換え
If InStr(wb.Name, 判定ブック名の前半部) = 1 Then

このように書き換えるだけで作成できます。


エラー技の方は、こういった活用はできません。

※エラー技のテンプレートとして使うことはできるので、
 On Error Resume Next の参考コードにしたい場合に、こっちを採用するのはアリ。


長々解説しておいてあれですが、
コピペして別の関数を作るメリットの方が大きそうなので、
今回は↑の丁寧なループ処理関数がおすすめです。

惜しくも不採用となってしまいましたが、
On Error Resume Next でスキップしながらやりたい処理をやってみて、
エラーが出るかどうかを関数で試すのは結構便利です。

いろいろな場面で活躍するので、テクニックとして覚えておきましょう。