2017-04-01 18 views
1

私は少しバインドされています... 私はいくつかのコマンドラインユーティリティをより現代的な方法で実行するためのGUIとしてHTAを使用しようとしています...私が望むように行った。コマンドラインGUIとしてのHTA

これまでのところ、私がやりたいことを得るためにいくつかのトリックを試しましたが、それぞれが私が目指しているものに少し不足しています。

私の目標は、コマンドラインツールとして実行され、リアルタイムで出力をキャプチャし、各ラインをパーズし、HTAウィンドウに何かを表示してツールが何をしているのかを示すexeを実行させることです進捗バーや何らかの種類の鮮明な書式設定されたログ出力などのシーンは、cmdウィンドウのコンソールテキストではなく、

問題は、HTAに別のプログラムを実行させる唯一の方法は、WScript.Shellの.Runまたは.Exec関数です。それぞれには自分の頭痛が付いていて、私のGUIが仕事をしないようにしています。 .Execはコマンドラインからstdoutをキャプチャし、各行を出力する際に​​解析することを意図しているので、最初は欲しかったようでしたが、使用しようとすると大きな空白のCMDウィンドウが表示され、ブロックされていましたexeが実行を終了するまで画面のほとんど。はい、HTAは出力をキャプチャし、私が望んでいたように応答しましたが、空白のCMDウィンドウはGUIを作成する目的を捨ててしまいます。 私は.Runを試みましたが、それは私が非表示のウィンドウでexeファイルを実行することができますが、私はリアルタイムでstdoutをキャプチャすることはできませんし、最高で私はtxtファイルにダンプするだけで事後にそれを読み込もうとします。

言うまでもなく、どちらも私は私が作成するために見ていることだろう何の現実世界(っぽい)の例として...

を探していますかなり何である、とのpingを実行するHTAを実行していると想像10のpingのように。どれくらいのpingが完了したかを示すプログレスバーを表示し、成功した回数と成功した回数、平均ヒットあたりのms数、そうです。次に、pingツールの実行が終了すると、HTAは進行状況バーを削除し、pingテストの最終的な合計を表示します。 .Execでは、これは技術的には機能しますが、コマンドウィンドウが画面のどこかに座っていると、迷惑で、通常はHTAがブロックされます。 .Runを使用すると、HTAは10個のpingが実行されている間にハングしたように見え、返されたエラーコードは0にしかならず、HTAで最終的に結果を表示する前に何が起こったのかを確認するテキストファイルをロードする必要があります。 ..絶えず理想的な結果ではありません。

私は何か重要なことを逃しているように感じます...リアルタイムでラインごとに出力をキャプチャすることができますが、隠れたウィンドウで実行可能なコマンドラインを実行するにはどうしたらいいですか? (私はJScriptのsetTimeoutなどで半リアルタイムに気にしません)コマンドプロンプトウィンドウがまったく表示されないようにしたいのですが、待たずにGUIの出力をGUIで表示できるようにしたいファイルを書き込んだり、読み込んだり、削除したりする時間を無駄にします。

+0

[このWSH VBS GUI](https://stackoverflow.com/a/47111556/2165759)ソリューションと[このコンソールウィンドウ非表示](https://stackoverflow.com/a/32302212/2165759)メソッドを確認してください。 – omegastripes

答えて

1

cscriptから隠しコンソール内で実行しているExec操作を開始すると、開始されたプロセスも非表示になります。しかし、.htaからは発生しません。Execメソッドは、実行されたプロセスにアタッチされたコンソールを作成し、サードパーティのコードなしで隠すことはできません。

コマンド出力の解析に対処する通常の方法は、出力をファイルにリダイレクトしてからファイルを読み取ることです。もちろん、開始されたプロセスのStdInストリームに書き込むオプションが失われます。

は、ファイルへのプロセスの出力をリダイレクトするには、最初のアイデアは

>"outputFile" command 

のようなものを使用することですが、問題は、これは直接動作しません実行しようとしていることです。このリダイレクションは、OSの一部が、cmd.exeの演算子ではありませんので、我々は、我々はまた、プロセスが終了しているかどうかを知る方法が必要になります

cmd /c" >"outputFile" command " 

ようなものを実行する必要があり、それ以上のデータが使用できなくなります。 RunメソッドのWait引数を使用できますが、.htaインターフェイスはフリーズされ、Runメソッドによってブロックされます。

Wait引数を使用できなかった場合、プロセスIDを取得し、プロセスが終了するまですべてのデータを読み込んで定期的に出力を待つ必要があります。

あなたがこれは単純に2つのpingのプロセスを開始します、とwindow.setIntervalを使用すると、各500ミリ秒に二つのプロセスの出力を取得し、追加します.hta

<html> 
<head> 
<title>pingMonitor</title> 
<HTA:APPLICATION 
    ID="pingMonitor" 
    APPLICATIONNAME="pingMonitorHTA" 
    MINIMIZEBUTTON="no" 
    MAXIMIZEBUTTON="no" 
    SINGLEINSTANCE="no" 
    SysMenu="no" 
    BORDER="thin" 
/> 
<script type="text/vbscript" > 

Class ProcessOutputMonitor 

    Dim shell, fso, wmi 
    Dim processID, retCode, processQuery 
    Dim outputFile, inputFile 

    Private Sub Class_Initialize 
     Set fso  = CreateObject("Scripting.FileSystemObject") 
     Set shell = CreateObject("WScript.Shell") 
     Set wmi  = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") 
     Set inputFile = Nothing 
    End Sub 

    Private Sub Class_Terminate 
     freeResources 
    End Sub 

    Public Default Function Start(ByVal commandLine) 
    Const SW_HIDE = 0 
    Const SW_NORMAL = 1 
    Const TemporaryFolder = 2 
    Dim startUp 

     Start = False 
     If Not IsEmpty(processID) Then Exit Function 

     outputFile = fso.BuildPath(_ 
      fso.GetSpecialFolder(TemporaryFolder) _ 
      , Left(CreateObject("Scriptlet.TypeLib").GUID,38) & ".tmp" _ 
     ) 

     ' "%comspec%" /c">"outputFile" command arguments " 
     commandLine = Join(_ 
      Array(_ 
       quote(shell.ExpandEnvironmentStrings("%comspec%")) _ 
       , "/c"">" & quote(outputFile) _ 
       , commandLine _ 
       , """" _ 
      ) _ 
      , " " _ 
     ) 

     ' https://msdn.microsoft.com/en-us/library/aa394375%28v=vs.85%29.aspx 
     Set startUp = wmi.Get("Win32_ProcessStartup").SpawnInstance_ 
     startUp.ShowWindow = SW_HIDE 

     retCode = wmi.Get("Win32_Process").Create(commandLine , Null, startUp, processID) 
     If retCode <> 0 Then 
      freeResources 
      Exit Function 
     End If 

     processQuery = "SELECT ProcessID From Win32_Process WHERE ProcessID=" & processID 


     Start = True 
    End Function 

    Public Property Get StartReturnCode 
     StartReturnCode = retCode 
    End Property 

    Public Property Get WasStarted 
    End Property 

    Public Property Get PID 
     PID = processID 
    End Property 

    Public Property Get IsRunning() 
     IsRunning = False 
     If Not IsEmpty(processID) Then 
      If getWMIProcess() Is Nothing Then 
       processID = Empty 
       freeResources 
      Else 
       IsRunning = True 
      End If 
     End If 
    End Property 

    Public Property Get NextLine 
     NextLine = getFromInputFile("line") 
    End Property 

    Public Property Get NextData 
     NextData = getFromInputFile("all") 
    End Property 

    Private Function getFromInputFile(what) 
    Const ForReading = 1 
     getFromInputFile = Empty 
     If Not IsEmpty(processID) Then 
      If inputFile Is Nothing Then 
       If fso.FileExists(outputFile) Then 
        Set inputFile = fso.GetFile(outputFile).OpenAsTextStream(ForReading) 
       End If 
      End If 
      If Not (inputFile Is Nothing) Then 
       If Not inputFile.AtEndOfStream Then 
        Select Case what 
         Case "line" : getFromInputFile = inputFile.ReadLine() 
         Case "all" : getFromInputFile = inputFile.ReadAll() 
        End Select 
       End If 
      End If 
     End If 
    End Function 

    Private Function quote(text) 
     quote = """" & text & """" 
    End Function 

    Private Function getWMIProcess() 
    Const wbemFlagForwardOnly = 32 
    Dim process 
     Set getWMIProcess = Nothing 
     If Not IsEmpty(processID) Then 
      For Each process In wmi.ExecQuery(processQuery, "WQL", wbemFlagForwardOnly) 
       Set getWMIProcess = process 
      Next 
     End If 
    End Function 

    Private Sub freeResources() 
    Dim process 
     Set process = getWMIProcess() 
     If Not (process Is Nothing) Then 
      process.Terminate 
     End If 
     processID = Empty 
     processQuery = Empty 
     If Not (inputFile Is Nothing) Then 
      inputFile.Close 
      Set inputFile = Nothing 
      fso.DeleteFile outputFile 
     End If 
    End Sub 

End Class 

</script> 

<SCRIPT LANGUAGE="VBScript"> 

Dim timerID 
Dim monitorGoogle, monitorMicrosoft 

Sub Window_onLoad 
    window.resizeTo 1024,400 
    Set monitorGoogle = New ProcessOutputMonitor 
     monitorGoogle.Start "ping -4 www.google.com" 
    Set monitorMicrosoft = New ProcessOutputMonitor 
     monitorMicrosoft.Start "ping -4 www.microsoft.com" 

    timerID = window.setInterval(GetRef("monitorPings"), 500) 
End Sub 

Sub monitorPings 
Dim buffer, keepRunning 
    keepRunning = False 

    buffer = monitorGoogle.NextData 
    If Not IsEmpty(buffer) Then 
     google.innerHTML = google.innerHTML & Replace(buffer, vbCrLf, "<br>") 
     keepRunning = True 
    Else 
     keepRunning = CBool(keepRunning Or monitorGoogle.IsRunning) 
    End If 

    buffer = monitorMicrosoft.NextData 
    If Not IsEmpty(buffer) Then 
     microsoft.innerHTML = microsoft.innerHTML & Replace(buffer, vbCrLf, "<br>") 
     keepRunning = True 
    Else 
     keepRunning = CBool(keepRunning Or monitorMicrosoft.IsRunning) 
    End If 

    If Not keepRunning Then 
     window.clearInterval(timerID) 
     timerID = Empty 
     alert("Done") 
    End If 

End Sub 

Sub ExitProgram 
    If Not IsEmpty(timerID) Then window.clearInterval(timerID) 
    window.close() 
End Sub 

</SCRIPT> 

</head> 

<body> 
    <input id="checkButton" type="button" value="EXIT" name="run_button" onClick="ExitProgram" align="right"> 
<br><br> 
    <span id="CurrentTime"></span> 
<br><br> 
    <table style="width:100%"> 
     <tr><th style="width:50%;">microsoft</th><th style="width:50%">google</th></tr> 
     <tr> 
      <td id="microsoft" style="font-family=courier;font-size:0.6em;vertical-align:top;"></td> 
      <td id="google" style="font-family=courier;font-size:0.6em;vertical-align:top;"></td> 
     </tr> 
    </table> 

</body> 
</html> 

このテストでは、この手法を実装して、基本的なクラス(ProcessOutputMonitor)を持っています両方のプロセスが終了するまで出力に出力します(悪い方法、ちょうどテストコード)。

+0

これは役に立ちますが、自分のバージョンが動作することを確認するために少し明確にする必要があります。まず、出力をファイルに送信しようとしたとき、出力を一度にファイルに送信したように見えましたが、通常は関数が数ミリ秒で完了するためです。しかし、私が正しく読んでいるなら、あなたの例では、pingのようなもっと長いプロセスがファイルの各行を出力することを示唆しています。ファイルを繰り返し読んで、直後に新しい行を見ることができますファイルに追加され、スクリプトがプロセスの実行状態を確認する機会を与えます。 @Cache、右。 – Ceetch

+0

私は、 'ping'の場合(デフォルトでは4つのパケットが2つある)、' .ReadAll'メソッドを使用して 'monitorPings'サブルーチンがインクリメンタルにデータを取得することを明確にしました。プロセスが速い場合は、間隔が開始される前にすべての出力が書き込まれ、最初の読み取り操作ですべてのデータが取得されます。 –

+0

これは知っていると便利です。 adbプロセスは、デバイスと接続するバックグラウンドサービスによって異なります。このような場合、 'adb devices'を実行すると、まずサービスが実行されているかどうかがチェックされ、そうでない場合は起動されます。起動されていない場合は、サービスが開始されていることを示すために追加行が印刷され、接続されているデバイスの一覧表示を続ける前にサービスがロードされている間ハングアップするように見えます。プロセスが完了する前に出力を読むことができれば、サーバーの開始をキャッチしてhtaの進行状況を表示することができます。 – Ceetch

関連する問題