我有点束缚...... 我正在尝试使用HTA作为GUI以更现代的方式运行一些命令行实用程序......除了它不像我希望的那样。
到目前为止,我已经尝试了一些技巧来获得我想要的东西,但每一个都落后于我的目标。
我的目标是让我的脚本运行一个作为命令行工具执行的exe并实时捕获输出,在发生时解析每一行,并在HTA窗口中显示一些内容,以指示该工具在场景,例如进度条或某种花哨的格式化日志输出,而不是cmd窗口的控制台文本。
麻烦的是,我看到让HTA运行另一个程序的唯一方法是WScript.Shell的.Run或.Exec函数。每个都有自己的头痛,使我的GUI不能完成它的工作。 .Exec似乎是我最初想要的,因为我的意图是从命令行捕获stdout并在打印时解析每一行,但是当我尝试使用它时,我注意到一个大的空白CMD窗口出现并阻塞大多数屏幕直到exe完成运行。是的,HTA抓住了输出并按照我的希望做出了回应,但那个空白的CMD窗口有点挫败了制作GUI的目的。 我试过.Run,但是虽然这让我在一个隐藏的窗口中运行exe,我无法实时捕获stdout,最多只能将它转储到txt文件中并尝试在事后读取它。
毋庸置疑,这也不是我想要的......
作为我想要创建的真实世界(ish)示例,想象一下运行一个运行ping的HTA就像10个ping一样。我想显示一个进度条,显示已经完成了多少次ping,其中一些文本显示了每次ping发生时的平均总数,比如有多少ping成功与超时,每次命中的平均ms是多少,以及等等。然后当ping工具完成运行时,HTA将删除进度条并显示ping测试的最终总计。 使用.Exec,这在技术上有效,但是整个时间我都有一个命令窗口坐在屏幕上的某个地方,这很烦人,通常会阻止HTA。 使用.Run,HTA似乎在10次ping运行时挂起,并且只返回0的返回错误代码,然后我必须加载一个文本文件以查看最终在HTA中显示结果之前发生了什么。 。肯定不是理想的结果。
我觉得我错过了一些重要的东西......我怎样才能让HTA在隐藏窗口中运行命令行可执行文件,同时仍然能够实时捕获输出? (我不介意使用JScript setTimeout或类似的半实时)我想避免让命令提示符窗口出现,我希望能够在GUI中显示输出,而不是必须等待它完成并浪费时间编写文件,阅读文件,然后将其删除。
答案 0 :(得分:1)
如果从隐藏控制台内运行的Exec
启动cscript
操作,则启动的进程也将被隐藏。但是.hta
不会发生这种情况,Exec
方法创建一个附加到执行过程的控制台,没有第三方代码就无法隐藏它。
因此,处理命令输出解析的常用方法是将输出重定向到文件然后读取文件,当然会丢失写入已启动进程的StdIn
流的选项。
要将进程的输出重定向到文件,第一个想法是使用类似
的内容>"outputFile" command
但问题是尝试直接执行此操作无效。此重定向不是操作系统的一部分,而是cmd.exe
的运算符,因此,我们需要运行类似
cmd /c" >"outputFile" command "
我们还需要一种方法来了解流程是否已经结束,并且不再有可用的数据。我们可以使用Wait
方法的Run
参数,但这会使.hta
接口冻结,被Run
方法阻止。
我们不能使用Wait
参数,我们必须启动进程,获取进程ID并等待它结束并读取其所有数据或定期检索输出,直到进程结束。 / p>
您有一个基本类(ProcessOutputMonitor
)在此测试中实现此方法.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>
这将简单地启动两个ping
进程,使用window.setInterval
将检索每个500ms两个进程的输出并将其(不好的方式,只是测试代码)附加到输出,直到两个进程结束了。