[[20190315015850]] 『エラー処理を入れる際のメッセージの使い方』(ピザ男) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『エラー処理を入れる際のメッセージの使い方』(ピザ男)

エラー処理を入れた際の、
完了メッセージとエラーメッセージを上手いこと二つ入れる方法が
よく分かりません。

現在下記のように作っておりますが
applicationオブジェクトを無駄に同じものを二箇所入れており、
見にくいです。

エラーが起きた時は「エラーが発生しました」(エラーが発生したら続けず直ぐにエラーメッセージを出したい)
完了した際は「完了しました」
とif文のような形で、そのイフが終わった後、
最後にTrueする処理を入れるように
書くことは可能でしょうか?
よろしくお願いいたします。

sub サンプル()
諸々変数宣言
Application.ScreenUpdating = False
Application.DisplayAlerts = False
On Error GoTo ErrLabel
〜諸々処理〜
msgbox "完了しました"
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub

ErrLabel:

    MsgBox "エラーが発生しました"
Application.ScreenUpdating = True
Application.DisplayAlerts = True

end sub

< 使用 Excel:Excel2010、使用 OS:Windows10 >


 回答ではないです。

 私がもし作るのであれば
 1)前提として、エラーが出ないように設計する
 2)エラーが想定される場合、事前にチェックする
 3)それでもエラーが出る場合、デバッグ状態で止まっててもらう(ResumeやGotoをエラートラップとして使わない)

 ですかね・・・
 大規模なプログラムの場合は必要でしょうけど、私のような素人が作る程度のものであれば、必要ないです。

 提示のコードそのまま直すなら
    Sub サンプル()
        諸々変数宣言
        Application.ScreenUpdating = False
        Application.DisplayAlerts = False
        On Error GoTo ErrLabel
        〜諸々処理〜
    ErrLabel:
        If Err.Number > 0 Then
            MsgBox "エラーが発生しました"
        Else
            MsgBox "完了しました"
        End If
        Application.ScreenUpdating = True
        Application.DisplayAlerts = True
    End Sub
 こんな感じですかね・・・

(稲葉) 2019/03/15(金) 08:37


 こんにちは。

 この書き方はこの書き方でありだと思います
 エラー処理後、必ずやるべきことをエラー処理ルーチンの中に書くのは、
 わかりやすいと思います。

 で、

 On Error Goto で飛んだエラー処理ルーチンから通常ルーチンに復帰する場合は、
 Resume ステートメントを使います。
 Resume ステートメントは復帰先をラベルで指定出来るので、以下のように成ります。

    Sub サンプル()

        Application.ScreenUpdating = False
        Application.DisplayAlerts = False

        On Error GoTo ErrLabel
            '〜諸々処理〜
            Debug.Print 1 / 0
            MsgBox "完了しました"
        On Error GoTo 0

    Normal_Terminate:
        Application.ScreenUpdating = True
        Application.DisplayAlerts = True
        Exit Sub

    ''===============エラー処理ルーチン =============
    ErrLabel:
        MsgBox "エラーが発生しました"
        Resume Normal_Terminate

    End Sub

(でれすけ) 2019/03/15(金) 08:53


 でれすけ先生がいらっしゃったので、追加で質問よろしいですか?
 「必ずやる処理」が終わった後、デバッグで止めたい場合などは
    Sub test2()
        Application.DisplayAlerts = False
        On Error GoTo ErrLabel
        Debug.Print 1 / 0
        MsgBox "完了しました"
ErrLabel:
        Application.DisplayAlerts = True
        On Error GoTo 0
        If Err.Number > 0 Then Resume: MsgBox "エラーが発生しました"
    End Sub
 このように書いても実用でしょうか?
(稲葉) 2019/03/15(金) 09:09

 こんにちは。

 稲葉様 先生はやめてくださいね

 で、いろんな書き方の流儀があるので、なんとも...ですが
 いま稲葉さんの書いたサンプルははっきりと問題があるのでダメです。

  ×エラー処理ルーチンの中に On Error Goto 0 がある
  ×Resumeステートメントは、ラベル無しだと、エラー発生行に復帰するのでMsgBoxは実行されない

 書こうと思えば、以下の様に書くこともできなくもないですが、
 エラー処理ルーチンは、はっきりと正常な処理と分けた方がいいです。
 以下のように兼用するのは、
 処理の流れがわかりにくい=可読性が悪く、バグの元です。
 やっちゃダメです。

 On Error に限らず、Goto で処理の流れを吹っ飛ばすのは、
 可読性が落ちるので、非推奨です。

 VBAでは、実行時エラーのトラップは、On Error Goto しかないので
 仕方なく使いますが、なるべくエラー処理ルーチンをはっきり分けた方がいいです。

    Sub だめなサンプル()

        Application.ScreenUpdating = False
        Application.DisplayAlerts = False

        On Error GoTo ErrLabel
            '〜諸々処理〜
            Debug.Print 1 / 0
            MsgBox "完了しました"
        On Error GoTo 0

    ErrLabel:  ''=====正常処理ルーチンとエラー処理ルーチン兼用==========

        Application.ScreenUpdating = True
        Application.DisplayAlerts = True

        Select Case Err.Number
           Case 0
              Exit Sub  'エラーじゃなかったら終了(正常)
           Case Else
              MsgBox "エラーが発生しました"
        End Select

    End Sub

(でれすけ) 2019/03/15(金) 09:51


同じような事を書きますが、私からも。
稲葉さんの例は、On Error GoTo 0 すると Err がクリアされてしまうし、エラーのあった行に Resume してしまうと、VBAによってエラー表示されてしまいます。
    Sub test21()
        Dim iEr As Long

        Application.DisplayAlerts = False
        On Error Resume Next
        Debug.Print 1 / 0
        iEr = Err.Number
        On Error GoTo 0
        Application.DisplayAlerts = True

        If iEr > 0 Then
            MsgBox "エラーが発生しました", vbCritical, iEr & " 「" & Error(iEr) & "」"
        Else
            MsgBox "完了しました", vbInformation
        End If
    End Sub

    Sub test22()
        Dim iEr As Long

        Application.DisplayAlerts = False
        On Error GoTo ErrLabel
        Debug.Print 1 / 0

    sExit:
        On Error GoTo 0
        Application.DisplayAlerts = True

        If iEr > 0 Then
            MsgBox "エラーが発生しました", vbCritical, iEr & " 「" & Error(iEr) & "」"
        Else
            MsgBox "完了しました", vbInformation
        End If
        Exit Sub

    ErrLabel:
        iEr = Err.Number
        Resume sExit
    End Sub
(???) 2019/03/15(金) 09:55

 でれすけさん ???さん
 ありがとうございます。
 ダメな例がまさに私が最初に書いたコードそのものですね・・ 
 エラーと正常処理ルーチンを別個で持つ理由はわかりましたが、問題の箇所を突き止めるデバッグ状態で
 止めたいことを主眼に置いた場合は、そもそもエラー処理ルーチン自体が不要という認識でよろしいでしょうか?
 >エラーのあった行に Resume してしまうと、VBAによってエラー表示されてしまいます。 
 逆にVBAによるエラーを吐き出させたいのです。

 ※先生の件承知しました。 
(稲葉) 2019/03/15(金) 10:01

 書き忘れ。

 On Error で飛ばしたあと、Resumeで復帰させると、処理が2段階でジャンプするので、
 処置の流れを追いにくいです。

 そうすると、結局、この場合は、ピザ男さんの書き方が可読性の面では一番いいということに。

 On Error Resume Next 使うって

 Try
  〜
 Catch 
  〜
 Finally 
 みたいな書き方ができるとすれば、それが一番いいとおもいますが、
 なかかうまくいかないですね。

 >逆にVBAによるエラーを吐き出させたいのです。
 自分でさらにエラーをRaise することも出来ます。

    Sub サンプル()

        Application.ScreenUpdating = False
        Application.DisplayAlerts = False

        On Error GoTo ErrLabel
            '〜諸々処理〜
            Debug.Print 1 / 0
            MsgBox "完了しました"
        On Error GoTo 0

        Application.ScreenUpdating = True
        Application.DisplayAlerts = True
    Exit Sub  

    ErrLabel:      ''=====エラー処理ルーチン==========

        Application.ScreenUpdating = True
        Application.DisplayAlerts = True
        Err.Raise Err.Number, , Err.Description

    End Sub

(でれすけ) 2019/03/15(金) 10:07


 またまた書き忘れ

 自分だけで使うマクロだったら、On Error Goto 使わないです。
 デバッグのいい機会なので、ソースを修正するか、
 デバックモードで、イミディエイトウィンドウでもじゃもじゃして..実行再開

   

(でれすけ) 2019/03/15(金) 10:13


エラー時に止めたい、という用途なら、Resume だけにして飛び先書かないのはアリです。 が、本来の用途としては、エラー原因をトラップ先で対処してから戻す、という使い方でした。(そもそも、VBAのエラーメッセージ表示でマクロは止まるし、あまり意味がないような? むしろ、エラー発生行が判らなくなって逆効果?)

エラー番号だけ他の変数に控えて(何なら処理番号とか変数に用意しておき、これも控えるとか)、そのままResumeが良いのではないでしょうか。 エラートラップやコールバック関数内は、処理を止めるような記述は厳禁、という意識があるので、止めたくはないですね。(直接はエラートラップとは関係ないですが、DBのトランザクションを掛けたままエラーメッセージ表示したため、システム全体が止まってしまった、というトラブル例を知ってます)
(???) 2019/03/15(金) 10:24


 でれすけさん ???さん
 返事遅くなりました。
 いろいろご指導ありがとうございました。
 > 自分だけで使うマクロだったら、On Error Goto 使わないです。
 この部分をどこまで広げるか、で対応が異なってくる気がしました。
 私は自分と自分が知りえる範囲でしかマクロを使わないので、On Error Gotoを使ったことがありませんでした。
 (こちらにアップするコードもですが)基本的な考え方がわかり、勉強になりました。

 >本来の用途としては、エラー原因をトラップ先で対処してから戻す
 ということは、想定されたエラーであるってことだと思うので、最初から手当してあげれば済むような?
 とも考えました・・・
 エラートラップの本来の使い方ってどうなのでしょう?
 想定外のエラーのみが対象ということでいいのでしょうか?

 あとは最初と最後の処理を別ルーチンにしてしまうと可読性が落ちるのか、ありなのか?

    Sub test3()
        config False
        On Error GoTo ErrLabel
        Debug.Print 1 / 0
        On Error GoTo 0
        config True
        MsgBox "完了しました"
        Exit Sub
ErrLabel:
        config True
        MsgBox "エラーが発生しました"
    End Sub
    Sub config(flg As Boolean)
        Application.ScreenUpdating = flg
        Application.DisplayAlerts = flg
    End Sub

 ピザ男さんスレッド借りてしまいすみません。
(稲葉) 2019/03/15(金) 13:27

エラートラップの使い方と言ったら、想定外のエラーが起こってもアプリがフリーズしたり落ちたりしないようにするため、でしょうね。 想定内のエラー対処ついでに、想定外のエラーでも落ちなくするような場合に使います。 元々のBASICはアプリ開発言語の一種でしたから、勝手に落ちるアプリは失格でした。 けど、Excelマクロで考えると、どの行で問題が発生したのかすぐに分かった方がデバッグが楽なので、エラートラップを使わない、という考えは有効だと思います。

また、稲葉さんのtest3のように、Application.ScreenUpdating 等をまとめて抑止するような書き方は、抑止自体が過去の言語ではなかったものなので、何が正解というのは無いでしょう。 ただ、全部まとめて共通関数化したコードは他で見たことが無く、可読性は落ちる、と感じました。

とは言っても微妙な話で、いろいろ抑止するという、ロジックの本筋以外を隠すことで、本筋のコードを追いやすくする、という意味では、可読性は上がっていると言えます。 共通化する事でプロジェクト内全てでも関数1つで済む、という利点は判りますし、ちょっとコードを追えば意味も判りますが、誰が見ても一瞬で意味が判るのか?、というところで、可読性が落ちたように感じるのですよ。 可読性を上げるために関数化したのでしょうけど、その関数名が通常のExcelには無いものなので、咄嗟に意味が判らない、という感じ。 まとめてもまとめなくても、どちらもメリット/デメリットがあるなら、ユニーク関数を追加しない、素直な書き方が受け入れられるのでは…、と思いました。

あとは、他人に追わせるコードとして、オブジェクト指向とは真逆なのですが、なるべくサブプロシジャは書きたくない、というのもあります。 1画面の上下だけで読ませたいし、関数コールによる内部のレジスタの退避/復元を無駄と感じるのです。(無駄と言っても所詮はインタプリンタであり、速度的には何の問題も無いのですけどねぇ)
(???) 2019/03/15(金) 14:37


 こんにちは。

   1. 想定内のエラーは、エラー原因を取り除いて、正常処理に復旧させる
   2. 想定外のエラー発生時に突然終了させずに、「最低限必要な処理をしてから」終了させる
   3. 例外発生の有無を条件分岐につかっって、コードを簡略化する

   想定されてても、事前に対処するのが面倒なときとか、出来ないときとかって結構ありましてね。
   特に昔は....

   今でもファイル入出力関係とかではよくあると思いますよ。
   書き込み途中でディスク容量いっぱいになったりとか。 (今はないですかね。)
   ファイルが壊れてて、あるはずのデータが無かったりとか。 

  >あとは最初と最後の処理を別ルーチンにしてしまうと可読性が落ちるのか、ありなのか? 
  やってもいいと思いますが、エラー処理ルーチンは、
  できるだけすぐに抜けたいので、自分はやらないです。

(でれすけ) 2019/03/15(金) 14:54


 お返事ありがとうございます。
 >ユニーク関数を追加しない、素直な書き方が受け入れられるのでは…、と思いました。 
 可読性という意味では、エクセルにない関数はないほうが読みやすいですね。

 >DBのトランザクションを掛けたままエラーメッセージ表示した
 >書き込み途中でディスク容量いっぱいになったりとか。
 この二つは共通してI/Oがらみなので、特に気を付けなくてはいけないのは、(読み)書きの場合なので
 エラートラップ設けるにしろ、特にI/Oに気を付けなければいけない、という感じでしょうか。

 >3. 例外発生の有無を条件分岐につかっって、コードを簡略化する
 については
 On Error Resume Next
 Set WB = Workbooks("存在しないブック")
 On Error Goto 0
 If WB Is Nothing Then '〜〜
 このような使い方でしょうか?

 それで最初に戻ると、私としてはその場でエラー出したいので
    Sub test4()
        Application.ScreenUpdating = False
        Application.DisplayAlerts = False
        Debug.Print 1 / 0
        Application.ScreenUpdating = True
        Application.DisplayAlerts = True
    End Sub
 この形が一番いいと思うようになりました。。。
(稲葉) 2019/03/15(金) 17:36

コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.