怠日記

写真・金魚・昆虫・プログラミングの趣味を語るサイトです。似たようなことをnoteにも書いたり書いてなかったり。

VBScript - エラー処理(その2)

スクリプト内で発生したエラーの対処方法を説明する。

エラー処理

スクリプトの実行中にエラーが起きた場合、通常、その時点でスクリプトは強制終了される。

エラーが起きた場合に強制終了ではなく独自の処理を行うときは、On Error ステートメントと Err オブジェクトを使う。

 

独自のエラー処理を行えるようにするには、On Error Resume Next ステートメントを使ってエラー処理を有効にする。

エラー処理を有効にすると、実行時エラーが起きても強制終了せず、そのまま処理を継続するようになる。

エラー処理を無効に戻すときは On Error GoTo 0 ステートメントを使う。

 

実行時エラーの発生有無は Err オブジェクトの Number プロパティで判定できる。

Number プロパティは正常なら 0 に、そうでないなら 0 以外になる。

 

基本的なエラー処理の流れは次のとおり。

  1. エラーの発生が想定される処理の前に On Error Resume Next ステートメントを置いて、エラー処理を有効化
  2. 処理を実行
  3. Err.Number <> 0 だったらエラー処理する
  4. On Error GoTo 0 ステートメントでエラー処理を無効化

不適切なエラー処理はスクリプトの暴走につながる。注意すること。

 

次のサンプルは、ユーザーに入力された数値で 100 を割り、結果を表示する。
エラーが起きた場合は、エラー情報を通知して終了する。

Dim y
y = InputBox("100を割る数を入力してください")

'★エラー処理を有効化
On Error Resume Next

Dim ret
ret = 100 / y
If Err.Number <> 0 Then
    '★エラー処理
    WScript.Echo "Error # " & CStr(Err.Number) & " : " & Err.Description
    WScript.Quit 1
End If

'★エラー処理を無効化
On Error GoTo 0

'計算結果を表示
WScript.Echo "100 ÷ " & y & " = " & ret

本来であれば計算の前に入力チェックするべきだが、まぁサンプルということで。

エラー情報(Errオブジェクト)

実行時エラーの内容は Err オブジェクトに格納される。

Err オブジェクトは以下のプロパティとメソッドを持つ。

Numberプロパティ

エラー番号。正常なら 0 、異常なら 0 以外。

Descriptionプロパティ

エラーの説明。

HelpFileプロパティ

ヘルプファイルのパス。

HelpContextプロパティ

ヘルプファイルのトピックを表すコンテキスト番号。
HelpFile プロパティを指定した場合、HelpContext プロパティで指定したトピックが自動的に表示される。

Sourceプロパティ

エラーを最初に発生させたオブジェクトの名前。

Clearメソッド

Err オブジェクトが持つすべてのプロパティの値をクリアする。

Raiseメソッド

実行時エラーを生成する。

Err.Raise number [, source] [, description] [, helpFile] [, helpContext]
  • number
    エラーの番号を Long 型の数値で指定する。
    0 ~ 65535 の範囲が指定可能。
    0 ~ 512 は VBScript の組み込みエラー、513 ~ はユーザー定義エラーとなる。

    ユーザー定義エラーは定数 vbObjectError 以上の数値にする。
    たとえば、エラー番号 1050 を発生させるには vbObjectError + 1050 とする。

  • source
    エラーを生成したオブジェクトまたはアプリケーションの名前を指定する。
    省略可能。

  • description
    エラーの説明を指定する。
    省略可能。

  • helpFile
    ヘルプファイルのパスを指定する。
    省略可能。

  • helpContext
    ヘルプファイル内のトピックを識別するコンテキストIDを指定する。
    省略可能。

エラー処理の適用範囲

On Error Resume Next ステートメントは使用したプロシージャにのみ適用される。

エラー処理が無効なプロシージャで実行時エラーが発生した場合、エラー処理が有効なプロシージャまで処理が渡る。

もしエラー処理が有効なプロシージャがなければ、そのままエラー情報が表示され、スクリプトは終了される。

 

例: エラー処理が有効なプロシージャからエラー処理が無効なプロシージャを呼び出した場合

FuncA → FuncB で呼び出し、FuncB で実行時エラーが起きた場合、FuncA に処理が渡る。

'エラー処理が有効なプロシージャ FuncA
Sub FuncA
    On Error Resume Next

    Dim ret
    ret = FuncB
    'FuncB で実行エラーが起きた場合、ここ(FuncB を呼び出した行の次行)に処理が渡る

    'エラー処理
    If Err.Number <> 0 Then
        WScript.Echo "Error # " & CStr(Err.Number) & " : " & Err.Description
        WScript.Quit 1
    End If
End Sub

'エラー処理が無効なプロシージャ FuncB
Function FuncB
    'ゼロ除算エラーを起こす
    FuncB = 100 / 0
End Function

 

例: 呼び出しの階層が深くなっても考え方は同じ

FuncA → FuncB → FuncC で呼び出し、FuncC で実行時エラーが起きた場合、FuncA に処理が渡る。

'エラー処理が有効なプロシージャ FuncA
Sub FuncA
    On Error Resume Next

    Dim ret
    ret = FuncB
    'FuncB/C で実行エラーが起きた場合、ここ(FuncB を呼び出した行の次行)に処理が渡る

    'エラー処理
    If Err.Number <> 0 Then
        WScript.Echo "Error # " & CStr(Err.Number) & " : " & Err.Description
        WScript.Quit 1
    End If
End Sub

'エラー処理が無効なプロシージャ FuncB
Function FuncB
    FuncB = FuncC
End Function

'エラー処理が無効なプロシージャ FuncC
Function FuncC
    'ゼロ除算エラーを起こす
    FuncC = 100 / 0
End Function

エラー処理メモ

確実にエラー処理できるようにするには

実行時エラーによってスクリプトを強制終了されないようにするには、メインルーチンで On Error Resume Next ステートメントを使うのが確実である。

そうすることで処理されなかったすべてのエラーを、メインルーチンでキャッチできるようになる。

'メインルーチン
On Error Resume Next
Call Main
If Err.Number = 0 Then
    '正常終了
    WScript.Quit 0
Else
    '異常終了(処理されなかったすべてのエラーをここでキャッチできる)
    WScript.Echo "Error # " & CStr(Err.Number) & " : " & Err.Description
    WScript.Quit 1
End If
On Error GoTo 0

'サブルーチン
Sub Main
    'TODO: 処理を書く
End Sub

サブルーチンのエラーをメインルーチンで処理する

サブルーチンで発生したエラーを、メインルーチンで処理するようにもできる。

そうする場合、サブルーチンはエラー処理を有効化しておく。
メインルーチンは無効のままで良い。

'メインルーチン
WScript.Echo Divide(100, 0)
If Err.Number <> 0 Then
    '★サブルーチンで発生したエラーの処理
    WScript.Echo "Divide Error # " & CStr(Err.Number) & " : " & Err.Description
    WScript.Quit 1
End If

'サブルーチン
Function Divide(ByVal a, ByVal b)
    '★エラー処理を有効化
    On Error Resume Next
    '実行時エラーが起きるかもしれない処理を実行
    Divide = a / b
End Function

VBScriptではラベルによるエラー処理はできない

VBScript では VB6 や VBA にあるラベルによるエラー処理は利用できない。

以下のように On Error GoTo 〈ラベル名〉 と指定して、エラー発生時に指定したラベルへ飛ばす書き方は VBScript では不可能。

'Notice: VBScript ではサポートされていない書き方

'エラーが起きたら ErrHandler ラベルに飛ばす
On Error GoTo ErrHandler
    Dim ret
    ret = 100 / 0

    MsgBox ret

ExitHandler:
    Exit Sub
ErrHandler:
    MsgBox "Error # " & CStr(Err.Number) & " : " & Err.Description
    GoTo ExitHandler

try-catchのようにエラー処理をする

VBScript では C#Java のような tru-catch での例外処理はサポートされていない。
が、少し工夫するだけでそれっぽくエラー処理をすることはできる。

まず VBScript におけるエラー処理で覚えておくべきポイント3つを以下に挙げる。

  • エラーを処理するには On Error Resume Next ステートメントでエラー処理を有効化する。
  • On Error Resume Next ステートメントによる設定は、そのプロシージャ内でのみ適用される。
  • エラー処理を有効にしていないプロシージャで実行時エラーが起きた場合、エラー処理が有効なプロシージャまで処理が戻る。
    エラー処理が有効なプロシージャがなかった場合はスクリプトが終了する。

これらのポイントをふまえて、try-catch のようなエラー処理にしたのが次のスクリプト

'try...catch...finally のサンプル
Sub TryCatchExample
    'エラー処理を有効化
    On Error Resume Next

    Dim errorHasOccurred
    errorHasOccurred = False

    'Try にあたる処理を呼び出す
    Call Try

    'Try で実行時エラーが発生した場合の処理を分岐内に書く
    If Err.Number <> 0 Then
        errorHasOccurred = True

        'TODO: catch にあたる処理を書く
    End If

    'TODO: finally にあたる処理を書く

    '実行時エラーが発生していた場合はここで処理終了
    If errorHasOccurred Then
        Exit Sub
    End If
End Sub

Sub Try
    'TODO: try にあたる処理を書く
End Sub

TryCatchExample プロシージャは On Error Resume Next ステートメントでエラー処理を有効化している。

TryCatchExample プロシージャから呼び出される Try プロシージャは、エラー処理は無効なまま。
なので Try プロシージャで実行時エラーが発生した場合、TryCatchExample プロシージャの Call Try の次の行に制御が渡る。

そこで Err.Number プロパティを見て、実行時エラーが発生していた場合はエラー処理をする。