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

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

中級者のためのExcel エクセルマクロVBA入門:On Error Goto を極める!

すんごい久しぶりっす。申し訳ない。別にわすれてたわけではありません。単に忙しかっただけです。でもブログはのんびり自分のペースで続けます。オス。

今回は、エクセルマクロをもっとオブジェクティブに使ってみたいと思います。今回はマクロを組む上で欠かせないエラー処理を極めます。これ使うとぶっちゃけすごい便利。

目的はエラー処理を一定の書式で同じ法則で入れられること


これ。マクロをどんどん組んでいくとどんどん複雑化していきます。その中でエラー処理をどのように拾っていくのか?ということが非常に重要になっていきます。場所によって処理の仕方や、内容が変わってしまうと、マクロ1本の中に同じエラー処理なのにいろんな書き方存在してしまい、非常に煩雑化します。もちろんその後のメンテナンスにも関わってきますよね?

これを共通化するのが目的です。今回は中級者よりのお話なのでオブジェクトとは?は勿論理解しているし、クラスとインスタンスコンストラクタについても理解がある前提で書きます。

エラークラスを作る


まず、エラークラスを作ります。このクラスを使うことで、エラー処理の共通化を行います。中級者よりに書くので今回はプログラムを作る流れとかは飛ばします。クラスモジュールとしてclsErrorファイル新規追加します。

Option Explicit

Private Const cCLS_NAME = "clsError"

'*****************************************************************
'*    機 能:エラークラス
'*    概 要:エラー情報を管理するクラス
'*    補 足:
'*****************************************************************

Private moduleName As String
Private procName As String
Private errorMsg As String

Private Const MSG_ERRNUM As String = "{{NUMBER}}"
Private Const MSG_ERRDESP As String = "{{DESCRIPTION}}"
Private Const MSG_ERRMODULE As String = "{{MODULENAME}}"
Private Const MSG_ERRFUNCTION As String = "{{FUNCTIONNAME}}"

Private Const MSG_ERROR As String = _
    "エラーが発生しました。" & vbCrLf & vbCrLf & _
    "エラー番号:" & MSG_ERRNUM & vbCrLf & _
    "エラー情報:" & MSG_ERRDESP & vbCrLf & _
    "モジュール名:" & MSG_ERRMODULE & vbCrLf & _
    "ファンクション名:" & MSG_ERRFUNCTION & vbCrLf & vbCrLf & _
    "※原因が分からない場合は、システム担当へご連絡ください。"


'--------------------------------------------------------
'*関数名:コンストラクタ
'*機  能:エラー情報を初期化
'--------------------------------------------------------
Private Sub Class_Initialize()

    moduleName = ""
    procName = ""
    errorMsg = ""

End Sub

'--------------------------------------------------------
'    機 能:デストラクタ
'--------------------------------------------------------
Public Sub Class_Terminate()
    
    moduleName = ""
    procName = ""
    errorMsg = ""
    
End Sub

'--------------------------------------------------------
'*関数名:SetError
'*機  能:エラー情報をセットする。
'--------------------------------------------------------
Public Sub SetError(ByVal mdlName As String, ByVal prcName As String)
    
    ' 最初のエラーを設定する(すでにセットされていたらセットしない)
    If moduleName = "" Then
        moduleName = mdlName
    End If
    If procName = "" Then
        procName = prcName
    End If

End Sub


'--------------------------------------------------------
'*関数名:throwError
'*機  能:エラーメッセージ表示
'--------------------------------------------------------
Public Sub ThrowError()

        Dim Msg As String
    
        Msg = MSG_ERROR
        Msg = Replace(Msg, MSG_ERRNUM, Err.Number)
        Msg = Replace(Msg, MSG_ERRDESP, Err.Description)
        Msg = Replace(Msg, MSG_ERRMODULE, moduleName)
        Msg = Replace(Msg, MSG_ERRFUNCTION, procName)
        
        Call MsgBox(Msg, vbCritical, APP_TITLE)
        
        
End Sub


'--------------------------------------------------------
'*関数名:SetErrMessage
'*機  能:エラーメッセージをセットする
'--------------------------------------------------------
Public Sub SetErrMessage(ByVal strErrMsg As String)
    
   errorMsg = strErrMsg

End Sub

'--------------------------------------------------------
'*関数名:GetErrMssage
'*機  能:エラーメッセージを取得する
'--------------------------------------------------------
Public Function GetErrMessage() As String
    
  GetErrMessage = errorMsg

End Function


はい、これがエラークラスです。コンストラクタとかデストラクタでエラーメッセージの初期化を行っています。各メソッドは以下のようになっています。

・SetError
 最初に起きたエラーの内容をセットします。メッセージを格納するプロパティは既にコンストラクタで初期化されていますので、これをSetErrorでは、初期化されていた場合に限りエラーの内容をセットするようになっています。

・throwError
 セットされたエラーメッセージを実際にメッセージボックスに表示させます。

・SetErrMessage
 エラーメッセージをプロパティにセットします。

・GetErrMessage
 プロパティにセットされているエラーメッセージを取得します。


こんな感じです。後はこれをどう使うか?になります。

オブジェクトを最初に宣言してしまう。


例えば、通常で使うための標準モジュール内で、このクラスのインスタンスをグローバルで作成しておきます。

Public errorObj As New clsError

こんな感じ。後はこのインスタンスであるerrorObjを使ってあげれば良いわけです。わぉ!便利。

errorクラスの使い方


このエラークラスの使い方ですが、使うための基本がいくつかあります。このエラークラスの仕組みは、どのようになっているのかというと、、、、

  1. すべてのプロシージャで起こる例外やエラーを拾う
  2. 最初に起こったエラーを拾う。(その後に起こるエラーは拾わない)
  3. 拾ったエラーはシートオブジェクトまで戻ってから最終的に表示される
  4. プロシージャ名と、モジュール名を設定しておくことで、どこで起きたエラーかが分かる


このような感じになっています。ですので、エラーを投げる。つまりthrowメソッドを使うのはシートオブジェクト内の最初に動くイベントプロシージャだけで良く、それ以外のプロシージャはErr.Raiseメソッドを使うだけ良くなります。

シートオブジェクト上でthrowしておく、後はすべてErr.Raise!

ということですね。
これは一例ですが、たとえばボタンを押したときの挙動のイベントプロシージャの中で・・・

Private Sub btnHogeHoge_Click()
On Error GoTo hErr

    Call 標準モジュール
    
    Exit Sub
hErr:
    Call errorObj.ThrowError
End Sub


というように、いつものように、On Error Gotoステートメントを利用して、さらにエラー時の内容をerrorObj.ThrowErrorを呼び出します。そして、このイベントプロシージャ内で呼び出される各モジュールで・・・

Option Explicit

Private Const cMODULE_NAME = "mdl_hoge"

' なんか出力するプロシージャ
Public Function OutputMain() As Boolean
On Error GoTo pError
Const cPROC_NAME = "OutputMain"

	'なんか処理
	・・・
	・・・
	・・・
	
pError:
    Call errorObj.SetError(cMODULE_NAME, cPROC_NAME)
    Call Err.Raise(Err.Number, , Err.Description)
End Function

このようにしておきます。ErrorクラスのSetErrorメソッドで、どのモジュールのどのプロシージャで起きたエラーなのかをセットして、その後エラーを上げています。


このように、すべてのモジュールとプロシージャでこのような処理を入れておくことで、エラーをきっちり管理できるようになります。いかがでしょうか?エラー処理ってとっても面倒くさいと思います。それを、「一定の決まりで書くこと」でエラーの設定漏れや拾い漏れをなくすことができます。


今日はここまで!


かしこ