トップ回答者
VBAでWSHをEXECで実施時の終了確認について

質問
-
EXCELのVBAで次のようにWSHのEXECでDOSコマンドを実行しているのですが、Do While~Loopでいつまでループし続け終了しません。何か間違った記述があるのでしょうか。どなたか、ご教示いただけないでしょうか。
Sub Sample2()
Dim WSH, wExec, sCmd As String, Result As String, tmp, i As Long
Dim tSht As Worksheet
Set WSH = CreateObject("WScript.Shell")
Set tSht = ThisWorkbook.Sheets(1)
Set Use_range = tSht.UsedRange
Rows(1 & ":" & Use_range.Rows.Count).Delete
sCmd = "dir C:\ /b /s"
Set wExec = WSH.Exec("%ComSpec% /c " & sCmd)
Do While wExec.Status = 0
DoEvents
LoopResult = wExec.StdOut.Readall
tmp = Split(Result, vbCrLf)
For i = 0 To UBound(tmp)
Cells(i + 1, 1) = tmp(i)
Next i
tSht.Range("A1").Activate
Set wExec = Nothing
Set WSH = NothingEnd Sub- 移動 栗下 望Microsoft employee, Moderator 2018年3月29日 4:04 Windows Powershell > Windows PowerShell
回答
-
© ウィンドウズスクリプトプログラマ - Windows Script Programmer 2018
calc、windowアプリの場合はstdoutがありません。一方、consoleアプリの場合、
Set wExec = WSH.Exec("%ComSpec% /c " & sCmd)
Do While wExec.Status = 0
DoEvents
LoopResult = wExec.StdOut.Readallでは
Do While wExec.Status = 0
は不要です。conditional readでないので、stdout.readallで待ち合わせされます。
DoEvents
Loop
それがあると、パイプ詰まりとデッドロックになって、永久待ちになります。
今回のケースは、stdout.readallで待ち合わせ可能でしょうが、一般的には、stderrもパイプ詰まりの可能性があります。つまり、execの実装は一般的に問題のある実装です。問題回避のテクニックが必要。例えばstderrをファイルにリダイレクトするとか。- 回答の候補に設定 牟田口大介 2018年3月28日 12:35
- 編集済み ウィンドウズスクリプトプログラマ 2018年3月28日 13:31
- 回答としてマーク bearbook41 2018年4月1日 5:35
-
フォーラム オペレーターの栗下 望です。
bearbook41 さん、こんにちは。
ご質問いただいた内容から 「Visual Basic for Application(VBA)」 フォーラムに
スレッドの移動をさせていただきました。
※ご自身のスレッドは画面左上の「クイック アクセス」の「マイ スレッド」よりご確認いただけます。既に皆様から返信が寄せられておりますので、
確認いただき参考になった回答には [回答としてマーク] をご設定くださいね。どうぞよろしくお願いいたします。
~ 参考になった投稿には回答としてマークの設定にご協力ください ~
MSDN/TechNet Community Support 栗下 望- 編集済み 栗下 望Microsoft employee, Moderator 2018年3月29日 4:06 移動後文言へ編集
- 回答としてマーク bearbook41 2018年4月1日 5:28
すべての返信
-
内容的に、
質問する場所が、Windows PowerShell ではなく Visual Basic for Application(VBA) なのではないかと思います。
コマンドプロンプトで、以下のコマンドを入力すると、とめどなくデータ表示されることを確認しました。
(途中で、Ctrl + C キーを押下して、停止させました)
dir C:\ /b /s
コマンドプロンプト上で、「dir /?」と入力して、オプションの説明を確認しましたところ、
以下のような説明内容でした。
/B ファイル名のみを表示します (見出しや要約が付きません)。
/S 指定されたディレクトリおよびそのサブディレクトリのすべての
ファイルを表示します。
上記から、VBA では以下のように修正したところ、すぐに結果が表示されました。
(C ドライブ直下のフォルダのみの命令)
'sCmd = "dir C:\ /b /s"
sCmd = "dir C:\ /b"
C ドライブを起点として全てのフォルダを表示させるのは、あまりにも大量にありすぎるため、現実的ではないかもしれません。
-
© ウィンドウズスクリプトプログラマ - Windows Script Programmer 2018
Do While wExec.Status = 0
を削除。不要。
DoEvents
Loop
すみません、なぜ不要なのか、教えていただけるでしょうか。というのも、調べてみたら、MSDNの説明に、このようなコードが書かれているからです。
https://msdn.microsoft.com/ja-jp/library/cc364410.aspx より
Dim WshShell, oExec Set WshShell = CreateObject("WScript.Shell") Set oExec = WshShell.Exec("calc") Do While oExec.Status = 0 WScript.Sleep 100 Loop WScript.Echo oExec.Status
DoEvents が悪いのか、stdout のバッファを読み出しながら Status の変化を待たないといけないのか。
# いや、stdout からの読み出しが終わった時点でコマンドも終了しているように思うけど。Jitta@わんくま同盟
-
© ウィンドウズスクリプトプログラマ - Windows Script Programmer 2018
calc、windowアプリの場合はstdoutがありません。一方、consoleアプリの場合、
Set wExec = WSH.Exec("%ComSpec% /c " & sCmd)
Do While wExec.Status = 0
DoEvents
LoopResult = wExec.StdOut.Readallでは
Do While wExec.Status = 0
は不要です。conditional readでないので、stdout.readallで待ち合わせされます。
DoEvents
Loop
それがあると、パイプ詰まりとデッドロックになって、永久待ちになります。
今回のケースは、stdout.readallで待ち合わせ可能でしょうが、一般的には、stderrもパイプ詰まりの可能性があります。つまり、execの実装は一般的に問題のある実装です。問題回避のテクニックが必要。例えばstderrをファイルにリダイレクトするとか。- 回答の候補に設定 牟田口大介 2018年3月28日 12:35
- 編集済み ウィンドウズスクリプトプログラマ 2018年3月28日 13:31
- 回答としてマーク bearbook41 2018年4月1日 5:35
-
フォーラム オペレーターの栗下 望です。
bearbook41 さん、こんにちは。
ご質問いただいた内容から 「Visual Basic for Application(VBA)」 フォーラムに
スレッドの移動をさせていただきました。
※ご自身のスレッドは画面左上の「クイック アクセス」の「マイ スレッド」よりご確認いただけます。既に皆様から返信が寄せられておりますので、
確認いただき参考になった回答には [回答としてマーク] をご設定くださいね。どうぞよろしくお願いいたします。
~ 参考になった投稿には回答としてマークの設定にご協力ください ~
MSDN/TechNet Community Support 栗下 望- 編集済み 栗下 望Microsoft employee, Moderator 2018年3月29日 4:06 移動後文言へ編集
- 回答としてマーク bearbook41 2018年4月1日 5:28