ブレイクポイントがない場所で止まる
VBAを実行すると、ブレイクポイントを貼ってないのになぜか「コードの実行が中断されました」とメッセージボックスが表示され、「デバッグ」ボタンを押すとVBAの画面でコードが止まっている箇所が表示されます。
ところが、表示されているVBAのコードを見ても、ブレイクポイントがありません。ブレイクポイントが無いのにコードが中断しているのです。
「は?」と思い、再度実行しても、また同じ場所で止まります。「いやいや、ブレイクポイント無いんですけど。なんで止まっちゃうの?」と不思議な事象を見ることになります。
この事象の原因は、過去の自分が貼ったブレイクポイントが内部的に残っているためです。通常、ブレイクポイントはデバッグ中に止めたい場所に設定します。そして、止める必要がなくなれば解除します。これが普通の使い方です。
しかし、「コードの実行が中断されました」のメッセージボックスが表示された場合は、見た目はブレイクポイントは見えないのにVBAの内部情報としてはブレイクポイントとして保持されたままになっており、「非表示のブレイクポイント」とも言うような状況に陥っています。
解決方法は「非表示のブレイクポイント」を消せばいい、ということになります。
解決方法
解決方法は以下の通りです。
- 「コードの実行が中断されました」のメッセージボックスの「デバッグ」ボタンを押します。
- VBAの画面がアクティブになり、ブレイクポイントがない行が表示されます。
- VBAの画面がアクティブの状態で、Ctrl キー + Pause/Break キーを2回(2回でまた止まる場合は1回)押します。見た目上は何も起きません。
- 実行ボタンを押して続行させるか、リセット(停止)ボタンを押して処理を止めます。
これで直るはずです。PCやExcelの再起動は必要ありません。
これでも直らない場合はPCの再起動をしてください。
発生原因
この事象が発生する原因は、Ctrlキー + Pause/Break キーで強制的に中断しているためです。
上にも書いていますが、ブレイクポイントの情報は見た目のブレイクポイントのマーク●の情報と、内部的にどこにブレイクポイントが設定されているか、という情報があります。通常のデバッグ時にブレイクポイントの設定と解除というVBA画面上での操作であれば、見た目情報と内部情報が一致しますが、Ctrlキー + Pause/Break キーで強制的に中断すると、見た目情報と内部情報に乖離が起きることがあります。
ただ、Ctrlキー + Pause/Break キーを押すと必ずこの事象が発生するわけではありません。
一般的に、Ctrlキー + Pause/Break キーを押すのは、なんらかの無限ループやそれに類する長い処理を止めたい場合に使います。言い方を変えれば、普通じゃない状態のときの緊急避難としての操作です。この操作はVBAの処理に対して割り込みを行うための操作で、「無理やりでいいからVBAを止めろ」という要求になります。
しかし、そんなことを言われても、Windowsがその割り込みを許可できない場合や、EscキーやCtrlキー + Pause/Break キーを押しながらタスクバーをいろいろクリックしまくって無理やり止める(タスク切り替えや処理停止のメッセージを連続で投げまくる)、なんてことをすると、VBA側で割り込みメッセージを適切に処理できず、本件の事象が発生します。
Windowsのイベント処理はWindowsプログラミングの中でも面倒かつ複雑な部類で、この事象は古いExcelでも発生していたため、多分ですがVBAの既知のバグです。まれにしか起きないのと修正が大変なので放置されているのかもしれません。
やらない方がいいこと
Application.EnableCancelKey というプロパティがあります。これは、外部のキー操作による割り込みをどのように扱うかを指定するプロパティです。
「Application.EnableCancelKey = xlDisabled」 と書くと、EscキーやCtrlキー + Pause/Breakキーを押された場合に、VBAの処理を中断しなくなります。なので、本件の事象の根本原因である「Ctrl + Pause/Breakで非表示のブレイクポイントが発生する」ということがなくなります。
ただ、このプロパティを使うことは本末転倒で、むしろ、マクロ利用者による強制割り込みを拒否したいなどのよほどの意図的な目的がない限りは使わない方がいいです。
「もしかしたらCtrl + Pause/Breakキーを押すと、コードの実行を中断しました、と出るかもしれない。そのときは見えないブレイクポイントが発生してしまい、VBAを実行する度に止まってしまう。なので、そうならないようにCtrl + Pause/Breakキーは処理しないようにしよう」という考え方は目的を誤ってます。
なので、本件事象の解決のために「Application.EnableCancelKey = xlDisabled」を書くのは目的を間違ってますのでやめましょう。