[[20240324075455]] 『皆様のVBA例外処理の方法を教えて下さい』(隠居Z) ページの最後に飛ぶ

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

 

『皆様のVBA例外処理の方法を教えて下さい』(隠居Z)

いまさらで、汗顔の至りですが^^;
1.複数のモジュール[ThisWorkbook、標準[複数]]にわたり
  コード書いてます。
2.呼び出し元のみですとどのモジュールのどの関数か
  解らないので全ての関数で処理の後、ログを作成し
  同じエラー番号で例外を作成、呼び出し元で拾うように
  しようとは思うのですが。。。ここで問題が^^;
3.結構呼び出し関数が多いもので横着をして
  CallBynameでApplication.Runをセット、メソッドを
  指定後関数名を配列に格納したものをループで回して
  関数呼び出しをしています。
4.3.では呼び出し元の例外処理の有効スコープが切れ
  エラーでコードが中断いたします。
5.ログとれりゃ別に呼び出し元で拾わなくても何も困りは
  しないかなぁ。。。と思っているのですが
  他に、こうすればぁ〜、とか、あほなことして、こうするんだよ!
  みたいなご意見がございましたら是非にもお願いいたします。
  聞くは一時の恥、聞かざるは末代までの。。。云云(*^^*)
で、よろしくお願いいたします。
例外処理とはOn Error Goto 〜 を指しておりますです。
m(__)m

 Module1

 Option Explicit
Private Sub main()
    Dim x, v(), eApp, i&, fps$, fNm$, eFlg As Boolean, xx()
    On Error GoTo errstp
    fps = ThisWorkbook.Path & "\"
    fNm = "errorlog.txt"
    Open fps & fNm For Output As #1
    Close #1
    Open fps & fNm For Append As #1
    Set eApp = Application
    v = Array("Module1.p1", "Module1.p2", "Module2.p3", "Module2.p4", "Module2.p5")
    ReDim xx(UBound(v))
    For i = LBound(xx) To UBound(xx)
        xx(i) = 0
    Next
    For i = LBound(v) To UBound(v)
         eFlg = CallByName(eApp, "Run", VbMethod, v(i))
         If eFlg Then xx(i) = 1
    Next
    Rem Err.Raise 13
    If Application.Sum(xx) <> 0 Then
        MsgBox "エラー発生ログを確認してください"
    Else
        MsgBox "正常終了"
    End If
    GoTo final:
errstp:
    MyErrSub Err.Number, Err.Description, "errm01", "Main"
    MsgBox "エラー発生ログを確認してください"
final:
    Erase v, xx
    Close #1
End Sub
Function p1(ParamArray dum())
    Dim x
    On Error GoTo errstp
    Err.Raise 1
    Exit Function
errstp:
    MyErrSub Err.Number, Err.Description, "errm01", "P1"
    p1 = True
End Function
Function p2(ParamArray Dummy())
    Dim x
    On Error GoTo errstp
    Err.Raise 2
    Exit Function
errstp:
    MyErrSub Err.Number, Err.Description, "errm01", "P2"
    p2 = True
End Function
Sub MyErrSub(num&, etx$, mtx$, ptx$)
    Dim ltx$
    On Error GoTo errstp
    ltx = "[" & Format(Now, " yyyy/mm/dd : hh:nn:ss ") & "]"
    ltx = ltx & Chr(32) & num & Chr(32) & etx & Chr(32)
    ltx = ltx & mtx & Chr(32) & ptx
    Print #1, ltx
    Rem Err.Raise 9
    Exit Sub
errstp:
    MsgBox "MyErrSub エラーですシステム担当に連絡してください" & Chr(13) & _
           Err.Number & Chr(32) & Err.Description
    End
End Sub

 Module2

 Option Explicit
Function p3(ParamArray dum())
    Dim x
    On Error GoTo errstp
    Err.Raise 3
    Exit Function
errstp:
    MyErrSub Err.Number, Err.Description, "errm02", "P3"
    p3 = True
End Function
Function p4(ParamArray dum())
    On Error GoTo errstp
    Err.Raise 4
    Exit Function
errstp:
    MyErrSub Err.Number, Err.Description, "errm02", "P4"
    p4 = True
End Function
Function p5(ParamArray dum())
    On Error GoTo errstp
    Err.Raise 5
    Exit Function
errstp:
    MyErrSub Err.Number, Err.Description, "errm02", "P5"
    p5 = True
End Function

たたき台として↑みたいな感じかなぁと思っています。^^;
<< _ _ >>
< 使用 Excel:Excel2016、使用 OS:Windows10 >


 >呼び出し元のみですとどのモジュールのどの関数か
 >解らないので全ての関数で処理の後、ログを作成し
 >同じエラー番号で例外を作成、呼び出し元で拾う

 それって、実際に発生するエラーの話じゃなくて、
 どのモジュールの関数か知りたくて、
 わざとエラーを発生させて、情報を集める
 と言う仕掛けの話なんですね?

 ロクに考えている訳じゃないのですが、
 本当にエラーが発生した時とは何も競合しないのですね?

(半平太) 2024/03/24(日) 16:27:59


ご返信ありがとうございます。
>> わざとエラーを発生させて、情報を集める
>> と言う仕掛けの話なんですね?
確かに↑のコード内容ではそぉなりますね。
実際のエラーを想定してデバッグ時間短縮のために考えております。

>>本当にエラーが発生した時とは何も競合しないのですね?
競合するかもしれません^^;↑コード作成時実際のエラーもありましたので
どちらか解りませんでした( ̄▽ ̄;)

実際は印刷処理が中心なので二回廻して、一回目で全ての関数でエラーが無ければ
二回目で印刷。とか考えております。

m(__)m
(隠居Z) 2024/03/24(日) 17:06:51


 ・・と言うことは、実際のエラーの捕捉(トラブる箇所の特定)が目的なんですね。

 今は何も考えておりません。
 取り敢えず、隠居Zさんの意図が確認できただけで十分です。 m(__)m

(半平太) 2024/03/24(日) 17:22:34


半平太 さん
ありがとうございました。
また、お気づきの点等々御座いましたら宜しくお願い致します。
m(__)m
(隠居Z) 2024/03/24(日) 18:11:51

 > MyErrSub Err.Number, Err.Description, "errm01", "P1"

 この部分て「個別」に書くと面倒くさい気がします。

 「P1」は呼んだ側の引数から割り出せるし、
 "errm01" は "Module1"でも差し支えないですから、
 同じく呼んだ側の引数から割り出せるので、「一律」に書ける気がしました。

(半平太) 2024/03/24(日) 20:59:05


こんばんは^^
1時間ほど考えてましたが。。。^^;。。。(T_T)
ありがとうございます。どのようにすればよいのでしょうか
具体的な方法だけでも教えて戴ければ幸甚です。m(__)m
サンプルなどお示し賜ればとてもうれしいです。
<< _ _ >>
(隠居Z) 2024/03/24(日) 22:43:49

↑まったく急ぎませんお手すきの時にでも
今夜はお先に休ませて戴きます。m(__)mm(__)mm(__)m
(隠居Z) 2024/03/24(日) 22:46:34

  えっ、そんな大したことじゃないんですが・・

  Module1
  Option Explicit

  Private Sub main()
      Dim x, v(), eApp, i&, fps$, fNm$, eFlg As Boolean, xx()
      On Error GoTo errstp
      fps = ThisWorkbook.Path & "\"
      fNm = "errorlog.txt"
      Open fps & fNm For Output As #1
      Close #1
      Open fps & fNm For Append As #1
      Set eApp = Application
      v = Array("Module1.p1", "Module1.p2", "Module2.p3", "Module2.p4", "Module2.p5")
      ReDim xx(UBound(v))
      For i = LBound(xx) To UBound(xx)
          xx(i) = 0
      Next
      For i = LBound(v) To UBound(v)
           eFlg = CallByName(eApp, "Run", VbMethod, v(i), Split(v(i), "."))
           If eFlg Then xx(i) = 1
      Next
      Rem Err.Raise 13
      If Application.Sum(xx) <> 0 Then
          MsgBox "エラー発生ログを確認してください"
      Else
          MsgBox "正常終了"
      End If
      GoTo final:
  errstp:
      MyErrSub Err.Number, Err.Description, "errm01", "Main"
      MsgBox "エラー発生ログを確認してください"
  final:
      Erase v, xx
      Close #1
  End Sub
  Function p1(s, ParamArray dum())
      Dim x
      On Error GoTo errstp
      Err.Raise 1
      Exit Function
  errstp:
      MyErrSub Err.Number, Err.Description, s(0), s(1)
      p1 = True
  End Function
  Function p2(s, ParamArray dum())
      Dim x
      On Error GoTo errstp
      Err.Raise 2
      Exit Function
  errstp:
      MyErrSub Err.Number, Err.Description, s(0), s(1)
      p2 = True
  End Function
  Sub MyErrSub(num&, etx$, mtx, ptx)
      Dim ltx$
      On Error GoTo errstp
      ltx = "[" & Format(Now, " yyyy/mm/dd : hh:nn:ss ") & "]"
      ltx = ltx & Chr(32) & num & Chr(32) & etx & Chr(32)
      ltx = ltx & mtx & Chr(32) & ptx
      Print #1, ltx
      Rem Err.Raise 9
      Exit Sub
  errstp:
      MsgBox "MyErrSub エラーですシステム担当に連絡してください" & Chr(13) & _
             Err.Number & Chr(32) & Err.Description
      End
  End Sub

  Module2
  Option Explicit

  Function p3(s, ParamArray dum())
      Dim x
      On Error GoTo errstp
      Err.Raise 3
      Exit Function
  errstp:
      MyErrSub Err.Number, Err.Description, s(0), s(1)
      p3 = True
  End Function
  Function p4(s, ParamArray dum())
      On Error GoTo errstp
      Err.Raise 4
      Exit Function
  errstp:
      MyErrSub Err.Number, Err.Description, s(0), s(1)
      p4 = True
  End Function
  Function p5(s, ParamArray dum())
      On Error GoTo errstp
      Err.Raise 5
      Exit Function
  errstp:
      MyErrSub Err.Number, Err.Description, s(0), s(1)
      p5 = True
  End Function

(半平太) 2024/03/24(日) 23:47:21


 とても興味深い議論、有難うございます。

 CallByNameの使い方は参考になりました。
 ただこれを使うと、
 ・Functionプロシージャの実行時における引数の型チェックは、通常はコールした時点で行われますが、
   提案の方式だと、呼び出された側で発生することになるんですね。
 ・それと返り値がある場合には、エラーフラッグも併せて配列で返すことになりますか。
   少し手間がかかりますかね。

 ところで、
 一般ユーザーには無理ですが、ご自身であれば、
 エラートラップの3つのうち、「エラー発生時に中断」を一時的に選択すれば、
 On Error xx  などのエラートラップは無効となって、エラーになった箇所で確実に止まります。
 これでどこでエラーが発生しているかは分かりますが、これではまずいですか?
(xyz) 2024/03/25(月) 07:48:17

おはようございます。。。^^
半平太 さんへ
深夜に、お手数をおかけし、恐縮です。ありがとうございます。

なるほど。確かに呼んだ側に情報ありましたですね。

一度書いたものは、使い回そぉ。。。ですね(*^^*)
これだと、キーボードバヒャバチャしなくてもコピペでポイポイで済みますね
使わせて戴きます。有難う御座いました。
m(__)m

xyz さん へ
>>・Functionプロシージャの実行時における引数の型チェックは、通常はコールした時点で行われますが、
>> 提案の方式だと、呼び出された側で発生することになるんですね。
気が付いておりません。勉強になります。じっくり動作確認してみます。
>>・それと返り値がある場合には、エラーフラッグも併せて配列で返すことになりますか。
>> 少し手間がかかりますかね。
Application.Run使用時、引数の参照渡し不可の件でしょうか?
Applicationをバリアント型、若しくはオブジェクト型[Application禁止^^;]
でSetして CallByNameでラップすれば可能になるようです。
でもEXCELバージョンによって動作が変わるとか変わらないとか。。。( ̄▽ ̄)

私の今回の場合、値渡しばかりですので使ってみました。
配列引数は引数が無い場合パブリックモード関数ですと開発タブのマクロダイアログに
表示されてしまうので、防止策のダミー引数です。
プライベートにしとけよ。ってしかられそおですが。。通常のcallも試したくて。。。^^;
ご配慮とご案内、ありがとう御座います。私が使用する分は仰せの通りかと
存じます。
使用者は。。。大奥の御台様がお使いになるとかならないとか
停止などするとテレビ見てても呼びつけられますので。。。^^;
それはもうこわぁ〜いですよぉm(__)mm(__)mm(__)m
(*^^*)v
(隠居Z) 2024/03/25(月) 08:29:17


追伸
済みません↑隠居Z) 2024/03/25(月) 08:29:17の
>>プライベートにしとけよ。ってしかられそおですが。。通常のcallも試したくて。。。^^;

間違いです。そのような理由では無かったようです。パブリックにしなければ型違いエラー
が出て呼び出し出来ません、それで止む無くパブリックにし表示を止める対策に偽引数を
置いたようです。改めて訂正とお詫びを申し上げます。
Application.Run単独使用だと何でも呼び込んでしまうのですが。。。不思議だなぁ^^;
m(__)m

(隠居Z) 2024/03/25(月) 09:42:34


 >それと返り値がある場合には、エラーフラッグも併せて配列で返すことになりますか。
 提案いただいた参考コードはエラーフラグを返す例ですが、
 なんらかの数値なりを返すfunction procedureの場合は、
 フラッグとその数値なりを配列にして返すか、数値は参照渡しで受け取るということになるんですかね、
 という本題と離れた些末なことでした。

 >一般ユーザーには無理ですが、ご自身であれば、
 と書きましたが、
 そうですね、そういう高貴なかたから呼び出されたくはありませんね。
 ありがとうございました。

(xyz) 2024/03/25(月) 11:36:02


こちらこそ、ありがとう御座いました。
m(__)m
(隠居Z) 2024/03/25(月) 12:12:08

 隠居Zさん

 一つ教えてください。

 今回、CallByNameメソッドが使われているのですが、
 Application.Run メソッドを直接使っても、
 使い勝手は大差ない様な気がするんですが、
 何か、重要な違いがあるのでしょうか?

(半平太) 2024/03/25(月) 12:56:19


私がお教えする事など何も無いのですが。。。教えて戴く方なので^^;
実際にどうやって指定したかは記憶に無いのですが
Application.Run で呼び出すと参照渡しの引数を指定した場合反応してくれませんでした。
検索したところCallByNameの第一引数のオブジェクト指定の箇所に
バリアント型、若しくはオブジェクト型の変数にApplicationを格納して使うと
参照渡しが通常の様に使う事が出来るという内容の物でした。←多少問題点も有るらしいですが^^;
↓ 参考URLです。
https://www.excel-chunchun.com/entry/application_run_byref

新しいもの好きで。。。試して見ようかなとか。。。思っただけです。
まだ、詳しい調査はしておりませんです。( ̄▽ ̄:)現在進行形^^
わたしの誤解、理解間違い等ありましたらご指摘賜れば幸甚です。でわ
<< _ _ >>
(隠居Z) 2024/03/25(月) 13:51:54


追伸
(隠居Z) 2024/03/25(月) 09:42:34 は私の勘違いだったようです。プライベートでも
呼び出せます。他のエラーと被っていたのだと思います。。。なにせエラー処理なので
。。。←言い訳^^;m(__)mすみませんでした。
ついでに超簡単テストです

 Option Explicit
Sub apptest_main()
    Dim mx, eApp
    mx = 10
    Set eApp = Application
    Application.Run "Module1.app1", mx
    MsgBox mx
    CallByName eApp, "Run", VbMethod, "Module1.app2", mx
    MsgBox mx
End Sub
Private Sub app1(ByRef x)
    x = 1
End Sub
Private Sub app2(ByRef x)
    x = 2
End Sub

結果 10 と 2 でした
(隠居Z) 2024/03/25(月) 14:41:48


  隠居Zさん

  貴重な情報をありがとうございました。
  なんか、深遠な闇があるんですね。

  同氏の話だと、型.Run は
    型     結果
   Application   不可 1
   xlApp_App     不可 2
   xlApp_Obj     成功 3
   xlApp_Var     成功 4

  CallByName経由なら
    型     結果
   Application   成功 5
   xlApp_App     成功  6
   xlApp_Obj     成功  7
   xlApp_Var     成功  8

  とのことなので、隠居Zさんは上記「6」を採用したと言うことですね。

  まぁ今回は、渡す引数については、簡単なサンプルを使っているので余り深入りできないです。

  私としては、もう少し簡単にならないかなぁと思っていまして。
  「呼び出される方のErr.Number」を呼び出し元に返させる、と言う方法が使えないかな、と。
  その為、呼び出される方は、エラー処理を行わず、 On Error Resume Nextだけで終わりにする。

  具体的アイデア
  Module1

  Private Sub main()
      Dim x, v(), eApp, i&, fps$, fNm$, eFlg As Boolean, xx()

      On Error GoTo errstp
      fps = ThisWorkbook.Path & "\"
      fNm = "errorlog.txt"
      Open fps & fNm For Output As #1
      Close #1
      Open fps & fNm For Append As #1
      Set eApp = Application
      v = Array("Module1.p1", "Module1.p2", "Module2.p3", "Module2.p4", "Module2.p5")
      ReDim xx(UBound(v))
      For i = LBound(xx) To UBound(xx)
          xx(i) = 0
      Next

      Err.Raise 1
      Dim ret, errorOccured

      For i = LBound(v) To UBound(v)
         ret = CallByName(eApp, "Run", VbMethod, v(i), v(i))
          If ret(0) <> 0 Then
              Err.Raise ret(0)
          End If

          ret = Empty '戻り値の初期化
      Next

      Err.Raise 13
      If Application.Sum(xx) <> 0 Then
          MsgBox "エラー発生ログを確認してください"
      Else
          MsgBox "正常終了"
      End If

      Erase v, xx
      Close #1
      Exit Sub

  errstp:
      If IsArray(ret) Then
          errorOccured = ret(1)
           xx(i) = 1
      Else
          errorOccured = "Module1.Main"
           xx(1) = 1
      End If

      MyErrSub Err.Number, Err.Description, errorOccured
      Resume Next
  End Sub

  Function p1(s)
     Dim x

     On Error Resume Next
     Err.Raise 1
     p1 = Array(Err.Number, s)
  End Function

  Function p2(s, ParamArray dum())
     Dim x

     On Error Resume Next
     Err.Raise 2
     p2 = Array(Err.Number, s)
  End Function

  Sub MyErrSub(num&, etx$, errDetail)
      Dim ltx$
      On Error GoTo errstp
      ltx = "[" & Format(Now, " yyyy/mm/dd : hh:nn:ss ") & "]"
      ltx = ltx & Chr(32) & num & Chr(32) & etx & Chr(32)
      ltx = ltx & errDetail
      Print #1, ltx
      Rem Err.Raise 9
      Exit Sub
  errstp:
      MsgBox "MyErrSub エラーですシステム担当に連絡してください" & Chr(13) & _
             Err.Number & Chr(32) & Err.Description
      End
  End Sub

  Module2

  Function p3(s, ParamArray dum())
     Dim x

     On Error Resume Next
     Err.Raise 3
     p3 = Array(Err.Number, s)
  End Function

  Function p4(s, ParamArray dum())
     Dim x

     On Error Resume Next
  '   Err.Raise 4
     p4 = Array(Err.Number, s)
  End Function

  Function p5(s, ParamArray dum())
     Dim x

     On Error Resume Next
     Err.Raise 5
     p5 = Array(Err.Number, s)
  End Function

(半平太) 2024/03/25(月) 15:48:15


>>なんか、深遠な闇があるんですね。
同感です。。。^^;
貴重なサンプルのご提示、ありがとうございます。
以前に、教えて戴いた、大域変数保持方法[別ブック案]とあわせ
よく、勉強して、必ず、有用させていただきます。本当に勉強になります。
m(_ _)m
(隠居Z) 2024/03/25(月) 16:38:04

 >On Error Resume Nextだけで終わりにする。

 これはマズかったです。m(__)m
 現実のプロシージャは色んなことやっているはずなので、
 無益な処理を続けさせるべきではなかったです。

 あと、プロシージャ名を引数に渡す必要はなかったです。
 今呼び出したばかりの関数名に決まっていますので、
 呼出元で容易に割り出せます

 なので、pnプロシージャはこう言う形の方が良かったです。

 Function pn(ParamArray dum())   
    On Error GoTo final

    Err.Raise n
 final:
    pn = Err.Number
 End Function

 これで返り値も単純なLong値となり、
 呼出元での疑似エラー発動処理もシンプルになります。

 それにしても、そこらじゅうでエラーしまくりそうなプロジェクトなんですね。
 地雷原を歩く様で怖すぎ。私はこれ以上近寄らないことにします。

(半平太) 2024/03/26(火) 13:46:54


半平太 さん 重ね重ね、アドバイス、恐縮です。
>>それにしたも、そのらじゅうでエラーしまくりそうなプロジェクトなんですね。

(*^^;*)。プロジェクト作成者の性格がちゃらんぽらんなもので。つい。。。m(__)m

今、おさらいを兼ねて、お示し戴いたコードに書き換えておりました。
更なるご提案も取り込んでまいります。

本当に、ありがとうございました。m(__)m
(隠居Z) 2024/03/26(火) 14:47:04


 以下のように呼び出し元でエラーを取得するものを作ったことがあります。
 ErrObjectのSourceプロパティを利用して、各モジュール名.プロシージャ名を
 どのルートでエラーになったか追えるようにしていました。
 参考になれば。

 'Module1
 Option Explicit
 Const MODULE_NAME$ = "Module1"
 Public Sub Main()'呼び出し元
     On Error GoTo OnErr
     Call CatchErrProc
     Exit Sub
 OnErr:
     'エラー処理
     MsgBox Err.Number & vbCrLf & _
            Err.Description & vbCrLf & _
            Err.Source
 End Sub
 Public Sub MyErrRaise(ByRef srcErr As ErrObject, ByVal moduleName$, ByVal procName$)
     Dim track$
     track = moduleName & "." & procName & ";"
     If srcErr.Source <> "VBAProject" Then track = srcErr.Source & track
     Err.Source = track
     Err.Raise srcErr.Number, track, srcErr.Description
 End Sub
 Private Sub CatchErrProc()
     Const PROC_NAME$ = "CatchErrProc"
     On Error GoTo OnErr
     '適時コメントアウトしてください
     Fnc1
     Fnc2
     Fnc4
     Exit Sub
 OnErr:
     MyErrRaise Err, MODULE_NAME, PROC_NAME
 End Sub
 Public Function Fnc1()
     Const PROC_NAME$ = "Fnc1"
     On Error GoTo OnErr
     Err.Raise 1004
     Exit Function
 OnErr:
     MyErrRaise Err, MODULE_NAME, PROC_NAME
 End Function
 Public Function Fnc2()
     Const PROC_NAME$ = "Fnc2"
     On Error GoTo OnErr
     Fnc3
     Exit Function
 OnErr:
     MyErrRaise Err, MODULE_NAME, PROC_NAME
 End Function
 Public Function Fnc3()
     Const PROC_NAME$ = "Fnc3"
     On Error GoTo OnErr
     Err.Raise 5
     Exit Function
 OnErr:
     MyErrRaise Err, MODULE_NAME, PROC_NAME
 End Function

 'Module2
 Option Explicit
 Const MODULE_NAME$ = "Module2"
 Public Function Fnc4()
     Const PROC_NAME$ = "Fnc4"
     On Error GoTo OnErr
     Err.Raise 9
     Exit Function
 OnErr:
     MyErrRaise Err, MODULE_NAME, PROC_NAME
 End Function

(tkit) 2024/03/26(火) 16:01:07


tkit さん ご返信ありがとう御座います。^^
後ほど謹んで、拝見させて戴きます。
取り急ぎ御礼まで、様々な方法が勉強出来て
本当にうれしいです。m(__)m
(隠居Z) 2024/03/26(火) 16:56:44

 お陰様で、例外処理の理解が深まりました。

 ついでに「元の例外処理の有効スコープが切れない場合(※)」はどうなるか、
 ちょっと考えてみました。
 ※つまり、普通にCallできて、CallByName や Application.Run を使わないで済む場合

 この場合は、Call元に制御が戻るので、Call先にはエラートラップを設定する必要がないようです。

 Module1

 Private Sub main()
     Dim x, v(), eApp, i&, fps$, fNm$, eFlg As Boolean, xx()
     Dim ret As Long

     On Error GoTo errstp
     i = -1 '初期化

     fps = ThisWorkbook.Path & "\"
     fNm = "errorlog.txt"
     Open fps & fNm For Output As #1
     Close #1
     Open fps & fNm For Append As #1
     Set eApp = Application
     v = Array("Module1.p1", "Module1.p2", "Module2.p3", "Module2.p4", "Module2.p5")

     ReDim xx(UBound(v))

     Err.Raise 11

     i = i + 1: Debug.Print p1
     i = i + 1: Debug.Print p2
     i = i + 1: Debug.Print p3
     i = i + 1: Debug.Print p4
     i = i + 1: Debug.Print p5
     i = -1   '初期化

     Err.Raise 12
     If Application.Sum(xx) <> 0 Then
         MsgBox "エラー発生ログを確認してください"
     Else
         MsgBox "正常終了"
     End If

     Erase v, xx
     Close #1
     Exit Sub

 errstp:
     If i = -1 Then 'Mainで発生
         xx(0) = xx(0) + 1
         MyErrSub Err.Number, Err.Description, "Module1.Main"
     Else
         xx(i) = 1
         MyErrSub Err.Number, Err.Description, v(i)
     End If

     Resume Next
 End Sub

 Function p1(ParamArray dum())
    Dim x
    Err.Raise 1
    p1 = "p1成功"
 End Function

 Function p2(ParamArray dum())
    Dim x
 '   Err.Raise 2
    p2 = "p2成功"
 End Function

 Sub MyErrSub(num&, etx$, errDetail)
     Dim ltx$
     On Error GoTo errstp

     ltx = "[" & Format(Now, " yyyy/mm/dd : hh:nn:ss ") & "]"
     ltx = ltx & Chr(32) & num & Chr(32) & etx & Chr(32)
     ltx = ltx & errDetail
     Print #1, ltx
 '    Err.Raise 9
     Exit Sub
 errstp:
     MsgBox "MyErrSub エラーですシステム担当に連絡してください" & Chr(13) & _
            Err.Number & Chr(32) & Err.Description
     End
 End Sub

 Module2
 Function p3(ParamArray dum())
    Dim x
    Err.Raise 3
    p3 = "p3成功"
 End Function

 Function p4(ParamArray dum())
    Dim x
 '   Err.Raise 4
     p4 = "p4成功"
 End Function

 Function p5(ParamArray dum())
    Dim x
    Err.Raise 5
    p5 = "p5成功"
 End Function

(半平太) 2024/03/27(水) 11:54:54


普通にCallできる バージョン
ありがとうございます。^^
この案も考えていましたので。とてもうれしいです。(*^^*)v
後ほど、確認、お勉強させて戴きます。取り急ぎ御礼まで。
m(__)m

(隠居Z) 2024/03/27(水) 13:56:28


こんにちは ^^ 例外処理
1.コンパイルエラーさんは治さないと動きませんので論外としまして
  実行時エラーなのですが、皆様にいろいろ教えて戴きまして、結局
  あまり変わった事をせず。オーソドックスに行こうかなととか思っ
  ています。結果、下記の様なコード[殆ど半平太さんのパクリですが^^;]
  を参考に施工してみます。
2.論理エラー。。。も含めて実行時エラーも予測できるものは出来るだけ
  関数化して発生しない様に[ドライブ、ファイル、シート、データ件数
  日付、文字列、数値、配列等々の確認]すれば、地雷原の様な事は避け
  られるかもしれません。
3.呼び出し元の索引でエラーの発生先を抽出していますので
  xyzさんからもご指摘のありました、先日私のミスで発生した
  エラー先が呼び出し先なのに呼び出し元で起きたような表示に。。。
  なっていました。←開発時に留意していきたいと思います。
5.今後ともまた宜しくお願い致します。m(__)m
6.tkitさん に教えて戴いた ErrObjectのSourceプロパティ
  何でも書き込めソぉですね^^
  更に見識を深めていきたいと考えております。
皆様、貴重なお時間をおさき戴き、本当に有難うございました。m(__)m
  、
 Option Explicit
Sub Program_Main()
    Dim i             As Long
    Dim Pr()          As Variant
    Dim pRoc          As Variant
    Dim rEv           As Long
    Dim ErPlace       As String
    Dim ErrFlg        As Boolean
    On Error GoTo ErrStp
    i = -1
    Pr = Array("Module1.LogFile_Open", "Module1.MyErrSub", _
               "Module1.P1", "Module1.P2", "Module2.P3", "Module2.P4", "Module2.P5")
    Module1.LogFile_Open i
    Module1.MyErrSub "Test", "Test", "Test", i
    i = -1
    Rem ***********************************************************************
    Err.Raise 11
    i = i + 3: Module1.p1
    i = i + 1: Module1.p2
    i = i + 1: Module2.p3
    i = i + 1: Module2.p4
    i = i + 1: Module2.p5
    Rem ***********************************************************************
    i = -1
    Err.Raise 12
    GoTo Finally
ErrStp:
    ErrFlg = True
    If i = 0 Or i = 1 Then
        GoSub Finally
    End If
    If i = -1 Then
        ErPlace = "Module1.Program_Main" & " i = " & i
    Else
        ErPlace = Pr(i)
    End If
    MyErrSub Err.Number, Err.Description, ErPlace
    Resume Next
Finally:
    Close
    Stop
    If ErrFlg And i = -1 Then
        MsgBox "異常終了です。エラーログを参照してください"
    ElseIf ErrFlg And (i = 0 Or i = 1) Then
        MsgBox "エラーログを出力出来ません" & Chr(13) & Pr(i) & Chr(13) & Err.Number & Err.Description
    Else
        MsgBox "正常に終了致しました。"
    End If
    Erase Pr
End Sub
Sub LogFile_Open(ByRef i As Long)
    Dim fps           As String
    Dim fNm           As String
    i = 0
    fps = ThisWorkbook.Path & "\"
    fNm = "ErrorLog.txt"
    If Dir(fps & fNm) <> "" Then
        Kill fps & fNm
    End If
    Open fps & fNm For Append As #1
    Exit Sub
End Sub
Sub MyErrSub(num, etx$, mPtx, Optional ByRef i As Long)
    Dim ltx           As String
    i = 1
    ltx = "[" & Format(Now, " yyyy/mm/dd : hh:nn:ss ") & "]"
    ltx = ltx & Chr(32) & num & Chr(32) & etx & Chr(32)
    ltx = ltx & mPtx
    Rem Err.Raise 9
    Print #1, ltx
    Exit Sub
End Sub
Private Sub p1()
    Rem Err.Raise 1
    Debug.Print "p1"
End Sub
Private Sub p2()
    Err.Raise 2
    Debug.Print "p2"
End Sub

 Module2

 Option Explicit
Sub p3(ParamArray Dummy())
    Rem Err.Raise 13
    Debug.Print "p3"
End Sub
Sub p4(ParamArray Dummy())
    Rem Err.Raise 14
    Debug.Print "p4"
End Sub
Sub p5(ParamArray Dummy())
    Rem Err.Raise 15
    Debug.Print "p5"
End Sub

(隠居Z) 2024/03/29(金) 13:13:44


 済みません。CallByName って使わなくなったのですか?
 (そっちがメインの話と思っていたのですが)。

(半平太) 2024/03/29(金) 14:30:58


半平太さん こんにちわ ^^
何時もお世話になります。
>>済みません。CallByName って使わなくなったのですか?。。。
^^;。。。通常の方をお勧め戴いたのかと。。。思っていました。
理由
呼び出し先で、何もせずとも呼び出し元にエラーをキックバックしてくださいますので
手間いらずかなとか。

それと同時にマイクロソフト様の例外処理のHP、確かVB。NETだったかも。←に
try、catch、finally、若しくはUsing とon error の ご説明がありまして
勿論今回はvbaのお話でtry、catch、finally、若しくはUsinは有りません。

結論は通常コール、CallByNameどちらも一長一短で。
本音はお勉強を兼ねて双方で組み立ててみようと思っております。[暇ですし(*^^*)]
更に余裕があれば
ケースバイケースで両方混在 ← 他ブックのマクロから呼び出し?
エラー処理のクラス化 ← これは出来るかどうかあまり自信は御座いません。

毛躓きましたら、質問させて戴くかもしれません。又
引き続き、これはどぉだぁ〜。。。、これだけは覚えておいて損はないよ。等々御座いましたら
お教えいただければ幸甚です。m(__)m

何せ今まではマクロ組んでも殆ど自分用なもので、真面にエラー処理してませんので
やり方をお聞きしたくてご質問させて戴いた次第です。

(隠居Z) 2024/03/29(金) 16:51:13


 >通常の方をお勧め戴いたのかと。。。思っていました。
 いや、CallByNameって面白い発想だなと思っていました。
 やりたいのはこっちに違いないと早合点してしまったようです。
 (実行中のマクロ名が取得できるツールがVBAにもあれば、問題も小さくなるんでしょうが・・)

 >呼び出し先で、何もせずとも呼び出し元にエラーをキックバックしてくださいますので
 >手間いらずかなとか。
 私もそうは思ったのですが、言うほど簡単でもなかったです。

 例えば、テキストファイルのOPEN段階でトラブるかも知れないとしたら
 書込み工程なんてそもそも無理ですから、処理の場合分けが錯綜せざるを得ないです。

 > お陰様で、例外処理の理解が深まりました。
 とか言ってしまいましたが、時期尚早でした。
 調べを進めると、 On Error GoTo -1 てな文があり、
 いままで見たことなかった(か、もしくは、スルーしていたか)ですが、
 エラー処理では普通の知識らしいので、まだまだ道遠しの感があります。

 コードを目だけで追って、ここでエラーになると最終的に何が起きるか、
 なんて分かる人っているんでしょうかね。
 結果を見てから後講釈なら幾らでもいるんでしょうが。

 まっ、私自身はエラー処理はほどんど書かないで済ませている側なので
 人様にアドバイスできるレベルにないです。
 他の回答者のアドバイスをご期待ください。

(半平太) 2024/03/29(金) 17:41:43


こんばんは。夕刻から外出しておりまして、先ほどご返信を拝読させて戴きました。

>>コードを目だけで追って、ここでエラーになると最終的に何が起きるか、
>> なんて分かる人っているんでしょうかね。
絶対とは申しませんが、あまり、居られないのでは。。。で、
エラー処理が有るのではと思いますです。

On Error GoTo -1
初耳です^^;早速調べてみます。。(*^^*)
また、お気づきの点等々御座いましたら、宜しくお願い致します。
ありがとう御座いました。m(__)m
(隠居Z) 2024/03/29(金) 21:01:01


おはようございます^^。。。
白旗ふりふり。。。降参します。済みませんが
on error goto -1 使い方教えて戴けますか。

自己流の軽〜い実験^^;

 Option Explicit
Sub OneInstanceMain()
    Dim i As Long
    Dim v
    On Error GoTo ErrStp
    On Error Resume Next
    Err.Raise 13
    On Error GoTo -1
    Debug.Print Err.Number
    On Error Resume Next
    Err.Raise 9
    On Error GoTo 0
    Debug.Print Err.Number
ErrStp:
    On Error GoTo -1
    With Worksheets("Sheet1")
    End With
End Sub
同じってことはないですよね。0 と -1 ^^;
(隠居Z) 2024/03/30(土) 09:59:32

 >エラー処理が有るのではと思いますです。
 いえ、あのー、そのエラー処理が分かり難いと言う趣旨なのですが・・

 一般論として、Goto文は流れがスパゲッティ状になるので出来るだけ書くな、と言われますよね。
 でも、Goto文なんてかわいいものです。何故って、これから何処へ飛ぶよって目で追う事が出来ます。

 しかし、エラー処理文だと
  何処から→そんなの分かりません。エラーが発生した時に判明するのだから。
  何処へ →それは、そこに書いてないです、ずーっと前に書いてあったでしょう? 忘れたんですか?
 と言う調子です。悪質度はこっちの方が高いです。
 それでいて、必要悪として使わざるを得ない場面がある。。 困った奴です。

 普通は、問題が発生する直前でトラップ入れて、そこを抜ければ元に戻すのだろうなぁと思っています。
 ※冒頭にトラップを入れて、後は処理プロシージャに任せるのは荒っぽ過ぎると言う認識です。

 地雷原を走破するなんて特殊事情がある時、そんな丁寧なことはやってられない、と言うことなんですかね。

 以前、ここでプロの意見を拝聴したことがあります。
[[20150906185643]]
 『On Error ?ステートメントについて』(ichinose)

(半平太) 2024/03/30(土) 10:53:47


半平太さん おはようございます ^^ 
エラー処理その物に関してのご感想、有難う御座います。
この件に関しては同じく案外難しいのでは。と私も思っています。
>>On Error ?ステートメントについて (ichinose)
継続元のURL共々、チラッと拝見いたしました。双方ともかなりの長文の様なので
ゆっくり時間をかけてじっくり拝見いたします。貴重な情報のご提供、有難う御座いました。
。。。m(__)m

(隠居Z) 2024/03/30(土) 11:49:22


 >on error goto -1 使い方教えて

 あくまで、私が理解した範囲です。m(__)m

 軽〜い実験コードをどう使えばいいのか分からなかったので、
 もっと軽〜いのでやります。

 ※OneInstanceMain2は、ステップ実行のみにしてください(無限ループに陥りますので)

 Sub OneInstanceMain1()
     On Error GoTo ErrStp
     Err.Raise 1

     Debug.Print "終了"
     Exit Sub
 ErrStp:
     Debug.Print Err.Number
     On Error GoTo 0
     Debug.Print Err.Number
     Err.Raise 11 ' エラー処理中なので実行時エラーとなる
 End Sub

 Sub OneInstanceMain2()
     On Error GoTo ErrStp
     Err.Raise 2

     Debug.Print "終了"
     Exit Sub
 ErrStp:
     Debug.Print Err.Number
     On Error GoTo -1  ’エラー処理中であることがキャンセルされ、普通にCallされたことになる
     Debug.Print Err.Number
     Err.Raise 12 ’再度ErrStpへジャンプ→無限ループになる
 End Sub

 ※On Error GoTo -1 は エラー処理中フラグ(?)もキャンセルしますので、
  エラー処理内でエラーが発生しても、実行時エラーにならず、
  エラー発生の直前のエラートラップが適用されます。例の場合、再度ErrStpに飛びます。

 エラー処理コード内でのエラー発生がそもそもトラブルの元で、
 前提がおかしい気がします。(テストではいざ知らず)。
 なので、普通はこんな形なんですかねぇ・・

 Sub OneInstanceMain3()
     Dim i
     On Error GoTo ErrStp
     i = 1
     Err.Raise 3
     i = 2
     Err.Raise 13

     Debug.Print "終了"
     Exit Sub
 ErrStp:
     Debug.Print Err.Number
     Debug.Print "エラー発生後の処理" & i
     Resume Next
 End Sub

(半平太) 2024/03/30(土) 14:02:58


 >     On Error GoTo 0
 >     Debug.Print Err.Number
 >     Err.Raise 11 ' エラー処理中なので実行時エラーとなる
 エラー処理中であろうがなかろうが、On Error GoTo 0 でDefaultにしているので、
 実行時エラーになるのは当然でした。 m(__)m

(半平太) 2024/03/30(土) 14:11:11


半平太さん 詳細なご説明、有難う御座います。
後刻、動かして、理解を深めます。
いま、ご紹介の諸先輩先生のご意見交換を拝見していました。まだ半分ほど残っております^^;
取り急ぎ、御礼まで。m(__)m
(隠居Z) 2024/03/30(土) 15:24:37

>>エラー発生の直前のエラートラップが適用されます。
がOn Error Goto 0 との相違点との理解で宜しいでしょうか。^^
な〜んとなくですが相違点が解ったような気がいたします。
ありがとう御座いました。。。m(__)m
(隠居Z) 2024/03/30(土) 15:48:52

大ベテランの諸先輩諸氏でも様々にご意見が分かれるのですね。。。
でも、その中でも共通した認識も多数御有りの様なのでとても勉強になります。
差し詰め、私ですと、考えうるエラーにも浅学菲才のため抜け落ちも多き事になる
可能性大なので。。。例外処理が必要処理の3〜4倍に成ろうとも^^;
両道[例 IO処理 Dir()とOn Error 〜]を兼ね揃え、無駄の多い、石橋を
叩いて渡るようなコーディングになるやもしれません。

一関数のステップが増えれば増えるほど必要な例外処理も増えると
思いますので、内容によっては変更も有るとは思いますが基本は
一処理一関数一例外処理
に刻めばコードの可読性は少しはましかも、とか考えています。

あ!、Erl関数も勉強になりました、ログに追加しようと思います(*^^*)
m(__)m
(隠居Z) 2024/03/30(土) 20:29:04


コメント返信:

[ 一覧(最新更新順) ]


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