質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

Q&A

7回答

23808閲覧

DoEventsが無限ループを引き起こす

ExcelVBAer

総合スコア1175

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

0グッド

2クリップ

投稿2018/01/29 08:24

編集2022/01/12 10:55

処理が長くなるFor文で、Excelが固まらないよう、途中で定期的にDoEventsをさせてるのですが、
稀に、DoEventsが暴走し、説明しにくいのですが無限ループ的になってしまいます。

もしどなたか知見をお持ちでしたら、
ご教授頂けますと幸いです。

同じ状況を経験した、という方がいらっしゃれば、
ご連絡頂ければ何か糸口が見つかるかもしれません。

下記がDoEvents用のモジュールで、
For分の途中で「fDoEvents」をCallしており、
「★」の箇所で再帰的に関数が呼ばれるようになってしまいます。。。

処理としては、あるフォルダの容量を知る為に、
フォルダ内のファイルを全て検索し、
各ファイル容量を基にフォルダ容量を計算する、
という処理のファイル検索の途中でfDoEventsを呼び出しています。

どうぞ宜しくお願い致します。

VBA

1Declare Function GetInputState Lib "user32" () As Long 2Private DoTime As Double 3Public Function fDoEvents() 4 5 If GetInputState Then 6 Call prDoEvent 7 Exit Function 8 End If 9 10 '一定時間(1秒)たっていたらDoEvents 11 If Abs(Timer - DoTime) > 1 Then 12 Call prDoEvent 13 Exit Function 14 End If 15 16End Function 17 18Private Function prDoEvent() 19 20 '一定間隔処理用に処理時点の時間を保持 21 DoTime = Timer 22 23 '実行 24 DoEvents '★稀に、DoEvents実行直後に「prDoEvent」が呼ばれ、再帰的に呼ばれ続けてしまう 25 26End Function 27

ファイル検索用の関数を追記いたします(1/30)

VBA

1Public Function fFile_Path_in_Folder(Path_Folder As String, _ 2 Optional Extention As String = "*", _ 3 Optional FileAttribute As VbFileAttribute = vbNormal) As Variant 4 5 6 '- 指定フォルダが無かった場合、終了 7 If FSO.FolderExists(Path_Folder) = False Then Exit Function 8 9 'フォルダパスを格納 10 Dim Path_FD As String 11 Path_FD = Path_Folder 12 If Right$(Path_FD, 1) <> Application.PathSeparator Then 13 Path_FD = Path_FD & Application.PathSeparator 14 End If 15 16 Dim Dic As Scripting.Dictionary 17 Set Dic = New Scripting.Dictionary 18 19 '最初のファイルパスを取得 20 'Dir関数に検索パスを設定 21 Dim FileName As String 22 FileName = Dir(Path_FD & "*" & "." & Extention, FileAttribute) 23 24 Do While FileName <> "" 25 26 Call fDoEvents 27 28 Dim Path_File As String 29 Path_File = Path_FD & FileName 30 31 'ファイルパスを辞書に登録 32 If Directory_IsFolder(Path_File) = False Then 33 Dic.Item(FileName) = Path_File 34 End If 35 36 '次のファイルパスを取得 37 FileName = Dir() 38 39 Loop 40 41 'ファイルロックの開放(念のため) 42 Call Dir(vbNullString) 43 44 If Dic.Count = 0 Then Exit Function 45 46 '- 戻り値 47 fFile_Path_in_Folder = Dic.Items 48 49 Set Dic = Nothing 50 51End Function

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

imihito

2018/01/30 15:44

問題発生時に「表示 > 呼び出し履歴(Ctrl + L)」を表示すると、どのような履歴になっているのでしょうか?
ExcelVBAer

2018/01/31 03:45

ありがとうございます。再現した際に確認し、ご連絡いたします。
sazi

2018/03/05 12:19

この案件結構時間経過しましたが、その後どうなんでしょう?
ExcelVBAer

2018/03/06 04:43

気にかけて頂き、ありがとうございます。 しかし残念ながら、全く再現しなくなってしまい、何もわからない状況となっています。
sazi

2018/03/06 15:57

対処が有効だったって事ですか?
ExcelVBAer

2018/03/08 00:15

同じコードで再現が見られなくなった、という状況です。。。
guest

回答7

0

Doeventsで処理されているのは、こちらについての対応の為でしょうか?

でしたら、ループ中で毎回発行するのではなく10回に1回とかの割合で発行されるようにした方が良いのではないかと思います。(そもそも処理時間が掛かっている為の回避策に時間の間隔を条件にしてもという気が)

追記

無限ループというより、Doeventsの発行がなされないということは考えられないでしょうか。
現在は、ループ中でDoevensを呼び出していると思いますが、その処理自体が応答なしの状態では、Doeventsの発行のしようがないと思われます。

ループ開始前にApplication.OnTime メソッド (Excel)によって、定期的に呼び出すようにしてみてはどうでしょう。

因みに、Doeventsなどのイベント絡みをブレークポイントを設定して動作確認しようとしても、徒労に終わるケースが多いです。
なぜなら、ブレークポイントを設定すること自体でイベント消化が行われ、発生している事象の再現にはならないからです。

追記2

元々ループ中の処理

あるフォルダの容量を知る為に、フォルダ内のファイルを全て検索し、各ファイル容量を基にフォルダ容量を計算する

というのがどのようなコマンドで行われているのかは不明ですが、非同期処理であるが為に、Doeventsを行っているんだと思います。
この処理を同期型の処理に置き換えるのも別解になるかと。

投稿2018/01/29 10:42

編集2018/01/30 05:58
sazi

総合スコア25138

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ExcelVBAer

2018/01/30 04:14

時間がかかっている為の回避策ではなく、時間がかかって「応答停止」になるのを回避する為、という対処としています。 回数で判断しても良いのかもしれませんね。試してみます。 (といっても、どういう時に再現するかも判明できておらず、苦戦しております。。。)
ExcelVBAer

2018/01/30 05:53

すみません、For文ではなく、Do Loop の間違いでした。 該当関数 fFile_Path_in_Folder を追記してますので、 参照頂けますと幸いです。
sazi

2018/01/30 06:06

dir()ではなく、FileSystemObjectを使用し、インスタンスを生成することで同期処理となり、Doeventsは不要になると思われますが。
ExcelVBAer

2018/01/30 06:14

DoEvents は、処理が長い時に「応答なし」状態で固まるので、 それを回避する為に使用しております。 同期処理、という概念について不勉強なのですが、 Dir()が非同期処理で、FSOは同期処理、 ということでしょうか? Dir と DoEvents を同じようなタイミングで繰り返し呼び出す、 という事に問題があるということなのでしょうか?
sazi

2018/01/30 06:28 編集

逆説的に、Doeventsを使用されているということなので非同期処理として扱われているのだろう程度です。 しかしながら、Dir()関数はインスタンスを持ちませんし、非同期であることは十分考えられます。 ※ループで続きの処理になることからも、非同期で会話しているんじゃないかと。 FileSystemObjectは良く使用しますが、時間が掛かっても応答なしになったことはありません。 ※excel vbaで使用したことは少なく、主にvbsやaccess vbaでの話ですが。
sazi

2018/01/30 08:09 編集

>Dir と DoEvents を同じようなタイミングで繰り返し呼び出す、 >という事に問題があるということなのでしょうか? 時間が掛かる処理(応答を待っている処理)でDoevents(応答を要求する)のは、実行不能なのではないでしょうか。別なイベントを消化させるというのなら意味があるかもしれませんが、特に処理は行っていないのでしょうし。 なので、処理を進めたい処理とは、別なタイミングで行うのが真っ当ではないでしょうか。 ※今回であればOntimeメソッドなどで。
guest

0

現象確認できたわけではないので、気になる点だけ。

GetInputStateはキー入力などのイベントが発生すると0以外の値を返す(つまりTRUE条件に入る)ようです。

fDoEventsがループ処理により絶え間なく呼ばれ続けている場合、Timer判定では1秒以上の間隔をあけてprDoEventsを呼び出していますが、GetInputState判定の方は連続してイベントが発生すれば1秒以内でもprDoEventsを繰り返し呼ぶことになりそうです。
これを繰り返すうちにDoEventsが追い付かなくなるなんてことは・・・あるのかどうか。
それが原因で周回遅れのprDoEventsがDoEvents後に発生するなんてことが・・・あるのかどうか。

そもそもVBAは基本的にマルチスレッドもできませんし、割り込み処理が苦手なとこありますよね。。
私の環境ではループ処理でfDoEventsを呼び続けている最中にExcelシート上でセル編集モードに入ると、その時点でもともと動いていたマクロは中断してしまい、あまりアレコレ操作できませんでした(^_^;

投稿2018/01/30 07:07

jawa

総合スコア3013

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ExcelVBAer

2018/01/30 07:26

ご回答ありがとうございます。 セルで編集モードにしてしまうと、強制的にマクロが止まってしまうので、 個人的には「応答なし」のままでいいと思っていたのですが、 お客さん側から強めの要望でそれは困るということで。。。 (Excelのウィンドウを動かす等ができない、動いてるのか不安 etc) 「GetInputState」については、 外してみても、不具合が再現してしまい、 そのままにしている状況です。
guest

0

プログラムにおいて再帰というのは自身の関数を呼び出すことを言うので、この場合は当てはまらないと思います。
また無限ループという言葉も、ループ処理から抜け出せないことを指すので、これも当てはまらないと思います。
想定外に速い間隔で何回も呼ばれるということでしょうか。
なんとなく1秒経過したらの処理周りが怪しいようにみえます。
Timerというのはなんでしょうか?秒数が入っているのでしょうか?
差が1(秒)を超えていたらという判定文なのに、型がdoubleなのは正しいですか?
実は1ms間隔になっていないですか?

投稿2018/01/30 00:09

編集2018/01/30 00:11
ttyp03

総合スコア16996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ExcelVBAer

2018/01/30 04:36

説明しにくいのですが、★箇所で「prDoEvent」が呼ばれ、 その状況が無限に続くという状況が続きます。 もっと正確にいうならば、 ステップ実行で★箇所の次に「Private Function prDoEvent()」 の行へ進み、それが終わらなくなり、 本来呼び出されているはずのFor文へ戻らなくなる、 という表現になります。 Timer はVBAに備わっている関数で、 呼ばれた時点のその日の秒数が取得できます。 詳細はこちらを参照ください。 https://msdn.microsoft.com/ja-jp/vba/language-reference-vba/articles/timer-function?f=255&MSPPError=-2147217396 処理回数は計測して確認しましたが、 間隔は1秒毎で処理されていました。 型は仰る通り正しくはSingle型のようです。 訂正して様子をみてみます。 (ただ、こちらでも稀にしか再現せず、苦戦してます。。。)
ttyp03

2018/01/30 04:55

ふむ、時間計測周りは問題ないようですね。 こちらの知識不足で、逆に勉強になりました。 提示されているコード範囲では、prDoEvent()を呼び出している箇所は、fDoEvents()のみです。 他に提示コード外で呼び出している箇所はないのでしょうか? 非同期で動いている別の処理(イベントなど)から呼ばれているようなことはないのでしょうか? もうそれしか考えられません。 DoEventsを実行することでイベントが動いてしまうのであれば、EnableEvents で制御する手もあります。
ExcelVBAer

2018/01/30 05:08

fDoEvents が Public で、prDoEvents はPrivate です。 さらに、同モジュールには、上記のコードしかないので、 他からは fDoEvents のみ呼び出されるという状況です。 フォルダ容量計測処理では、ファイルを回しているループ内で、 1ループに1回「fDoEvents」を呼んでいるのみです。 確かにイベント等で何か知らない処理が挟まっている可能性もありますが、 当ツールは単純な処理のみで、イベント等で処理しているものはない状況です。 ほんと、困り果てております。。。
ttyp03

2018/01/30 05:14

原因究明はお手上げですが、以下参考までに教えてください。 ・暴走したときというのはどれくらいの間隔でprDoEventsが呼び出されるのでしょうか ・暴走したことによって何か弊害があるのでしょうか?(メイン処理側のループに戻らない?)  暴走してもDoEventsが頻繁に呼びされるだけなので、処理時間は伸びるかもしれませんが、実質処理に影響はないような気がします。 ・暴走したとき、メイン処理側のループにブレークポイントを置いても止まらないのでしょうか?
ExcelVBAer

2018/01/30 05:19

暴走した場合は、該当箇所にブレークポイントを置くと止まりますが、 処理としては、prDoEvents の無限ループが起こっており、終わらなくなります。 どれくらいの間隔で、という事に関しては現在調査中です (本来は1秒おきに呼ばれるはずなのですが。。。)
ttyp03

2018/01/30 05:24

思うに、prDoEvenetsは正常に処理されているように感じます。 むしろメイン処理側に問題があるのではないでしょうか? 例えば1から10まで回るFor文があって、その途中でfDoEventsを呼び出している。 しかし暴走してprDoEventsが何回も呼ばれる。 しかしFor文は継続して動いており、ブレークポイントで停止する。 となればFor文の終了条件が満たされず、無限ループしているのではないでしょうか?
ExcelVBAer

2018/01/30 05:34

For文に関する関数「fFile_Path_in_Folder」を追記しました。 すみません、Do Loop 文の間違いでした。 しかし、ブレークポイントで停止し、その後にステップ実行で処理を進めた際に、 ★部分の次に「Private Function prDoEvent()」へ移動する、 という不可解な事象が起こっております。 次に再現した際、ループ文側でブレークできるか試してみます。
ttyp03

2018/01/30 05:43

追記ありがとうございます。 見た感じ問題なさそうですね。 謎。
guest

0

無限ループにはまる原因は思い当たりませんが、Call fDoEventsの部分を純粋なDoEventsにしてしまえば良いのでは?
こうしてしまえば無限ループにハマる余地はなくなります。
この事例においては関数にするほどのものではないように思えます。
数万回のループならGetInputStateやTimerでの時間計測を入れることで劇的に処理速度が向上しますが、特定フォルダ内のファイル走査程度の繰り返し回数であれば、体感速度としてはそれほど変わらないと思われます。
もちろん、検索対象フォルダ内のファイル数が何千、何万もあるようなケースであれば別ですが。。。

VBA

1Public Function fFile_Path_in_Folder(Path_Folder As String, _ 2 Optional Extention As String = "*", _ 3 Optional FileAttribute As VbFileAttribute = vbNormal) As Variant 4 5 6 '- 指定フォルダが無かった場合、終了 7 If FSO.FolderExists(Path_Folder) = False Then Exit Function 8 9 'フォルダパスを格納 10 Dim Path_FD As String 11 Path_FD = Path_Folder 12 If Right$(Path_FD, 1) <> Application.PathSeparator Then 13 Path_FD = Path_FD & Application.PathSeparator 14 End If 15 16 Dim Dic As Scripting.Dictionary 17 Set Dic = New Scripting.Dictionary 18 19 '最初のファイルパスを取得 20 'Dir関数に検索パスを設定 21 Dim FileName As String 22 FileName = Dir(Path_FD & "*" & "." & Extention, FileAttribute) 23 24 Do While FileName <> "" 25 26 'Call fDoEvents 27 DoEvents '← 28 29 Dim Path_File As String 30 Path_File = Path_FD & FileName 31 32 'ファイルパスを辞書に登録 33 If Directory_IsFolder(Path_File) = False Then 34 Dic.Item(FileName) = Path_File 35 End If 36 37 '次のファイルパスを取得 38 FileName = Dir() 39 40 Loop 41 42 'ファイルロックの開放(念のため) 43 Call Dir(vbNullString) 44 45 If Dic.Count = 0 Then Exit Function 46 47 '- 戻り値 48 fFile_Path_in_Folder = Dic.Items 49 50 Set Dic = Nothing 51 52End Function

投稿2018/01/30 15:23

MyRadio

総合スコア19

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ExcelVBAer

2018/01/31 03:51

回答ありがとうございます。 確かに今の一般的なPCであれば数千程度の処理では不要でしょう。 しかし、PCのスペックが低い場合や、ファイル数が数万の場合等も考えられます。 そして最大の問題は『ループ中にDoEventsを呼ぶことによる異常現象』なので、 ファイルを検索というのは1ケースに過ぎず、 原因が分からない以上、他ケースのループ処理でも起こりうる、 と考えられますので、この機会に原因究明・解消できればと思い、 この場をお借りした次第です。
guest

0

本当にprDoEventが再呼び出しされているのなら、処理中フラグを設けてやって、

VB

1Dim procFlag as Boolean 2 3Private Function prDoEvent() 4 5 if procFlag then Exit Function 6 7 procFlag = True 8 9 '一定間隔処理用に処理時点の時間を保持 10 DoTime = Timer 11 12 '実行 13 DoEvents '★稀に、DoEvents実行直後に「prDoEvent」が呼ばれ、再帰的に呼ばれ続けてしまう 14 15 procFlag = False 16 17End Function

として、すぐに抜ければ良いように思います。

投稿2018/01/30 08:16

PineMatsu

総合スコア3579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ExcelVBAer

2018/01/30 08:48

ご回答ありがとうございます。 試してみます。
guest

0

DoEventsをうまく使うのページに、正解と思われるコードが掲載されています。
どのような意図で作ったコードなのか、どのように動作するコードなのかといった判り易い説明がされているので、参考になると思います。
こちらのコードを使ってみては如何ですか?

投稿2018/01/30 04:42

coco_bauer

総合スコア6915

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

VBA と VB.NET は別物です。どちらかのタグを外してください。見たところ VB.NET ではないですか?

DoEvents が Application.DoEvents ならここで無限ループはおかしいです。
メッセージによって呼び出されているのでしょうか?
それとも自作関数なのでしょうか?

無限ループと無限再帰は別物です。無限ループは終わりませんが、無限再帰はスタックオーバーフローですぐ終わります。

全体的に問題の前提とデバッグ方法が間違っていてコードも足りません。
これだけの情報では何もわかりません。

実行可能な最小限のソースを提示すれば何かわかることもあるかもしれません。

投稿2018/01/29 10:21

Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ExcelVBAer

2018/01/30 04:20

おっしゃる通り、無限再帰であれば終わります。 説明がしにくいのですが、 ★部分で再帰的に「prDoEvent」が呼ばれ、 その状況が無限に続くという状況が起こっています。 こちらはVBAですが、起こっている状況が不思議すぎるので、 ひょっとしたら、VB.NET方面の人ならご存じかも、 藁にもすがる思いでタグを付させて頂きました。 こちらの環境でも再現できる条件が判明しておらず、 同じ条件(同フォルダパス、同じファイル数)でも、 起こる場合と、起こらない場合があり、 しかも稀に起こるという状況です。
ExcelVBAer

2018/01/30 05:09 編集

DoEvents は 正確には Interaction.DoEvents です。 ※Application は Excel ないし、Word です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問