Glib
Glib

Reputation: 262

Find the Window handle for a Chrome Browser

I have found some code to bring the browser to the front, even if it was minimized. I don't however know how to get the hwnd for the browser.

I have had a look at the FindWindow API function, but you need the classname and windowname. I have found that the classname I require is "Chrome_WidgetWin_1" The windowname though would be constantly changing depending on what was open in the browser.

I think one of the 'children' of this window has a classname of "WrapperNativeWindowClass" I'm not sure if I can use this to find the original parent.

Can anyone advise on how to find the window handle of a chrome browser (using VBA)?

Upvotes: 7

Views: 27333

Answers (3)

user257319
user257319

Reputation:

A typical Chrome/Chromium run session (multi-process, handling rendering/out of process iframes and offline-browser-applications and (standard) extensions.

typical run NirSoft's excellent WinExplorer tool (among many many others)

shows the following (probably similar to yours) class displays enter image description here

try looking for both Chrome_WidgetWin_1 and Chrome_RenderWidgetHostHWND

Upvotes: 3

Glib
Glib

Reputation: 262

Thanks for that Steve - I appreciate you taking the time to look at this. It confirmed that I was on the correct lines.

With the browser open and just a single default tab showing, there are 4 instances of "Chrome_WidgetWin_1".

I also discovered that the classname "WrapperNativeWindowClass" was not suitable if the browser was open but not in gmail. I ended up using the classname "Chrome_RenderWidgetHostHWND" (the next sibling) - but there was also more than one instance of this. So I had to identify where the parent had classname(Chrome_WidgetWin_1) and one of the children had classname(RenderWidgetHostHWND).

I think I have managed it though.

Another thing that came up was that Windows8 doesn't store the Chrome Application in the same place all the time. Some versions are under Programs(x86) and others are under appdata. Not sure if there was a switch back to the Windows7 location or something, but I've included this in my code incase anyone else can make use of it (links are noted in the code as I tend to confuse people with my disjointed 'style').

There is also a macro for BringWindowToFront as the API call BringWindowToTop wasn't managing it. Where possible I have noted who posted the code and where from, although i'm sure I've missed some people. I would still like to say thanks for getting me this far.

I haven't had time to comment my code fully and if it's working, I probably won't as this was sending me in circles. After Identifying the href with the correct classname, I then had to loop to find the next parent before checking that the href's ultimate parent had the appropriate classname.

If you spot anything wrong, please let me know, or tell me if something will make it not work.

'Mark007 - VBA to find all Window Handles http://www.vbaexpress.com/kb/getarticle.php?kb_id=52
'Thanks to Ivan F Moala at MrExcel - I'm not sure if i used any of his code, but reading the comments on his code defiantely helped my understanding.
'Jaafar Tribak who posted the BringWindowToFront function at the start of the month
'Scott Huish who posted the GetFolder macro (helped with disparity in location of Chrome)
Option Explicit

Private Declare Function SHGetFolderPath Lib "shell32.dll" Alias "SHGetFolderPathA" (ByVal hwndOwner As Long, ByVal nFolder As Long, ByVal hToken As Long, ByVal dwFlags As Long, ByVal lpszPath As String) As Long
Private Declare Function SetForegroundWindow Lib "User32.dll" (ByVal hwnd As Long) As Long
Private Declare Function ShowWindow Lib "User32.dll" (ByVal hwnd As Long, ByVal lCmdShow As Long) As Boolean
Private Declare Function GetAncestor Lib "user32" (ByVal hwnd As Long, ByVal flags As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As Long, ByVal wFlag As Long) As Long
Private Declare Function AttachThreadInput Lib "user32" (ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As Long) As Long
Private Declare Function GetForegroundWindow Lib "user32" () As Long
Private Declare Function IsIconic Lib "user32" (ByVal hwnd As Long) As Long

Private Const S_OK = &H0
Private Const S_FALSE = &H1
Private Const E_INVALIDARG = &H80070057
Private Const SHGFP_TYPE_CURRENT = 0
Private Const SHGFP_TYPE_DEFAULT = 1
Private Const GW_HWNDNEXT = 2
Private Const GA_PARENT = 1
Private Const SW_SHOW = 5
Private Const SW_RESTORE = 9

Public retainedChromeHwnd As Long, ChildHwnd As Long, ChildFound As Boolean, origChildFound As Boolean
Public NextHandle As Boolean, GotNextParent As Boolean

Private Function BringWindowToFront(ByVal hwnd As Long) As Boolean
'Many thanks to Jaafar Tribak who posted this on MrExcel
'http://www.mrexcel.com/forum/excel-questions/730660-visual-basic-applications-code-maximise-view-activeworksheet-after-having-ie-navigation.html
    Dim ThreadID1 As Long
    Dim ThreadID2 As Long
    Dim nRet As Long

    On Error Resume Next

    ' Nothing to do if already in foreground.
    If hwnd = GetForegroundWindow() Then
        BringWindowToFront = True
    Else
        'First need to get the thread responsible for this window,
        'and the thread for the foreground window.
        ThreadID1 = _
        GetWindowThreadProcessId(GetForegroundWindow, ByVal 0&)
        ThreadID2 = _
        GetWindowThreadProcessId(hwnd, ByVal 0&)

        'By sharing input state, threads share their concept of
        'the active window.
        Call AttachThreadInput(ThreadID1, ThreadID2, True)
        nRet = SetForegroundWindow(hwnd)

        'Restore and repaint.
        If IsIconic(hwnd) Then
            Call ShowWindow(hwnd, SW_RESTORE)
        Else
            Call ShowWindow(hwnd, SW_SHOW)
        End If

        'BringWindowToFront returns TRUE if success.
        BringWindowToFront = CBool(nRet)
    End If
End Function

Private Function GetFolder(ByVal lngFolder As Long) As String
' With thanks to Scott Huish who posted this macro on MrExcel
'http://www.mrexcel.com/forum/excel-questions/706627-using-visual-basic-applications-open-links-non-default-browser.html

    Dim strBuffer As String * 1000
    Dim strPath As String
    Dim lngReturn As Long

    lngReturn = SHGetFolderPath(0&, lngFolder, 0&, SHGFP_TYPE_CURRENT, strBuffer)
    If lngReturn = S_OK Then
        strPath = Left$(strBuffer, InStr(strBuffer, Chr$(0)) - 1)
    Else
        strPath = "(error)"
    End If
    GetFolder = strPath
End Function

Public Sub Chromelink(hparent As Long, xcount As Long)
Dim ChromeID As Long, strtext As String, ChromeClassName As String, ChromeHwnd As Long
Dim lngret As Long

ChromeHwnd = FindWindowEx(hparent, 0&, vbNullString, vbNullString)
If origChildFound = True Then
    ChromeHwnd = retainedChromeHwnd
    origChildFound = False
End If
If ChildFound = True And GotNextParent = True Then
    Exit Sub
ElseIf ChildFound = True Then
    NextHandle = True
    ChildFound = False
End If
While ChromeHwnd <> 0
    strtext = String$(100, Chr$(0))
    lngret = GetClassName(ChromeHwnd, strtext, 100)
    ChromeClassName = Left$(strtext, lngret)
    If ChromeClassName = "Chrome_RenderWidgetHostHWND" Then
        ChildFound = True
        ChildHwnd = ChromeHwnd
    End If

    xcount = xcount + 1
    Chromelink ChromeHwnd, xcount 'loop through next level of child windows

    If ChildFound = True Then Exit Sub
    ChromeHwnd = FindWindowEx(hparent, ChromeHwnd, vbNullString, vbNullString)
    If hparent = 0 And NextHandle = True Then
        retainedChromeHwnd = ChromeHwnd
        ChildFound = True
        GotNextParent = True
    End If
Wend
End Sub

Sub ChromeSetup()
Dim myURL As String, ChromePath As String, strtext As String, lngret As Long
Dim ChromeOpen As Boolean
Dim W As Object
Dim ProcessQuery As String
Dim processes As Object
Dim process As Object

Set W = GetObject("winmgmts:")
ProcessQuery = "SELECT * FROM win32_process"
Set processes = W.execquery(ProcessQuery)
'helpful process properties - http://msdn.microsoft.com/en-us/library/aa394372(v=vs.85).aspx
For Each process In processes
    'check if Chrome is open
    If process.Name = "chrome.exe" Then
        'Chrome is open, find the Handle for the Chrome Browser
        Chromelink 0&, 0
        strtext = String$(100, Chr$(0))
        lngret = GetClassName(ChildHwnd, strtext, 100)
        'loop incase of more siblings
        While Not Left$(strtext, lngret) = "Chrome_WidgetWin_1"
            ChildHwnd = GetAncestor(ChildHwnd, GA_PARENT)
            strtext = String$(100, Chr$(0))
            lngret = GetClassName(ChildHwnd, strtext, 100)
            'Duplicate of classname but WidgetWin_0
            If ChildHwnd = 0 Then
                origChildFound = True
                Chromelink retainedChromeHwnd, 0
            End If
        Wend
        ChromeOpen = True
        Exit For
    End If
Next process

myURL = "http://www.google.com/"

If ChromeOpen = False Then 'Chrome needs to be opened so more time required
    'which localtion is Chrome at?
    ChromePath = "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
    If Len(Dir(ChromePath)) Then
        shell "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe -url " & myURL
    Else
        ChromePath = GetFolder(&H1C) & "\Google\Chrome\Application\chrome.exe"
        If Len(Dir(ChromePath)) Then
            shell GetFolder(&H1C) & "\Google\Chrome\Application\chrome.exe -url " & myURL
        Else
            MsgBox "Chrome could not be found."
            Exit Sub
        End If
    End If
    Application.Wait Now() + TimeValue("00:00:10")
Else
    BringWindowToFront ChildHwnd
    Application.Wait Now() + TimeValue("00:00:01")
End If
End Sub

I probably should put error handling in and maybe find out a little more about the 'borrowed' code. Some of it I don't understand - even the basics like 0 is not the same as 0&. That one threw me for a bit.

Upvotes: 2

Blackhawk
Blackhawk

Reputation: 6120

The parameters in the FindWindow function are optional. If you provide only lpClassName, it will find the handle first window of that class. If you provide only the lpWindowName, the handle first window with that name. If you provide both, then only the handle of a window matching both conditions can be returned. I (unfortunately) have Internet Explorer, so I would do what you are asking as follows, assuming there is only one window:

Declare Function FindWindow Lib "User32.dll" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As Long) As Long

Public Sub Test()
    Dim ClassName As String
    Dim WindowName As String
    Dim hwnd As Long
    Dim Ret As Long
    ClassName = "IEFrame" 'You would use "Chrome_WidgetWin_1"
    WindowName = vbNullString
    hwnd = FindWindow(ClassName, WindowName)
    Ret BringWindowToTop(hwnd)
End Sub

If you have more than one Chrome window, you would have to look at using the EnumWindows function instead.

Upvotes: 2

Related Questions