wscript.sleep在错误的时间发生

时间:2012-02-24 20:32:01

标签: vbscript wmi sleep sendkeys wsh

我不知道VBScript,但最近我需要编写一个具有SendKeys功能的脚本,而我无法在批处理文件中执行此操作。当我使用命令cscript test.vbs从Windows 7计算机的命令提示符运行以下代码时,睡眠发生在telnet启动之前。问题可能是命令已执行但结果输出直到睡眠完成后才会显示。我该如何解决这个问题?

Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.SendKeys "telnet localhost 25" & vbCr
wscript.sleep 5000
编辑:如果我在启动之前放入一个sleep命令,然后单击它必须键入的命令提示符窗口,我可以让我的脚本工作。这样,当脚本暂停时,它会暂停执行,但不会暂停显示命令提示符中的输出。我仍然需要一个命令行解决方案。

1 个答案:

答案 0 :(得分:7)

好的,你的脚本有两个基本缺陷导致你可能遇到第三个问题。他们很容易错过,但我认为一旦你看到它们就会对你有意义。

首先,SendKeys方法是异步执行的,这意味着在发送击键时脚本执行不会暂停。等待命令执行的时间也必须考虑到首先发送击键所需的时间。你监控你发送击键的窗口真的好多了。我将告诉你两种方法。

其次,你不应该单独使用SendKeys方法,因为它没有提供任何方法来控制实际接收发送的击键的窗口。您应该始终将它与Shell对象的AppActivate方法一起使用,该方法允许您的脚本将任何打开的窗口置于焦点上(从而使其可用于接收您要发送的击键)。

最后,我不确定您是在尝试在新的命令提示符下打开telnet还是抓取当前实例。你的脚本意味着后者,但这可能不是一个好方法,因为它更容易出错。打开它自己的命令提示符实例可能会更好。您可以使用WshShell对象的Run方法轻松完成此操作。所以在基本级别,你的脚本应该是这样的。

' Start Telnet
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "telnet localhost 25"
' Wait for Telnet to start
WScript.Sleep 5000
' Bring the window into focus
Set objShell = CreateObject("Shell.Application")
objShell.AppActivate "telnet localhost"
' Send keystrokes to telnet window
WshShell.SendKeys "some command here"

虽然这非常有效,但它根本不是防弹的。例如,AppActivate方法具有必需参数,该参数是要激活的Window的标题。这并不总是很容易理解,它可能因机器而异,使得这些代码不那么便携。如果同时打开多个相同类型的窗口,它也容易出错。但还有另一种方式。

让我们改用WMI。 Win32_Process类有一个Create方法,允许我们创建或启动我们自己的进程。与原生WSH相比,这种方法的独特之处在于我们可以为新的启动过程配置一些属性。这包括(对于控制台应用程序)窗口标题。这意味着我们可以设置,从而最终知道我们想要发送击键的应用程序的窗口标题。这就是它的样子。

strCommandLine = "telnet localhost 25"
strUniqueTitle = "Telnet localhost (" & WScript.ScriptName & ")"

Const SW_NORMAL = 1

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set objProcessStartup = objWMIService.Get("Win32_ProcessStartup")
Set objStartupInformation = objProcessStartup.SpawnInstance_
objStartupInformation.ShowWindow = SW_NORMAL
objStartupInformation.Title = strUniqueTitle

Set objProcess = GetObject("winmgmts:root\cimv2:Win32_Process")
intReturn = objProcess.Create("cmd /k" & strCommandLine, null, objStartupInformation, intProcessID)

If intReturn = 0 Then
    ' Wait for Telnet to connect
    WScript.Sleep 5000
    ' Bring the window into focus
    Set WshShell = CreateObject("WScript.Shell")
    WshShell.AppActivate strUniqueTitle
    ' Send keystrokes to telnet window
    WshShell.SendKeys "exit{ENTER}"
End If

如果你采取步骤,脚本很容易理解。第一步是提供我们希望运行的控制台应用程序的命令行。在这种情况下,telnet。然后,我们使用脚本的名称为我们即将打开的窗口创建一个独特的窗口标题。这可以是您确定不会与任何其他打开窗口匹配的任何内容。

strCommandLine = "telnet localhost 25"
strUniqueTitle = "Telnet localhost (" & WScript.ScriptName & ")"

脚本的开始是设置WMI常量并连接到WMI服务。我们还创建了一个Win32_ProcessStartup类的实例,该实例用作配置对象,其中包含我们将要创建的进程的设置。这是我们确保启动新进程窗口是一个可见状态以及我们设置新窗口标题的地方。

Const SW_NORMAL = 1

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set objProcessStartup = objWMIService.Get("Win32_ProcessStartup")
Set objStartupInformation = objProcessStartup.SpawnInstance_
objStartupInformation.ShowWindow = SW_NORMAL
objStartupInformation.Title = strUniqueTitle

有了所有先决条件,就可以实际启动我们的控制台应用程序了。为此,我需要使用Win32_Process类的Create方法。传统上,WMI脚本将查询WMI服务以返回特定类的实例。由于我们正在创建一个,因此我没有查询的现有实例。这意味着我需要使用与您可能习惯的语法略有不同的语法。使用VBScript的GetObject方法,我直接连接到Win32_Process类命名空间。通过引用该命名空间,我可以调用Create方法并返回一个表示新创建的流程实例的对象。

Set objProcess = GetObject("winmgmts:root\cimv2:Win32_Process")
intReturn = objProcess.Create("cmd /k" & strCommandLine, null, objStartupInformation, intProcessID)

我使用的Create方法返回一个整数,指示是否成功创建了进程。所以为了确保没有任何问题,我将剩余的代码包装在If块中。

If intReturn = 0 Then
    ' Wait for Telnet to connect
    WScript.Sleep 5000
    ' Bring the window into focus
    Set WshShell = CreateObject("WScript.Shell")
    WshShell.AppActivate strUniqueTitle
    ' Send keystrokes to telnet window
    WshShell.SendKeys "exit{ENTER}"
End If

这种特殊方法的工作方式的一个好处是Win32_Process类的Create方法不会异步执行。在创建新进程之前它不会返回,所以我永远不必担心在完成此操作后让我的脚本进入休眠状态。此方法还为正在运行的进程提供常量WMI引用。这意味着我们可以主动监视它的状态,以确保我们的脚本正常运行,并确保在脚本退出之前正确关闭进程。我希望这有用。