How to find the window Title of Active(foreground)

2019-01-07 23:11发布

问题:

I want to find the title of the window which is currently active(having focus) using Window Script Host(WSH) because I want my WSH script to Sendkeys only If the desired window is active.

Note* I am not in a condition to use the alternative i.e activating the desired window before invoking sendkeys.

Any help is appreciated.

回答1:

Short answer: You can't. At least not without writing a COM wrapper for the relevant Windows API calls.

Can't you just use AppActivate and check the result?

Set oShell = CreateObject("WScript.Shell")
If oShell.AppActivate "Untitled - Notepad" Then
  oShell.SendKeys "Hello, world!"
End If


Long answer: To get the active window title, you need to call the Windows API GetWindowText function and pass the GetForegroundWindow() handle. VBScript and Windows Script Host don't support Windows API calls, so you'll need to write a COM wrapper around these functions, that you can then use in your script. Here're examples:

Get current active Window title in C

How do I get the title of the current active window using c#?



回答2:

You can make a COM object with GetForegroundWindow and GetWindowText.

Put following lines into wso.cls and store is a folder called wso on your desktop.

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32

Namespace WindowScriptingObject

    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
   Public Interface _WindowScriptingObject
        <DispId(1)> Function ActiveWindow() As Integer
        <DispId(2)> Function WindowText(ByVal hWnd As Integer) As String
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
     ClassInterface(ClassInterfaceType.None), _
     ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
        Implements _WindowScriptingObject

        Public WindowScriptingObject()

        Public Declare Auto Function GetForegroundWindow Lib "user32" Alias "GetForegroundWindow"() As Integer
        Public Declare Auto Function GetWindowText Lib "user32.dll" (ByVal hwnd As Int32, <Out()> ByVal lpString As System.Text.StringBuilder, ByVal cch As Int32) As Int32
        Public Function ActiveWindow() As Integer Implements _WindowScriptingObject.ActiveWindow
    ActiveWindow=GetForegroundWindow()

        End Function

        Public Function WindowText(hwnd as Integer) As String Implements _WindowScriptingObject.WindowText
    on error resume next
    Dim b As New System.Text.StringBuilder(ChrW(0), 512)
                Dim ret = GetWindowText(hWnd, b, b.Capacity)
    WindowText = b.tostring
        End Function


    End Class

End Namespace

Then create a bat file in same folder called wso.bat.

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe" /target:library /out:"%userprofile%\desktop\wso\wso.dll" "%userprofile%\desktop\wso\wso.cls" /verbose

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm" /codebase "%userprofile%\desktop\wso\wso.dll" /tlb:"%userprofile%\desktop\wso\wso.tlb" /v

If /i "%cmdcmdline:~0,6%"=="cmd /c" pause

To use in vbs after running bat file.

Set wso=CreateObject("WindowScriptingObject")
x = wso.ActiveWindow
msgbox x, , "vbs"
msgbox wso.windowtext(x), , "vbs"

The GUIDs used here are specific to this project. Do not use them for other purposes.

More info on what we are doing

http://social.msdn.microsoft.com/Forums/en-US/adcae113-4758-481a-a367-60d5d14d97d6/this-is-how-to-turn-vbs-and-js-files-into-exe-files-from-the-command-line-without-third-party-tools?forum=scripting

If you must do a per user install, use regasm to make a regfile instead of registering it. Then change all references to HKCR to HKCU\Software\Classes. Then merge with regedit /s regfile.reg.

To move the file you need to run Regasm on it in it's new location. See command in bat file.

Will be posed at MS site of course for accurate historical purposes.



回答3:

This is an updated version for use. Previous answer is minimun needed for it to work.

This also replaces the answer here (appactivate between multiple internet explorer instances), as it didn't work for Windows 7 and later due to sendmail being a reserved name on those OSs.

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32

Namespace WindowScriptingObject

    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
   Public Interface _WindowScriptingObject
        <DispId(1)> Function ActiveWindow() As UInteger
        <DispId(2)> Function WindowText(ByVal hWnd As UInteger) As String
        <DispId(3)> Function WindowPID(ByVal hWnd As UInteger) As UInteger
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
     ClassInterface(ClassInterfaceType.None), _
     ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
        Implements _WindowScriptingObject

        Public WindowScriptingObject()

        Public Declare Auto Function GetForegroundWindow Lib "user32" Alias "GetForegroundWindow"() As UInteger
        Public Declare Auto Function GetWindowText Lib "user32.dll" (ByVal hwnd As Int32, <Out()> ByVal lpString As System.Text.StringBuilder, ByVal cch As Int32) As Int32
        Public Declare Auto Function GetWindowThreadProcessId Lib "user32" Alias "GetWindowThreadProcessId" (ByVal hwnd As UInteger, ByRef lpdwProcessId As UInteger) As UInteger

        Public Function ActiveWindow() As UInteger Implements _WindowScriptingObject.ActiveWindow
    ActiveWindow = GetForegroundWindow()
    If err.lastdllerror <> 0 then
        Dim tmp as uinteger = err.lastdllerror and &h80070000
        err.raise(tmp,  "WindowSystemObject.GetForegroundWindow", "Type net helpmsg " & err.lastdllerror & " in a command prompt for help")
        Exit Function
    End If
        End Function

        Public Function WindowText(hwnd as UInteger) As String Implements _WindowScriptingObject.WindowText
    Dim b As New System.Text.StringBuilder(ChrW(0), 512)
                Dim ret as uinteger = GetWindowText(hWnd, b, b.Capacity)
    If err.lastdllerror <> 0 then
        Dim tmp as uinteger = err.lastdllerror and &h80070000
        WindowText = ""
        err.raise(tmp,  "WindowSystemObject.GetWindowText", "Type net helpmsg " & err.lastdllerror & " in a command prompt for help")
        Exit Function
    End If
    WindowText = b.tostring
        End Function

        Public Function WindowPID(HWnd as UInteger) As UInteger Implements _WindowScriptingObject.WindowPID 
    Dim X as UInteger
    Dim M as UInteger = 1
    X=GetWindowThreadProcessID(HWnd,M)
    If err.lastdllerror <> 0 then
        Dim tmp as uinteger = err.lastdllerror and &h80070000
        WindowPID = 0
        err.raise(tmp,  "WindowSystemObject.GetWindowThreadProcessID", "Type net helpmsg " & err.lastdllerror & " in a command prompt for help")
        Exit Function
    End If  
    WindowPID = M
        End Function


    End Class

End Namespace


回答4:

The batch file has to run without errors.

The first command makes the dll from the cls file. It will say Compilation Sucessfull. It expects the files to be in a folder called wso on your desktop.

The second command registers it per machine. You must be an admin to do this. If you are not an admin then you must generate a reg file, and change all HKEY_CURRENT_ROOT to HKEY_CURRENT_USER\Software\Classes.

To generate a regfile

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm" /regfile:"%userprofile%\desktop\wso\wso.reg" "%userprofile%\desktop\wso\wso.dll"  /v

After editing wso.reg merge it with

regedit /m "%userprofile%\desktop\wso\wso.reg"

And you need to read the results of the commands.

Here is the script running showing hwnd, PID, and window title (and error code). Note how when script starts there is no active window for about two seconds (windows is waiting for your program to create one for it to make active. It only waits 2 seconds). Usually at program starts, but also other times, for short periods there will be no active window. You must trap this. Here's a script that does.

On error resume next
Set wso=CreateObject("WindowScriptingObject")
Do
x = wso.ActiveWindow
    wscript.echo x

    wscript.echo wso.windowtext(x)

    wscript.echo (err.number)
    err.clear
    wscript.echo wso.windowpid(x)

    wscript.echo (err.number)
    err.clear
    wscript.sleep 1000
Loop

And this is what it looks like when run with CScript in a command prompt.

C:\Users\User>cscript "C:\Users\User\Desktop\ActiveWindow.vbs"
Microsoft (R) Windows Script Host Version 5.7
Copyright (C) Microsoft Corporation. All rights reserved.


-2147024809
-2147024809
3344366
Administrator: Command Prompt - cscript  "C:\Users\User\Desktop\ActiveWin
dow.vbs"
0
972
0
3344366
Administrator: Command Prompt - cscript  "C:\Users\User\Desktop\ActiveWin
dow.vbs"
0
972
0
3344366
1312854
vbscript - How to find the window Title of Active(foreground) window using Windo
w Script Host - - Windows Internet Explorer
0
4724
0
1312854
vbscript - How to find the window Title of Active(foreground) window using Windo
w Script Host - - Windows Internet Explorer
0
4724
0
3344366
Administrator: Command Prompt - cscript  "C:\Users\User\Desktop\ActiveWin
dow.vbs"
0
972
0
^C
C:\Users\User>

----EDIT----

It's looks like you've been hit by a notepad bug when pasting from web pages from the funny spacing of the object name in the error message.

If using notepad to write it copy and paste into wordpad to check line breaks. Notepad totally ignores and hides carriage returns but other programs don't. Notepad only looks for line feeds. If coping from browser based documentation such as web pages and help systems sometimes stray carriage returns get invisibly inserted in notepad.



标签: vbscript wsh