在调用SendKeys时,如何确保我的应用程序保持关注? (有比使用睡眠更好的方法吗?)

时间:2014-04-30 12:23:18

标签: vbscript

我正在更新一些需要将焦点切换到应用程序的脚本,发送一些键击,然后将焦点返回到另一个应用程序。

Option Explicit

dim objShell 
set objShell = WScript.CreateObject("WScript.Shell") 

objShell.AppActivate "AnApplication"
WScript.Sleep 1000 

objShell.SendKeys("%{I}")
...
objShell.SendKeys("{END}") 

WScript.Sleep 1000
objShell.AppActivate "AnotherApplication"

Set objShell = Nothing

我研究了对这些脚本的一些改进,我希望做的一件事是删除Sleep语句以加速脚本的执行。在研究这个时,我发现建议您在继续之前检查AppActivate的返回值,以便有效地使脚本等到应用程序具有焦点并且可以发送击键。

我尝试更新我的脚本来执行此操作 -

Option Explicit

dim objShell 
set objShell = WScript.CreateObject("WScript.Shell")

While Not objShell.AppActivate "AnApplication" 
    Sleep 300
Wend 

objShell.SendKeys("%{I}")
...
objShell.SendKeys("{END}")

While Not objShell.AppActivate "AnotherApplication"
    Sleep 300
Wend 

然而,键盘似乎只在焦点返回到AnotherApplication后才发送。

有没有办法做到这一点,以确保AnApplication在发送击键时有焦点,而不使用Sleep?

2 个答案:

答案 0 :(得分:3)

正如我在评论中提到的,没有必要将AppActivate()置于循环中。这是一个同步调用,该函数不应该返回,直到它激活窗口(或尝试失败)。

如果您担心窗口失去焦点,可以在发送几次击键后再次拨打AppActivate(),或者在每次键击之前调用它。

例如:

If Not MySendKeys("AnApplication", "%{I}") Then MsgBox "Could not send %{I}"
If Not MySendKeys("AnApplication", "{End}") Then MsgBox "Could not send {End}"

Function MySendKeys(strApp, strKeys)
    If objShell.AppActivate(strApp) Then
        objShell.SendKeys strKeys
        MySendKeys = True
    End If
End Function

答案 1 :(得分:0)

这对我帮助很大,但我发现我需要添加少量时间让操作系统实际关注该窗口,因此我在WScript.Sleep 100之间添加了AppActivateSendKeys行。

哪个工作得很好,直到我意识到在某些情况下我的某些进程在脚本被调用之前已经退出,导致我的键击被发送到错误的窗口。请注意,问题是Shell.AppActivate将发送命令以通过它的PID激活程序,但是没有什么可以检查该程序是否已退出。所以,我添加了另一个子查询活动PID的winmgmts(以及CommandLine,因为每个进程都被唯一地调用)。

这是我的贡献,最深切的谢意:

'================
Sub DelayedSendKeysWithFocusAndProcessCheck(str, procid, cmdline)
  Call ProcCheck(procid, cmdline)
  Shell.AppActivate procid
  WScript.Sleep 100
  Shell.SendKeys str
End Sub
'================
Sub ProcCheck(procid, cmdline)
  CheckedPID = ""
  CheckedCmdLine = ""
  Set ProcessCollection = GetObject("winmgmts:\\" & ComputerName & "\roo" &_
  "t\cimv2")
  Set ProcessResults = ProcessCollection.ExecQuery(" Select * from Win32_Pr" &_
  "ocess where ProcessID = '" & procid & "'")
  For Each obj in ProcessResults
    CheckedPID = obj.ProcessID
    CheckedCmdLine = obj.CommandLine
  Next
  If CheckedPID <> procid Then
    Shell.Popup "PID " & procid & " (" & cmdline & ") appears to have exite" &_
  "d!", 0, "Called Process is Missing!", vbOkOnly
    Call EndScript
  Else
    Call RegExSearch(cmdline, CheckedCmdLine)
    If ReturnValue(0) <> "" Then
      Shell.Popup "The PID " & procid & " no longer shows the same command " &_
  "line I started it with!" & vbcrlf & "When I started it, it used " & vbcrlf &_
  cmdline & vbcrlf & "Now it shows " & CheckedCmdLine & "I'm going to exit no" &_
  "w. You can try to run me again or simply open the windows manually." & vbcrlf &_
  "(Please note that this is an incredibly rare error and you should probabl" &_
  "y buy a lottery ticket.)", 0, "Called process command line has changed!", vbOkOnly
      Call EndScript
    End If
  End If
End Sub
'================