Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(指定なし : 指定なし)
『On Error Go To 0』がいまだに理解できない
投稿日時: 24/09/03 23:51:56
投稿者: けけちゃま

いつもお世話になっております。
初心者となります。。。
現在、VBAでアウトルックを起動するプログラム作ってます。
(と、いってもネットのコピペなんですが、ちゃんと一行ずつ理解しながらかいております)
 
https://gist.github.com/toshiyukino/10a7ecd4769875ac17ca
 
こちらに乗っているコードは下記の通りになっております。
 

Private Sub メール送信画面作成()
  Dim objOutlook As Object
  Dim objMail As Object
  Dim isInit As Boolean
  Dim strSignature As String
  Dim strBody As String
  
  '取り敢えず取得してみる
  On Error Resume Next
    Set objOutlook = GetObject(, "Outlook.Application")
  On Error GoTo 0
  
  If objOutlook Is Nothing Then
    'outlookは起動していないので新しく起動
    Set objOutlook = CreateObject("Outlook.Application")
    isInit = False
  Else
    isInit = True
  End If
  
  '新規メール作成
  Set objMail = objOutlook.CreateItem(0)  'olMailItem=0
  
  '件名
  objMail.Subject = "件名です"
  
  '画面表示(表示しないとデフォルト署名が取れない)
  objMail.Display
  
  '署名取得
  strSignature = objMail.Body
  
  '本文
  strBody = ""
  strBody = strBody & "○○ 様" & vbCrLf
  strBody = strBody & "" & vbCrLf
  strBody = strBody & "いつもお世話になっております。" & vbCrLf
  strBody = strBody & "" & vbCrLf
  strBody = strBody & "あの件です" & vbCrLf
  
  objMail.Body = strBody & vbCrLf & strSignature
  
  If isInit = False Then
    '新規に起動した場合は開放しておく
    Set objOutlook = Nothing
  End If
End Sub

 
 
この11行目の「On Error GoTo 0」の存在意味がわからなくて、これを消しても通常通り起動するのです。
なぜこれが必要なのか、教えていただければ幸いです。。
 
私と同様の疑問をもったかたのサイトを見つけました。↓
https://terakoya.sejuku.net/question/detail/33210
が、結局下記のコードも「On Error GoTo 0」を削除しても結果は同じだったのです。
分かりやすい短いコードの例文があればいいのですが…。
 
Sub エラー処理の例()

    ' エラーハンドリングを無効にするために On Error Resume Next を使用
    On Error Resume Next
    
    ' エラーハンドリングを無効化している間は、エラーが発生しても処理が中断されない
    Debug.Print 10 / 0
    
    ' エラーハンドリングを再度無効化するために On Error GoTo 0 を使用
    On Error GoTo 0
    
    ' 特定の箇所のみでエラーハンドリングを行いたい場合、ここでエラーハンドリングを有効化する
    On Error GoTo ErrorHandler
    
    ' エラーハンドリングが有効化されているため、エラーが発生した場合に対応する処理を記述できる
    Debug.Print 10 / 0
    
    Exit Sub
    
    ' エラーハンドラ
    ErrorHandler:
        MsgBox "エラーが発生しました!"
        ' エラーに対する具体的な処理を記述する
        Resume Next ' エラーハンドリングを再度無効化して次の行に処理を移行する

End Sub

回答
投稿日時: 24/09/04 07:19:13
投稿者: simple

Outlookが立ち上がっているかどうかを事前に簡単に調べる方法は無いので、
こうした場合、エラー覚悟で実行してみて、その結果で判断するという方法がとられます。
エラーで止まると困るので、On Error Resume Nexを使いますが、それが有効な期間は
できるだけ狭い範囲にすべきです。
 
というのは、On Error Resume Nextをそのままにしておくと、
本来エラーになるべき箇所があっても、何のメッセージも出なくなるので、
異常事態が隠蔽されてしまい、却って重大な問題になる可能性があるからです。
(初心者のかたが、プロシージャの先頭で、On Error Resume Nextとし、そのままにする
という例が時々ありますが、これは完全な悪手です。しかもデバッグの障害になってしまいます)
 
つまり、標語「On Error Resume Nextは出来るだけ狭い範囲で使う」ということです。
 
そして、On Error Resume Nextを無効にして、平常の処理に戻す命令、
これが「On Error Goto 0」の役目です
。(勿論無効にする対象はこれだけではないですが)
 
On Error Resume NextとOn Error Goto 0はペアで考え、ふたつは出来るだけ近くにあることがよいのです。
 
【補足】
先だってのmyResetですが、再帰的なタイマー予約がされているかどうかを調べる方法は無いので、
一律に予約取り消しをしています。
その際、タイマー予約がされていない(Sheet2を一度もアクティブにせずに閉じる)場合は
エラーで止まってしまいます。そこでOn Error Resume Nextを使ってエラーで止まらないようにしています。
 
上の議論からすると、

Sub myReset()
    On Error Resume Next
    Application.OnTime EarliestTime:=myTime, Procedure:="test", Schedule:=False
    On Error Goto 0
End Sub
と書くのが本来正しい。
ただ、直ぐにプロシージャを抜け、その場合は自動的にOn Error Resume Nextの効力はなくなるので省略したものです。
 
なお、ThisWorkbookモジュールで、Call Resetと書いたのは勿論ミスで、Call myResetが正しいですね。気づかれたと思いますが。

回答
投稿日時: 24/09/04 09:04:45
投稿者: MMYS

けけちゃま さんの引用:

この11行目の「On Error GoTo 0」の存在意味がわからなくて、これを消しても通常通り起動するのです。
なぜこれが必要なのか、教えていただければ幸いです。。

試しで消すなら
On Error Resume Next
の方です。アウトルックが起動してない場合、どうな動きしますか。
詳細はsimpleさんが解説されてます。
 
On Error Resume Next を日本語に直訳すると「エラー時は無視して次の行から再開」です。
なんか、そのままだとヤバそうです。
 
例えるなら、コロナで緊急事態宣言した。でも、解除せずそのまま何年も放置。みたいな。
 

回答
投稿日時: 24/09/04 09:58:00
投稿者: hatena
投稿者のウェブサイトに移動

すでに御二方から適切な回答が出ていますので、

けけちゃま さんの引用:

私と同様の疑問をもったかたのサイトを見つけました。↓
https://terakoya.sejuku.net/question/detail/33210
が、結局下記のコードも「On Error GoTo 0」を削除しても結果は同じだったのです。
分かりやすい短いコードの例文があればいいのですが…。

「分かりやすい短いコードの例文」を置いておきます。
 
Sub エラー処理の例()

    ' 以降のエラーを無視して次行から再開
    On Error Resume Next
    
    ' 0除算エラーが発生するが、無視して次行からコードを実行
    Debug.Print 10 / 0
    
    ' 以前のエラー処理宣言(On Error Resume Next)を無効化
    On Error GoTo 0
    
    ' 0除算エラーが発生するのでここでエラーメッセージが出て停止
    ' On Error GoTo 0がコメントアウトされている場合は、無視して次行へ
    Debug.Print 10 / 0

    ' 以降エラーが出たら、ErrorHandlerラベルへ移動
    On Error GoTo ErrorHandler
    
    '  0除算エラーが発生したので、ErrorHandlerラベルへ移動
    Debug.Print 10 / 0
    
    Exit Sub
    
    ' エラーハンドラ
    ErrorHandler:
        MsgBox "エラーが発生しました!"
        ' エラーに対する具体的な処理を記述する
        Resume Next ' エラーが発生した次行からコードを実行

End Sub

 
上記のコードで、「On Error GoTo 0」をコメントアウトした場合とそうでない場合で、ステップ実行して動作を確認してみてください。
動作の違いがわかると思います。

回答
投稿日時: 24/09/04 11:09:05
投稿者: Suzu

On Error Go To 0  の必要性について
 
VBAコードを実行する上で On Error Resume Next にて、
エラーを無視してコードを実行できるなら、
On Error Go To 0 しなくても そのままで良いと思うかも知れません。
 
ユーザー目線では、訳わからんエラーメッセージが出てこなくて良い。
確かにそうでしょう。
 
 
でも、コーディングを行う者としてみたら、
メッセージが出てくるという事は、意図しない動作をしているという事。
 
つまり、その時点で何かしらの不具合が出ているという事。
 (コード設計の不具合から、 VBA上の MS側の不具合の可能性もあります。)
それを、
On Error Go To 0
を入れずに 非表示にしたら・・・
 
本来の、求める処理が行われない。
ユーザーは、処理が正常に行われていると 認識しているけど
求める 処理は 中途半端で、本来の処理が途中で止まっている状態。
 
表示されたなら、
ユーザーは 異常が発生した事に気づき VBAのコーディング者に聴く 等のアクションが行われ
コーディング者も、正常に行われていないのがすぐに気づけます。
 
 
意図しないエラー発生を最小限とするため、
Resume Next と、Go To 0 の間隔を最小にします。

投稿日時: 24/09/04 22:35:53
投稿者: けけちゃま

simple様
 
いつもありがとうございます。
非常によくわかりました!一瞬でよくわかりました!
ご回答ありがとうございます。
 
 
【補足の件】
確かに前回質問のmyResetにもOn Error Resume Nextありました。
単に、私がぶち当たったエラーを無視して、予約タイマーをリセットすることなんだと理解しておりましたがOn Error Go To 0を省略したものだったんですね。
ダブルでよくわかりました。
 

引用:
その際、タイマー予約がされていない(Sheet2を一度もアクティブにせずに閉じる)場合は
エラーで止まってしまいます。

 
今、これをきいて思ったのですが、前回の質問の続きにもなってしまって大変恐縮ではございますが、簡易時計をSheet2のA1に置く目的が単に時計代わりであれば、Worksheet_Activate()ではなく、Workbook_Open() と Workbook_BeforeClose に書きコードを書けばいいかと思いつきました。
 
その場合なら、On Error Resume Next さえも書かなくてもよいと思い下記コードを書きました。
 
'【標準モジュール】
Option Explicit
Dim myTime As Date

Sub clock()

    myTime = CDate(Format(Now() + TimeValue("00:01:00"), "hh:mm:00"))
    Sheet2.Range("A1").Value = Now()
    Sheet2.Columns("A").AutoFit
    Application.OnTime myTime, "clock"

End Sub

Sub myReset()
    Application.OnTime EarliestTime:=myTime, Procedure:="clock", Schedule:=False
End Sub
myReset()
'【ThisWorkbook モジュール】

Private Sub Workbook_BeforeClose(Cancel As Boolean)
 Call myReset
End Sub

Private Sub Workbook_Open()

 Call clock

End Sub

 
ただ、実験しているんですが、ブックを閉じようとするとなぜかエラーが生じるんです。
 
ブック開く→clock()起動<時刻表示とともに次回起動予約がセットされる。Worksheet_Activate()に書くとSheet2がアクティブになるたびに次回起動予約がセットされるから、いろんな起動予約がごちゃごちゃになって時計が複雑な動きになる。Workbook_Open()にかけば、開いたときの1回だけ実行されるので、データをリセットしなくても、閉じるときにリセットさえすればいい?>
ブック閉じる→単に、clock()の次回起動予約を消すだけなのでエラーは想定されない。
と思うのですが…。なぜエラーが生じるんでしょうか・・・。
 
 
 
 
 
 

投稿日時: 24/09/04 22:40:13
投稿者: けけちゃま

 MMYS様
 
ありがとうございます。
エラー無視できるなんて超便利とも思っていたんですが、確かに気づかずエラーが積もるといつか大きな問題が発生しそうですよね。。
よくわかりました!!
 
コロナで緊急事態宣言してもコロナは完全になくなっていないわけだから、そのままなにもせずに放置したらまた緊急事態宣言をしなくてはいけないくらいの大問題に発展しますよね。
 
分かりやすいイメージありがとうございます笑

回答
投稿日時: 24/09/04 23:12:34
投稿者: simple

> ただ、実験しているんですが、ブックを閉じようとするとなぜかエラーが生じるんです。
閉じる時点でタイマー予約がされていないということだと思います。よく確認してください。
 
開くときにclockを実行しないように変更して、
開いたあとに閉じるとエラーになります。それは予約がされていないからエラーで当然です。
手動でclockを実行してから閉じるとエラーはでません。整合的な動きです。
 
そういうことに神経を使うのが無駄なのでOn Error Resume Nextを使っています。

投稿日時: 24/09/04 23:22:40
投稿者: けけちゃま

 hatena 様
 
ありがとうございます!
いまいちネットのやつはよくわからなかったのですが、コメントも書いてありすごくわかりやすかったです!!
素敵な例をありがとうございました泣!

投稿日時: 24/09/04 23:25:41
投稿者: けけちゃま

Suzu様
 
ありがとうございす。
たしかにユーザー側からすればストレスはないですが、エラーを無視しつづけたことで中途半場な処理になっているのは本末転倒ですもんね。
便利なコードかもしれませんが使い方を間違えてはいけないと思いました><!

回答
投稿日時: 24/09/04 23:29:41
投稿者: simple

続けての発言で恐縮。
> ただ、実験しているんですが、ブックを閉じようとするとなぜかエラーが生じるんです。
エラーメッセージは何で、どの行で発生していますか?
まさか、構文エラーじゃないですよね。

回答
投稿日時: 24/09/05 09:52:03
投稿者: 半平太

下は、AI先生の説らしいですが、全体的に頓珍漢な話に感じますねぇ。
特に(3)の説明はひどい。
 
>(1) ' エラーハンドリングを無効にするために On Error Resume Next を使用
> On Error Resume Next

>(2) ' エラーハンドリングを無効化している間は、エラーが発生しても処理が中断されない
> Debug.Print 10 / 0

>(3) ' エラーハンドリングを再度無効化するために On Error GoTo 0 を使用
> On Error GoTo 0
 
(3)が再度無効化することなら、(1)を書く意味さえなくなっちゃいます。
だって、On Error GoTo 0は、On Error Resume Nextを無いものにするんですから、
初めから(1)は要らない、と言うのが論理的帰結じゃないですか?
 

投稿日時: 24/09/05 22:03:39
投稿者: けけちゃま

simple様
 
申し訳ございません。
ご指摘のように私が実験といって手動でmyResetをしてから閉じていたようでした…恥
さすがでございます…。
普通に開けて閉じるの作業をしたらうまくいきました。
 
あとは、自動保存機能をオンにしていたのが原因だったのか保存されませんって警告ばかりでて、それがVBAエラーと関係していると勘違いしておりました。(なんか調べたらエクセルの自動保存機能はマクロとあまりいい影響をあたえないみたいでした)
 
https://learn.microsoft.com/ja-jp/office/vba/library-reference/concepts/how-autosave-impacts-addins-and-macros
 
構文エラーじゃなくてよかったです(ここで構文エラーなんてグーパンですよね苦笑)
 
ちなみに思ったのですが、今回のOutlookのコードは頑張れば、On Error Resume Next構文を使わなくてもかけるものなのでしょうか。(ふとした疑問なので、お手間取らせてしまうのであれなので、YESかNOかだけでもいいです。)
 

投稿日時: 24/09/05 22:12:12
投稿者: けけちゃま

半平太様
 
ありがとうございます。
AI先生の答えやっぱり変でしたか?!
(質問者様も途中で質問やめているし、自分の理解力の問題かと思ってました泣)
エラーハンドリングとう名前もややこしい、てなんで無効化しているのに再度無効化?とチーンって感じでした。
 
でも、hatena様のコードがめちゃくちゃわかりやすくて理解できました!
 

投稿日時: 24/09/05 22:41:21
投稿者: けけちゃま

simple様
 

引用:
Outlookが立ち上がっているかどうかを事前に簡単に調べる方法は無いので

 
申し訳ございません。最初に言ってくださっておりました…汗!!
Outlook起動時には
 
On Error Resume Next
On Error GoTo 0
 
を使います!!!

回答
投稿日時: 24/09/05 23:09:51
投稿者: simple

(1)
> 構文エラーじゃないですよね。
の件。
24/09/04 22:35:53発言にある
Sub myReset()
    Application.OnTime EarliestTime:=myTime, Procedure:="clock", Schedule:=False
End Sub
myReset()
この赤字のものは何でしょうか。
 
(2)
> 保存されませんって警告ばかりでて
それはOneDriveとの関係でしょ?
私はMS社の行き過ぎたOneDriveの宣伝だと思っております。
 
(3)
> On Error Resume Next構文を使わなくてもかけるものなのでしょうか。
自分がOutlookを立ち上げているかどうかは、操作者自身がよく知っていると考えるのが普通で、
常にコードでOutlookを立ち上げる仕様にしておき、
既存のインスタンスがあるかのチェックは端折って、
より簡単なコードにしておくという考え方もあるでしょう。
(むろん、常に立ち上げておいてから、エラーなしで GetObjectする手もあるでしょう。)
 
お尋ねの件を、出題されたクイズとして回答するなら、以下。
WordアプリケーションにTasksオブジェクト(これは現在立ち上がっているタスクを列挙するもの)があり、その Existsメソッドを使えば、Outlookが立ち上がっているかどうかを Booleanで返すことができます。
エラー処理は経由しません。調べて見て下さい。
ただし、そこまでするメリットは余り感じられません。

投稿日時: 24/09/05 23:46:43
投稿者: けけちゃま

simple様
 
(1)
 

Sub myReset()
    Application.OnTime EarliestTime:=myTime, Procedure:="clock", Schedule:=False
End Sub
myReset()

 
実はわたしも投稿したあとに「あ、余計なやつ入ってしまった・・」と思ったんですが、ばれましたよね。
my Reset はただの消し忘れであって、実験中は特にこれは起動しておりませんでした。
紛らわしくて申し訳ございません…。
 
  
(2)
!!
そうです!!OneDriveに保存しておりました!そやつだったんですね!
(OneDriveのメリットデメリットがよくわかっていないんですが、マクロとは相性悪いんですかね、明日ITの人に聞いてみます)
 
 
(3)
まさか答えがここまで返ってくるなんて…敬服いたします。ありがとうございます泣。
(なんか本当すみません、、すごい時間をさいてもらっている気がします)
Existsメソッドですね。ちょっと頭パンクしかけておりますが、参考にいたします!!
 
 

投稿日時: 24/09/06 21:16:52
投稿者: けけちゃま

皆様、ありがとうございました。
またお世話になる確率120%ですが、どうぞよろしくお願いいたします。