shell 起動のプログラムの終了について

解決


YU  2004-12-14 10:20:35  No: 118295  IP: [192.*.*.*]

次ぎのようにしてプログラムの終了を得ています。(人の作ったものを流用してます。)
タスクマネージャのパフォーマンスを見ているとこのルーチンへ入るとCPUが
100%になり抜けるまでずっとそのままです。
これはしかたがないのでしょうか。何か別の方法があるのでしょうか。


'------------------------------------------------------------------
' 外部のプログラムを実行し、そのプログラムの終了を待つプロシージャ
' no: 0  バックグランド     1  フォアグランド
'     5  アイコン
'------------------------------------------------------------------

  Dim hShell As Long
  Dim hProc As Long
  Dim lExit As Long
  Dim bret  As Boolean
  Dim rc As Long

  hShell = Shell(cmdline, no)

  hProc = OpenProcess(PROCESS_QUERY_INFORMATION, False, hShell)

  Do
    GetExitCodeProcess hProc, lExit
    DoEvents
  Loop While lExit = 259        ' Loop While lExit = STILL_ACTIVE
  
  execmd = CloseHandle(hProc)

End Function

編集 削除
ねろ  2004-12-14 10:52:53  No: 118296  IP: [192.*.*.*]

こう言う処理はよくあることで、CPUの使用率が100%になっても
バスが激しく動く訳じゃないので、CPUがあっちっちになることは
まず無いのでは、私は全く気にしませんが。
ただし中にはとても気にする人がいます、その場合は
Loopの中に「Sleep(1)」とでも入れればCPUの使用率はガクンと
下がると思います。

編集 削除
聖 羅樹  URL  2004-12-14 12:12:30  No: 118297  IP: [192.*.*.*]

起動したプログラムが終了するまで待機する方法には2種類あります。

一つはYUさんのコードのようにDo〜Loopで待つ方法。
これは待機している側がDo〜Loop内を延々繰り返しているので、
そのままではCPU使用率が100%になります。
これを回避する方法はねろさんのようにSleepを入れることです。
私はSleep(100)としていますが。精度によっては300だったりします。

もう一つはWaitForSingleObjectで待つ方法です。
これは呼び出された側が終了するまで
待機側(呼び出し元)が停止状態になりますので、CPU使用率は上がりません。
流れ的には次のようになるかと思います。
    hProc = OpenProcess(PROCESS_QUERY_INFORMATION, False, hShell)
    If hProc <> 0 Then
        Call WaitForSingleObject(hProc , INFINITE)
        Call CloseHandle(hProc)
    End If
また、OpenProcessではなく、CreateProcessを使っても出来ます。
この場合は
1.CreateProcess
2.WaitForInputIdle
3.WaitForSingleObject
4.CloseHandle
という流れになります。

編集 削除
魔界の仮面弁士  2004-12-14 12:27:17  No: 118298  IP: [192.*.*.*]

その手法は、 http://nienie.com/~masapico/doc_AppExec.html という
再現しにくい問題を引き起こす可能性があるため、あまりお奨めしません。

CreateProcess と MsgWaitForMultipleObjects を使った手法を使うか、
もしくは、WshShellオブジェクトのRunメソッドを使ってみては如何でしょう。

つまり、
> hShell = Shell(cmdline, no)
>   :
> execmd = CloseHandle(hProc)
ではなく、これらを

 ret = CreateObject("WScript.Shell").Run(CmdLine, no, True)

の1行だけにしてしまう、という事です。

編集 削除
聖 羅樹  URL  2004-12-14 13:30:00  No: 118299  IP: [192.*.*.*]

魔界の仮面弁士さんありがとうございます。
そして、失礼致しました。

OpenProcessとWaitForSingleObjectを使う方法は
ご指摘の通り、場合によっては問題が起こることを忘れていました。
その問題を回避するためにCreateProcessとWaitForSingleObjectに
変えたことを忘れていました。
失礼致しました。

また、リンク先にはCreateProcessとWaitForSingleObjectを使う方法は
特に問題がなさそうに書かれていましたが、WaitForSingleObjectではなく
MsgWaitForMultipleObjectsを勧めていらっしゃるということは、
WaitForSingleObjectではまだ何か問題がある、ということなのでしょうか。

編集 削除
魔界の仮面弁士  2004-12-14 15:41:33  No: 118300  IP: [192.*.*.*]

起動したexeが終了するまで、呼び出し元のアプリを操作する必要が
無い場合は、WaitForSingleObjectで十分かと思います。

編集 削除
YU  2004-12-14 15:56:08  No: 118301  IP: [192.*.*.*]

皆様ありがとうございます。試してみました。
①Sleep(1) の挿入でCPUは0近くに落ちました。
②ret = CreateObject("WScript.Shell").Run(CmdLine, no, True)
  同じくCPUは0近くに落ちました。
  数行のコードが1行ですんでしまうのは驚きです。
  CreateObject を使ったのは始めてでいろいろ調べてみたのですが
  まだすっきりと理解したとは言えません。今のところこのように書けば動くの
  だという状態です。
  この掲示板で CreateObjectを検索したらいっぱい出てきました。
  それだけいろいろ使えるものなんですね。
③CreateProcess とWaitForSingleObject あるいは  
  MsgWaitForMultipleObjects を使った手法はまだできません。難しいです。

プログラムに不具合があって見直しをしていた所,CPU使用が跳ね上がることに
たまたま気がついて今回質問させていただきました。不具合とは関係がありません
でしたが,いつかまた同じように "なぜだ" ということがないよう修正します。
ライブラリのような形でいろいろなプログラムより呼んでいますので今後のものに
ついては問題は起きないようになります。ありがとうございました。

編集 削除