3

我发现了一些代码可以将浏览器置于最前面,即使它已被最小化。但是,我不知道如何获取浏览器的 hwnd。

我看过 FindWindow API 函数,但你需要类名和窗口名。我发现我需要的类名是“Chrome_WidgetWin_1”窗口名虽然会根据浏览器中打开的内容而不断变化。

我认为这个窗口的一个“孩子”的类名是“WrapperNativeWindowClass”我不确定我是否可以用它来找到原来的父母。

谁能建议如何找到 chrome 浏览器的窗口句柄(使用 VBA)?

4

3 回答 3

3

典型的 Chrome/Chromium 运行会话(多进程,处理渲染/进程外 iframe 和离线浏览器应用程序和(标准)扩展。

典型的运行 NirSoft的优秀WinExplorer工具(以及许多其他工具)

显示以下(可能与您的类似)类显示 在此处输入图像描述

尝试同时寻找Chrome_WidgetWin_1Chrome_RenderWidgetHostHWND

于 2015-10-15T12:19:27.830 回答
1

谢谢史蒂夫 - 我很感激你花时间来看看这个。它证实我在正确的路线上。

在浏览器打开并且只显示一个默认选项卡的情况下,有 4 个“Chrome_WidgetWin_1”实例。

我还发现如果浏览器打开但不在 gmail 中,则类名“WrapperNativeWindowClass”不合适。我最终使用了类名“Chrome_RenderWidgetHostHWND”(下一个兄弟)——但也有不止一个实例。所以我必须确定父级的类名(Chrome_WidgetWin_1)和其中一个孩子的类名(RenderWidgetHostHWND)。

我想我已经做到了。

出现的另一件事是 Windows8 并不总是将 Chrome 应用程序存储在同一个地方。有些版本在 Programs(x86) 下,而其他版本在 appdata 下。不确定是否有切换回 Windows7 位置或其他什么,但我已将其包含在我的代码中,以防其他人可以使用它(链接在代码中注明,因为我倾向于将人们与我脱节的“风格”混淆')。

BringWindowToFront 也有一个宏,因为 API 调用 BringWindowToTop 没有管理它。在可能的情况下,我已经注意到谁发布了代码以及来自哪里,尽管我确信我错过了一些人。我仍然想说谢谢你让我走到这一步。

我没有时间完全评论我的代码,如果它有效,我可能不会,因为这让我陷入困境。在用正确的类名识别 href 之后,我必须循环查找下一个父级,然后再检查 href 的最终父级是否具有适当的类名。

如果您发现任何错误,请告诉我,或者告诉我是否有什么会使其无法正常工作。

'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

我可能应该将错误处理放入其中,并且可能会更多地了解“借用”代码。其中一些我不明白 - 甚至像 0 这样的基础知识也与 0& 不同。那个让我有点震惊。

于 2013-11-01T13:11:16.690 回答
1

FindWindow 函数中的参数是可选的。如果您只提供lpClassName,它将找到该类的句柄第一个窗口。如果您仅提供lpWindowName,则处理具有该名称的第一个窗口。如果两者都提供,则只能返回匹配这两个条件的窗口句柄。我(不幸的是)有 Internet Explorer,所以假设只有一个窗口,我会按照您的要求执行以下操作:

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

如果您有多个 Chrome 窗口,则必须考虑使用该EnumWindows功能。

于 2013-10-31T18:29:37.233 回答