初心者のためのExcelマクロ超入門(絶対できるVBA開発)

マクロがまったくわからない人のためにエクセルマクロやVBAについてできるだけわかりやすく書いています。Twitter:@shuhhohhey

初心者のためのExcel(エクセル)マクロVBA入門-成績表マクロの作成:エラーチェック

今回はエラーのチェックですね。何度でも載せておきますが処理の流れを設計の回で書いたのでおさらいです。

<処理の流れ>
  1. ボタン押下時のイベント処理
  2. メイン処理(mdlMain)
    1. 事前の処理(mdlStartEnd.PreStart)
    2. エラーのチェック(mdlErrorCheck.InputCheck)
    3. 成績表の読み込み(mdlInputScore.InputScore)
    4. 成績表の出力(mdlOutputScore.OutputScore)
      1. 印刷プレビュー(mdlOutputScore.PrintScore)
    5. 事後の処理(mdlStartEnd.PreEnd)


この中のエラーのチェックについて書いて行きます。エラーのチェックは想定されるエラーを書いてますので、それらのメッセージをmdlDefineに定義します。それとエラーチェックのメソッドInputCheckメソッドを書いて行きましょう。設計の段階で書いておいた想定されるエラーをもう一度掲載します。

<想定されるエラー>
  • 出席番号が入力されてない場合
    • 出席番号を入力してください。
  • 出席番号入力に数字以外を入力した場合
    • 出席番号が存在しません。処理を終了します。
  • 成績表の点数に数字以外が入っていた場合
    • 成績表の点数が不正です。処理を終了します。
  • 存在しない出席番号が入力された場合
    • 出席番号が見つかりません。処理を終了します。
  • 成績表テンプレートがない場合
    • テンプレートファイルがありません。処理を終了します。
  • 指定している保存先がない場合
    • 保存先が存在しません。処理を終了します。


これが想定されるエラーでした。ではここにあるエラーメッセージをmdlDefineに記載しておきます。

mdlDefineモジュールにて

Public Const APP_TITLE = "成績表マクロ"

Public Const MSG_CONFIRM_MACROSTART = "成績表を出力します。" + vbCrLf + "よろしいですか?"

Public Const ERROR_MSG1 = "出席番号を入力してください。"
Public Const ERROR_MSG2 = "出席番号が存在しません。処理を終了します。"
Public Const ERROR_MSG3 = "成績表の点数が不正です。処理を終了します。"
Public Const ERROR_MSG4 = "出席番号が見つかりません。処理を終了します。"
Public Const ERROR_MSG5 = "テンプレートファイルがありません。処理を終了します。"
Public Const ERROR_MSG6 = "保存先が存在しません。処理を終了します。"

Public Const ERROR_WINDOW_TITLE = "成績表マクロ:エラー"

以上です。設計にあるメッセージを定数ERROR_MSG1~5としているだけですね。次にエラーをチェックするメソッドInputCheckメソッドを書いてみましょう。大枠は作ってあるので、mdlErrorCheckモジュールに書いて行きます。

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' 入力チェックのメソッド
' InputCheck
' 引数:なし
' 戻り値:Boolean
' エラーの場合はエラーメッセージを入れる
' エラーがない場合はFalseを返す
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function InputCheck() As Boolean
On Error GoTo cmnErr
    
    ' 出席番号が入力されてない場合
    '' 出席番号を入力してください。
    ' 出席番号入力に数字以外を入力した場合
    '' 出席番号が存在しません。処理を終了します。
    ' 成績表の点数に数字以外が入っていた場合
    '' 成績表の点数が不正です。処理を終了します。
    ' 存在しない出席番号が入力された場合
    '' 出席番号が見つかりません。処理を終了します。
    ' 成績表テンプレートがない場合
    '' テンプレートファイルがありません。処理を終了します。
    ' 指定している保存先がない場合
    '' 保存先が存在しません。処理を終了します。
    
    ' エラーがないならとしてTrueを返す
    InputCheck = True
    
    Exit Function

cmnErr:
    MsgBox "エラーが発生しました" & vbCrLf & _
            "エラー番号:" & Err.Number & vbCrLf & _
            "エラーの種類:" & Err.Description, vbOKOnly + vbExclamation, _
            mdlDefine.ERROR_WINDOW_TITLE
    'マクロを終了する
    Exit Function
End Function


いつも通り最初の出だしと想定外のエラーチェックをキャッチする箇所は前回やった時と同じです。中身はコメントだけになっています。また、最後にエラーの場合、つまりチェックをすべて通過した場合にはTrueを返すようにします。
後はコメントにしたがって、エラーのチェックしていくだけですね。

エラーになった時の処理は??


さてここで問題が出てきます。入力チェックで引っかかった場合にどのような処理をしておくのがいいのか?ということです。本来、マクロを意図的に中断する場合、サブの処理ではなく、メインの処理で終わるのが一番良いとされています。なぜならそれが一番処理の流れを追いやすいからです。つまり・・・

  1. メインからエラーチェックを呼び出す
    1. エラーチェックを行う。
    2. エラーになったことをメインに教える
  2. メッセージを表示して処理をメインで終了する

というの流れですね。メインの処理が他のすべての処理を「管理」するわけです。つまりエラーになった時の処理は今上に書いたような流れになる方がいいですね。

参照渡しを使うか?グローバル変数を使うか?


ここでまた問題です。上に書いたInputCheckメソッドの戻り値は「Boolean」ですのでTrueかFalseを戻します。つまりメインでエラーメッセージを表示したいのですが、どのエラーになったのか?がFalseだけではわかりません・・・どうしましょう?色々な解決方法があるのですが、いくつか挙げてみます。

  • どこでも使える変数を用意してそこにエラーメッセージを入れておく
  • 引数に空のエラーメッセージを入れる変数を渡して、エラーならそこに入れる
  • エラーの番号を入れる変数を渡して、メインでエラーの判定をしてメッセージを表示する
  • InputCheckメソッドでエラーメッセージを表示するところまでやってしまう。


上のすべて正解です。だって動きますから。後は、どのようにしたら「楽」なのか?ということや、変更があった時に楽に修正ができるのはどの「やり方」だろう?と考えます。それぞれにメリットデメリットがありますので、あとは取捨選択をすればいいのです。
私は今回はこのように考えてみました。

  • エラーメッセージの変数ってメイン以外でも使うかな?
  • エラー番号を返すにすると、If文やSelect Case文を使わないといけないな
  • エラーになったらその時点で他のチェックはしないでいいな
  • 最後の方法だとメッセージを表示する命令がチェック項目ごとに必要そうだな

以上のように考えて、2番目の方法を使うことにしました。
ではコードです。まだ中身をざっくりだけです。

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' 入力チェックのメソッド
' InputCheck
' 引数:なし
' 戻り値:Boolean
' エラーの場合はエラーメッセージを入れる
' エラーがない場合はFalseを返す
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function InputCheck(ByRef strErrMsg As String) As Boolean
On Error GoTo cmnErr
    
    ' 出席番号が入力されてない場合
    If 出席番号チェック Then
        '' 出席番号を入力してください。
        strErrMsg = mdlDefine.ERROR_MSG1
        
        InputCheck = False
        Exit Function
        
    End If
    
    ' 出席番号入力に数字以外を入力した場合
    If 数字チェック Then
        '' 出席番号が存在しません。処理を終了します。
        strErrMsg = mdlDefine.ERROR_MSG2
        
        InputCheck = False
        Exit Function
        
    End If
    
    ' 成績表の点数に数字以外が入っていた場合
    If 成績表数字チェック Then
        '' 成績表の点数が不正です。処理を終了します。
        strErrMsg = mdlDefine.ERROR_MSG3
        
        InputCheck = False
        Exit Function
        
    End If
    
    ' 存在しない出席番号が入力された場合
    If 出席番号存在チェック Then
        '' 出席番号が見つかりません。処理を終了します。
        strErrMsg = mdlDefine.ERROR_MSG4
        
        InputCheck = False
        Exit Function
        
    End If
    
    ' 成績表テンプレートがない場合
    If ファイル存在チェック Then
        '' テンプレートファイルがありません。処理を終了します。
        strErrMsg = mdlDefine.ERROR_MS5
        
        InputCheck = False
        Exit Function
        
    End If
    
    ' 指定している保存先がない場合
    If 保存先存在チェック Then
        '' 保存先が存在しません。処理を終了します。
        strErrMsg = mdlDefine.ERROR_MSG6
        
        InputCheck = False
        Exit Function
        
    End If
    
    ' エラーがないならとしてTrueを返す
    InputCheck = True
    
    Exit Function

cmnErr:
    MsgBox "エラーが発生しました" & vbCrLf & _
            "エラー番号:" & Err.Number & vbCrLf & _
            "エラーの種類:" & Err.Description, vbOKOnly + vbExclamation, _
            mdlDefine.ERROR_WINDOW_TITLE
    'マクロを終了する
    Exit Function
End Function


はい、ここまで。コメントにそってIf文を付けます。そのエラーチェック項目に該当したら、該当エラーメッセージを参照渡しで引数になっている変数strErrMsgにセットして、あとはFalseを戻り値にし、メソッドを終了(Exit)しているだけです。ちょっと長いですが、チェックする項目分まったく同じものがあると考えていいです。

後は、具体的なエラー項目に対するチェックの内容を書いて行けば、このメソッドは完成ですが、長くなってきたので次回へ持ち越します。みなさん具体的なエラーのチェック、どうぞ書いてみてくださいね。


今日はここまで!


かしこ