[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『(マクロ) Exit Subについて』(マイン)
お世話になっております。
質問は
マクロAで、Exit Subで抜けた場合
マクロBも含めてマクロを終了したい
UserFormを用いて処理をしています。
ユーザーが正しい操作をしない場合はExit Subで処理中止し
メッセージを出すと言ったものです
しかし、呼び出し先のマクロ内で、Exit Subが出ても、移行の処理は続けられます。
試しに マクロA の If a < 1 Then End
とすると、全てのマクロは停止(メッセージ、フォームも)してしまいます。
みなさまどうかアドバイスの程よろしくお願い致します。
Sub 呼び出し先マクロ()
Call マクロA
Call マクロB
End sub
↓呼び出し元↓
sub マクロA()
If a < 1 Then
MsgBox "処理を中止します" Exit Sub End If end sub
sub マクロB()
(処理)
end sub
< 使用 Excel:Excel2010、使用 OS:Windows7 >
マクロBもマクロAと同じ条件でExit Subする。のは どうでしょうか。 0.パラメータでBに渡す(参照渡し?) 1.変数aを作業用セルに変え共有する 2.パブリック変数にする(不安要素ありかも ^^;) 3.ファイルに書込Bから読み込む 4.レジストリ、使う
0.、1.がとても安全、確実 ^^ 3.めんど〜 4.はとてもむつかしいし、危険がいつぱい (私は解かりません。)^^; かな〜〜〜〜と想像します。 m(_ _)m
(隠居じーさん) 2018/11/21(水) 07:25
ステップ実行で動きを追ってみるとわかりますが、呼び出し先のプロシージャが終われば呼び出し元に戻る仕様です
<参考;ステップ実行>
https://www.239-programing.com/excel-vba/basic/basic023.html
また、Endステートメントはすべてのプログラムを終了させる命令です。
<参考;Endステートメント>
http://officetanaka.net/excel/vba/statement/End.htm
なので、そのような構成にしたいのであればば呼出"元"で条件分岐させてください。
Sub 呼出元マクロ() If a < 1 Then Call マクロA Else Call マクロB End If End Sub
(もこな2) 2018/11/21(水) 07:26
参照渡しでフラグを渡してしまうのもありですね。
Sub 呼出元マクロ() Dim 終了フラグ As Boolean
Call マクロA(終了フラグ)
If 終了フラグ Then Exit Sub
Call マクロB
End Sub
Sub マクロA(ByRef フラグ As Boolean) Dim a As Long
If a < 1 Then フラグ = True MsgBox "処理を中止します" Exit Sub End If End Sub
【参照渡し】
http://officetanaka.net/excel/vba/tips/tips94.htm
また、複数のプロシージャから同じ変数を使いたいのであれば、グローバル変数にするという手もあります。
【グローバル変数(モジュールレベル変数)】
http://officetanaka.net/excel/vba/variable/05.htm
(もこな2) 2018/11/21(水) 07:41
朝早くからありがとうございます。
説明不足でしたので追加させてください。
マクロAですが、処理内容はエラーチェック用に作成しました。
条件によって、Exit Subで抜ける
If a < 1 Then
のようなものが5つほどあります。
あれから、ネットで探していたところ
まさに、やりたいことのコードをを見つけました。
しかし、x.text>0 then でオブジェクトが見つかりませんと弾かれました。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11117180951
Private sub test()
dim chck As Boolean
Hantei(chck)
If chck then
Msgbox("Exit で抜けました")
Exit Sub
Else
Msgbox("通常に抜けました")
End If
End sub
Private sub hantei(chck As Boolean)
dim x as Variant
If x.text>0 then ←エラー
chck = True
Exit sub
Else
chck = False
End if
End sub
(マイン) 2018/11/21(水) 09:04
前回は不発だったので、、スミマセン(笑)
私のほうからは、まだ誰からも提案のないやり方を一つ。。
Subから処理の成否を返せるFunctionに変更してはどうでしょうか?
この方法はかなり一般的なやり方だと思います(^^)
Option Explicit
Sub 呼出元マクロ()
If Not マクロA() Then ''マクロA() = Falseでも可 MsgBox "処理を中止します" Exit Sub End If
Call マクロB
End Sub
''Subから値を返せるFunctionに変更します Function マクロA() As Boolean
Dim a As Long
''この行はなくてもマクロAの初期値はFalseだが年の為に(^^) マクロA = False
If a < 1 Then Exit Function
''色々な処理 ''・・・
''処理が正常終了したら マクロA = True
End Function
Sub マクロB() ''(処理) End Sub
判定箇所が何個もあるとのことですが、
正常終了したと思われるところにマクロA = True を書いておけば
途中で抜けた場合は、マクロAの値は全てFalseになり処理を中断出来ますね!
(みそじのおじさん) 2018/11/21(水) 09:11
たぶんActiveXのテキストボックスがあってその名前がxなのだろう。 そしてxが0より大きければExit subするのだろう。 なので、 >If x.text>0 then 部分が処理をマインさんの判断基準に置き換て見てくれ。 (ねむねむ) 2018/11/21(水) 09:17
投稿から10分足らずでアドバイスありがとうございます。
皆さんのを参考に挑戦して結果をご報告いたします。
(マイン) 2018/11/21(水) 09:21
はじめてのことでよく分からずにアドバイスコードを記述しまいたが
「配列には割り当てられません」とでます。
'★初期値を念のためにFalseにする リストボックス_エラーチェック = False
'★処理完了後にTrueにすれば、読み込みマクロ先で本マクロでエラー抜け時に中断可能 リストボックス_エラーチェック = True
すみません、アドバイスをお願いできますでしょうか
'【重要】ファンクション化
'リスト選択内容と実在するシートを選択したうえで所定の処理を実施
Function リストボックス_エラーチェック() As Boolean()
Dim List選択 As Long, List数 As Long 'リスト選択有無チェック Dim j As Long 'ListBox内の値をReDim Preserveで追記 Dim i As Long 'ListBox用カウンター Dim strSelect() As String 'ListBox内の値を格納 Dim ii As Long 'Sheetエラー用カウンター Dim jj As Long 'Sheetエラー値をReDim Preserveで追記 Dim msg As String 'Sheetエラー用 Dim strError() As String 'Sheetエラー値を格納
'★初期値を念のためにFalseにする リストボックス_エラーチェック = False ←配列には割り当てられません。とでます。
With ListBox1 '------------------------------------------------ '★リストを選択しているかチェック For List選択 = 0 To ListBox1.ListCount - 1 If ListBox1.Selected(List選択) = True Then List数 = List数 + 1 End If Next If List数 < 1 Then '格納値があれば MsgBox "リストを選択してください", vbOKOnly, "リスト未選択" Call 全てのフォーム色を通常に戻す Call Label_wの制限解除 Exit Function End If '------------------------------------------------ '★Listが選択状態であれば下記処理 j = -1 'ListBox内で選択された項目 jj = -1 'ListBox内の存在しないSheet抽出 For i = 0 To .ListCount - 1 'List内のリスト数分ループ If .Selected(i) Then '選択行ならば j = j + 1 'リスト項目の取得と格納 ReDim Preserve strSelect(j) strSelect(j) = .List(i, 1) 'List値(行,列)=シート名取得 '------------------------------------------------ '★シート存在チェック 【重要】Function shCheck(name As String)と連携 If Not (shCheck(strSelect(j))) Then jj = jj + 1 'エラーSheet名取得と格納 ReDim Preserve strError(jj) strError(jj) = strSelect(j) End If '------------------------------------------------- End If Next i '------------------------------------------------- '★シート存在チェック(存在なし:メッセージ表示とExit Sub) If jj > -1 Then For ii = LBound(strError) To UBound(strError) msg = msg & strError(ii) & vbCrLf 'msg に配列の中身を連結しながら格納 Next ii MsgBox "【選択された記録が見つかりません】" & vbCrLf & _ vbCrLf & msg & vbCrLf & "※リストとシート名を確認してください", vbOKOnly, "Sheetエラー" Call 全てのフォーム色を通常に戻す Call Label_wの制限解除 Exit Function End If '------------------------------------------------- '★格納したシート名を選択し形成処理 If j > -1 Then '格納値があれば Worksheets(strSelect).Select 'リスト名と同じシート名を選択する End If End With
'★処理完了後にTrueにすれば、読み込みマクロ先で本マクロでエラー抜け時に中断可能 リストボックス_エラーチェック = True
End Function
(マイン) 2018/11/21(水) 10:55
>Function リストボックス_エラーチェック() As Boolean() を Function リストボックス_エラーチェック() As Boolean に。 (ねむねむ) 2018/11/21(水) 11:10
取り急ぎ報告です。
動きました。
このコードを使い回しすれば、不要なコードの削減に繋がります。
すごいです。
もうしばらく、弄りたいと思います。
(マイン) 2018/11/21(水) 11:34
参考にしてください インプットボックスで1と入力された時は意図的にエラーを発生させて Macroの最後の行まで飛びます。 それ以外の場合は正常に動作します。
条件分岐の部分を直せばそのまま使えると思います。 考えてみてください。
------------------------------------------------------ Sub Macro() On Error GoTo LINE Call A Call B LINE: End Sub
-------------------------------------------------------
Sub A() If InputBox("入力") = "1" Then Err.Raise 1 End Sub
-------------------------------------------------------
Sub B() MsgBox "Hallo" End Sub
------------------------------------------------------- (TAKA) 2018/11/21(水) 11:41
------------------------------------------------------ Sub Macro() On Error GoTo LINE Call A Call B LINE: On Error GoTo 0 End Sub
-------------------------------------------------------
Sub A() If InputBox("入力") = "1" Then Err.Raise 1 End Sub
-------------------------------------------------------
Sub B() MsgBox "Hallo" End Sub
------------------------------------------------------- (TAKA) 2018/11/21(水) 11:43
ありがとうございます。
超シンプルです。
Err.Raise 1を加えるだけで!!
しかも、正常に動きました。
If List数 < 1 Then '格納値があれば MsgBox "リストを選択してください", vbOKOnly, "リスト未選択" Call 全てのフォーム色を通常に戻す Call Label_wの制限解除
Err.Raise 1 'Exit Sub
End If
(マイン) 2018/11/21(水) 12:05
Err.Raise 1
とは、IF文で何らかのエラーが発生したら、あえて
「1」のエラーを発生させる
→アプリケーション定義またはオブジェクト定義のエラーです。”のエラーが発生
それを、取得して On Error GoTo LINE につなげるという認識でよろしいでしょうか?
(マイン) 2018/11/21(水) 12:13
はいその通りです。 お役に立ててよかったです。 (TAKA) 2018/11/21(水) 18:07
皆様ありがとうございました。
無事に完動しました。
おかげさまで、コード全体の縮小化できました。
解決です。
(マイン) 2018/11/21(水) 20:55
私は、みそじのおじさんの案がまっとうだと思っていたのですがねぇ・・
(半平太) 2018/11/21(水) 22:01
皆様、こんばんは。
解決したとのことで不要かとは思いますが。。 私も、もちろんErr.Raiseさせる時もあるのですが、一つだけ注意点があります。 マインさんのこのファイルは御自身一人でで使う前提ですか?それとも配布予定ですか?
エラートラップには各PC毎に(個人個人のExcel毎に)設定が異なっていて その設定が違えば意図しないエラートラップになり期待した効果が得られない場合があるので注意が必要ですね!
VBEのエディタに ツール オプション 全般タブに エラートラップ という項目があり ・エラー発生時に中断 ・クラスモジュールで中断 ・エラー処理対象外のエラーで中断
この3つがあり、デフォルトでは確か3番目のエラー処理対象外のエラーで中断になっているはずですが ここを他の二つに設定していると、場合によってはマインさんが設定したエラートラップに飛ばなくなります。エラーの旨を示すメッセージボックスが現れ終了ボタンを押してしまうとそこで処理が終了します。
このエラートラップの設定はファイルを配布しても同じ設定にはなりません。 これは各Excelアプリケーション独自の設定なのです。
私の経験ですが、配布先でこのエラートラップの設定が私と異なり、エラー発生後の重要な後処理を行えずに 処理が終了してしまってデータベースに甚大な被害を与えてしまったという苦い思い出があります^^;
もし配布するということであれば、配布先のエラートラップの設定を確認する事をお薦めしますね(^^)
・エラー発生時に中断 これになっていると問答無用でエラーのトラップは出来ません!! ただし、エラーの発生場所を突き止めるには一番良い設定です。これはデバック用の設定ですね。
・クラスモジュールで中断 ユーザーフォームやクラスモジュールを使用している場合にデバック用に設定する事がありますが メインの呼び出し側が標準モジュール側で、On Errorを仕掛けても実際の処理がユーザーフォームモジュール内になっていたりするとエラーのトラップが出来ません!要注意です。
・エラー処理対象外のエラーで中断 On Error を使っていない箇所だと中断してしまいますが、マインさんのコードですと この設定になっていないと駄目だと思います(^^)
あーだこーだ書きましたが 私もエラートラップの設定を確認してErr.Raise "は”"使っています! 設定さえ合っていれば便利なものですね(^^)
ちなみにErr.Raiseはマインさん独自の番号を割り当てが出来て、メッセージも適切なものに変更できますよ! 参考のURL貼っておきますね。 https://qiita.com/ririn_yume/items/13d3b7aad7bc49411eb7
(みそじのおじさん) 2018/11/21(水) 23:18
お世話になっています。
エラーについてのアドバイス感謝します。
・エラー処理対象外のエラーで中断
の部分ですが、当初のアドバイスコードでは問題なかった処理が、
Err.Raise導入で、予期せぬ部分で処理がストップする事態に陥っていました。
関係するコード全てに対しOn Errorを加えてとりあえず回避することができました。
当初のコードとErr.Raiseはよく考えて使わければと後々気づきました。
全体をもう一度見直し、どちらの処理が適しているか考えたいと思います。
(マイン) 2018/11/22(木) 07:59
>関係するコード全てに対しOn Errorを加えて
これもピンとこないですねぇ・・
旨くいったならこれ以上言うのは野暮ですが、一般論として言わせていただくと、 ここで質問している人のレベルでOn Errorを書きまくるなんて、 とてもまともなものに仕上がるとは思えないです。
老婆心からです、悪しからず m(__)m
(半平太) 2018/11/22(木) 09:01
そうですね、後付でErr.Raiseするとそういう状態に陥ることはありますね^^;
私は"一般的に"とやわらかく表現しましたが、本当は"Functionにするべき!"
と書きたかったのが本音です(笑)
超私見ですが、色々なやり方はありますがほとんどの方がFunction派だと私は
勝手に思っています(^^)
前回ご紹介したAPIのSetTimer関数ですが Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, _ ByVal nIDEvent As Long, _ ByVal uElapse As Long, _ ByVal lpTimerFunc As Long) As Long
タイマーのセットに成功したらLong型のタイマーID(0以外)、失敗すれば0が返されるという
仕様になっています。ですのでユーザーは0が返ってくれば失敗した!という認識で
後処理をすれば良いとなります。
これはWindowsがプログラマに対して公開しているAPIですので
やはりFunction化は”一般的”と言えるのではないでしょうか?
#私はVBAをやられている方とメールのやり取りをする事がありますが、
#この件については、「やはりFunction化だね!」とか「テスト環境に限ってErr.Raiseを使用する」
#なんて話も聞きましたね(^^)
(みそじのおじさん) 2018/11/22(木) 09:07
VBAはSubが標準なのでFunction使えない方が多いのですが、関数型言語では常識中の常識です。
C言語なんかでは真っ先に覚える話です。
int main(){return 0;} は正常終了
int main(){return 1;} は異常終了(であることを呼び出し元に通知する)
Err.Raiseはクラスなんかで様々なパターンのエラーを呼び出し元に通知する時に使うくらいで、初心者が使うようなものではないと私は考えます。
(名無し) 2018/11/22(木) 09:46
>Err.Raise導入で、予期せぬ部分で処理がストップする事態に陥っていました。
予期せぬ部分でストップ(GoTo LINE)してしまうということは、 Err.Raiseを書く場所が間違っているか、条件分岐が正しくない、 もしくはErr.Raise以外の部分で別のエラーが発生しています。 ステップ実行でマクロの動きを確認した方がいいです。 コードを見直してください。
>関係するコード全てに対しOn Errorを加えてとりあえず回避することができました。
On Error はエラーになる箇所や原因を理解している上で、想定内のエラーに対して使うものです。 よく分からないけどとりあえず回避する、というように安易に使うべきではありません。 修正しないといけない重大なエラーに気づかず、取り返しの付かないことになる可能性があります。
ステップ実行などで、何故予期せぬ部分でストップしてしまうのか分からなければ この方法を無理矢理使うのはオススメできません。 こんなやり方もあるんだな、と参考程度にして、一般的な方法でやってみてください。
ちなみに、配列には割り当てられませんと出るのは Function リストボックス_エラーチェック() As Boolean() ←この最後の括弧が原因です。 (TAKA) 2018/11/22(木) 10:16
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.