我大部分时间都在寻找解决方案,我开始认为它可能不符合我的要求
我的基本设置是运行从excel vba代码调用的vbscript(.vbs)。 vba代码必须继续并保持vbscript运行,但会使用Exec.Status
在vbscript中,我正在使用WScript.StdOut.WriteLine "whatever"
来跟踪/调试它的进度,但是就目前而言,我只能在excel vba代码完成后才能读取它的输出。
我想要的是从vbscript
看到实时输出到控制台这是vba代码......
Dim WSH As IWshRuntimeLibrary.WshShell 'Windows Script Host Object Model
Dim Exec As WshExec
Set WSH = CreateObject("WScript.Shell")
Set Exec = WSH.Exec("%COMSPEC% /C CSCRIPT.EXE //nologo " _
& VbsFileDir _
& " " & Arg1 _
& " " & Arg2 _
& " " & Arg3 _
& " " & Arg4)
我已经能够通过从WSH.Exec
转换为WSH.Run
获得实时输出,但我确实需要Exec.Status
的访问权限,WSH.Run
下无法访问WSH.Run("cscript C:\28353522-B.vbs")
}
更新 - 2015-02-06
进一步澄清......使用@ Ekkehard.Horner答案提供的示例'... B.vbs'代码......以下excel-vba代码将向控制台显示实时输出...
WSH.Exec("cscript C:\28353522-B.vbs")
...但以下内容不会向控制台显示任何内容
.Run()
我无法使用.Status
,因为我使用.Exec()
中的{{1}}标记
此外,我不能将vbscript移动到VBA代码中,因为VBA继续与vbscript并行执行其他任务。
P.S。如果有人可以提交答案解释为什么不能这样做,那么我会将其标记为已接受。
答案 0 :(得分:3)
使用.Stdout.ReadLine()直到进程完成并且.Stdout.ReadAll()来覆盖输出的其余部分 - 如
28353522-A.vbs
Option Explicit
Const WshFinished = 1
Dim oExc : Set oExc = CreateObject("WScript.Shell").Exec("cscript 28353522-B.vbs")
WScript.Echo "A", "start"
Do While True
If oExc.Status = WshFinished Then
WScript.Echo "A", "WshFinished"
Exit Do
End If
WScript.Sleep 500
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadLine()
Loop
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadAll()
28353522-B.vbs
Option Explicit
Dim i
For i = 1 To 10
WScript.Echo "B", i, "whatever"
WScript.Sleep 100
Next
输出:
cscript 28353522-A.vbs
A start
A B 1 whatever
A B 2 whatever
A B 3 whatever
A WshFinished
A B 4 whatever
B 5 whatever
B 6 whatever
B 7 whatever
B 8 whatever
B 9 whatever
B 10 whatever
BTW - 你是如何通过.Run()获得实时输出的?
答案 1 :(得分:0)
为什么要运行两个文件?没有必要。
VBA是完全基本的,可以写入自己的控制台。
所以把你的vbs放到VBA中(你可以将VBS剪切并粘贴到VBA中,如果你把sub / end sub放在它周围它会起作用)。让VBS在VBA中运行时会放一个触发它的计时器。
这是VBA创建/读/写控制台的类模块。
'User global var gconsole
Public Function WriteToConsoles(sOut As String)
If IsConsoleAvailable() = True Then
Dim Result As Long, cWritten As Long
Result = WriteConsole(hConsole, ByVal sOut, Len(sOut), cWritten, ByVal 0&)
End If
End Function
Public Sub ExecuteCommand(Cmd As String, ReturnToPrompt As Boolean)
If IsConsoleAvailable() = True Then
If Len(Cmd) <> 0 Then
If ReturnToPrompt = True Then
Shell Environ$("comspec") & " /k " & Cmd
Else
Shell Environ$("comspec") & " /c " & Cmd
End If
End If
End If
End Sub
Public Sub CreateConsole()
If IsConsoleAvailable() = False Then
If AllocConsole() Then
hConsole = GetStdHandle(STD_OUTPUT_HANDLE)
If hConsole = 0 Then MsgBox "Couldn't allocate STDOUT"
Else
MsgBox "Couldn't allocate console"
End If
End If
End Sub
Public Sub CloseConsole()
If IsConsoleAvailable() = True Then
CloseHandle hConsole
hConsole = 0
FreeConsole
End If
End Sub
Public Function IsConsoleAvailable() As Boolean
If hConsole <> 0 Then
IsConsoleAvailable = True
Else
IsConsoleAvailable = False
End If
End Function
答案 2 :(得分:0)
我已经找到了自己问题的答案。虽然这不是一个首选的解决方案(我将在下面解释),所以我不会标记为正确,但也许有人可以通过解决方案解决问题? (如果是的话,发一个答案,我会标记为正确)
首先,@ Ekkehard.Horner给出了启发此解决方案的答案。
创建文件&#39; B.vbs&#39;代表我要运行的主要vbscript。
Option Explicit
Dim i
For i = 1 to 10
Wscript.Echo "B", i, "whatever"
Wscript.Sleep 100
Next
创建文件&#39; A.vbs&#39;充当主vbscript和我的Excel VBA代码之间的中间人
Option Explicit
Dim WSH
Set WSH = CreateObject("WScript.Shell")
WSH.Run "cscript C:\B.vbs", , True
Set WSH = Nothing
现在是excel VBA代码......
Option Explicit
Sub Test()
Dim WSH As IWshRuntimeLibrary.WshShell
Dim Exec As WshExec
Set WSH = CreateObject("WScript.Shell")
Set Exec = WSH.Exec("cscript C:\A.vbs")
'Showing that I can still access the Exec.Status
While Exec.Status = WshRunning
Debug.Print "Running"
Wend
'But downside is nothing is avaiable from Stdout
Debug.Print Exec.StdOut.ReadAll
Set Exec = Nothing
Set WSH = Nothing
End Sub
所以Excel VBA称之为'A.vbs&#39;仍在使用WSH.Exec()
,然后会调用&#39; B.vbs&#39;使用WSH.Run()
,打开第二个控制台窗口,显示实时输出
<强>优点强>
Exec.Status
缺点(原因我没有标记为正确)
Exec.Terminate()
只会终止&#39; A.vbs&#39; (第一个控制台窗口),&#39; B.vbs&#39;将继续运行Exec.StdOut.
无法读取&#39; B.vbs&#39;