Vbscript 如何确保调用SendKeys时应用程序保持焦点?(有没有比睡眠更好的方法?)

Vbscript 如何确保调用SendKeys时应用程序保持焦点?(有没有比睡眠更好的方法?),vbscript,Vbscript,我正在更新一些脚本,这些脚本需要在将焦点返回到另一个应用程序之前将焦点切换到一个应用程序,发送一些击键 Option Explicit dim objShell set objShell = WScript.CreateObject("WScript.Shell") objShell.AppActivate "AnApplication" WScript.Sleep 1000 objShell.SendKeys("%{I}") ... objShell.SendKeys("{END}

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

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 
然而,按键似乎只有在焦点返回到另一个应用程序后才会发送


有没有一种方法可以确保在发送击键时应用程序具有焦点,而不使用睡眠

正如我在评论中提到的,没有必要将
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

这对我帮助很大,但我发现我需要添加少量的时间来让操作系统真正聚焦该窗口,因此我在
AppActivate
SendKeys
行之间添加了
WScript.Sleep 100

这已经足够好了,直到我意识到在某些情况下,我的一些进程在调用脚本之前已经退出,导致我的击键被发送到错误的窗口。请看,问题在于
Shell.AppActivate
将通过其PID发送激活程序的命令,但是没有什么要检查的,以查看该程序是否已退出。因此,我添加了另一个子系统,该子系统查询winmgmts的活动PID(以及命令行,因为每个进程都是唯一调用的)

以下是我的贡献,并表示最深切的感谢:

'================
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
'================
'================
Sub-DelayedSendKeysWithFocusAndProcessCheck(str、procid、cmdline)
调用ProcCheck(procid,cmdline)
Shell.AppActivate程序ID
WScript.Sleep 100
Shell.SendKeys str
端接头
'================
子ProcCheck(procid,cmdline)
CheckedPID=“”
CheckedCmdLine=“”
设置ProcessCollection=GetObject(“winmgmts:\\”&ComputerName&“\roo”&_
“t\cimv2”)
设置ProcessResults=ProcessCollection.ExecQuery(“从Win32\U Pr中选择*&_
“流程,其中ProcessID=”&procid&“”)
对于ProcessResults中的每个obj
CheckedPID=obj.ProcessID
CheckedCmdLine=obj.CommandLine
下一个
如果选中PID procid,则
Shell.Popup“PID”&procid&“(&cmdline&”)似乎已退出”&_
“d!”,0,“调用的进程丢失!”,vbOkOnly
调用EndScript
其他的
调用RegExSearch(cmdline,CheckedCmdLine)
如果返回值(0)”,则
弹出“PID”&procid&“不再显示相同的命令”&_
“我开始使用的行!”&vbcrlf&“当我开始时,它使用了”&vbcrlf&_
cmdline&vbcrlf&“现在显示”&CheckedCmdLine&“我要退出否”&_
“w.您可以尝试再次运行我,或只是手动打开窗口。”&vbcrlf&_
“(请注意,这是一个极其罕见的错误,您应该&_
“y购买彩票。)”,0,“调用的进程命令行已更改!”,vbOkOnly
调用EndScript
如果结束
如果结束
端接头
'================
AppActivate()
是一个同步函数。当它返回时,您就可以开始发送密钥了。您应该检查它是否返回
True
,但无需将其放入循环中,除非窗口不在屏幕上并且您正在等待它出现。