vba 查找 Chrome 浏览器的窗口句柄
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19705797/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Find the Window handle for a Chrome Browser
提问by Glib
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.
我找到了一些代码,即使它被最小化,也可以将浏览器置于最前面。但是,我不知道如何获取浏览器的 hwnd。
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.
我看过 FindWindow API 函数,但您需要类名和窗口名。我发现我需要的类名是“Chrome_WidgetWin_1”窗口名虽然会根据浏览器中打开的内容不断变化。
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.
我认为此窗口的“子项”之一的类名是“WrapperNativeWindowClass”,我不确定是否可以使用它来查找原始父项。
Can anyone advise on how to find the window handle of a chrome browser (using VBA)?
谁能建议如何找到 chrome 浏览器的窗口句柄(使用 VBA)?
回答by Glib
A typical Chrome/Chromium run session (multi-process, handling rendering/out of process iframes and offline-browser-applications and (standard) extensions.
典型的 Chrome/Chromium 运行会话(多进程,处理渲染/进程外 iframe 和离线浏览器应用程序和(标准)扩展。
NirSoft's excellent WinExplorertool (among many many others)
NirSoft出色的WinExplorer工具(以及许多其他工具)
shows the following (probably similar to yours) class displays
try looking for both Chrome_WidgetWin_1
and Chrome_RenderWidgetHostHWND
尝试同时寻找Chrome_WidgetWin_1
和Chrome_RenderWidgetHostHWND
回答by Blackhawk
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:
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
If you have more than one Chrome window, you would have to look at using the EnumWindows
function instead.
如果您有多个 Chrome 窗口,则必须考虑使用该EnumWindows
功能。
回答by Glib
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".
在浏览器打开并且只显示一个默认选项卡的情况下,有 4 个“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).
我还发现如果浏览器打开但不在 gmail 中,则类名“WrapperNativeWindowClass”不合适。我最终使用了类名“Chrome_RenderWidgetHostHWND”(下一个兄弟) - 但也有不止一个这样的实例。所以我必须确定父级的类名(Chrome_WidgetWin_1)和其中一个孩子的类名(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').
出现的另一件事是 Windows8 不会一直将 Chrome 应用程序存储在同一位置。某些版本在 Programs(x86) 下,而其他版本在 appdata 下。不确定是否切换回 Windows7 位置或其他位置,但我已将其包含在我的代码中,以防其他人可以使用它(代码中注明了链接,因为我倾向于将人们与我脱节的“风格”混淆')。
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.
也有一个用于BringWindowToFront 的宏,因为API 调用BringWindowToTop 没有管理它。在可能的情况下,我已经注意到谁发布了代码以及从哪里发布,尽管我确定我错过了一些人。我仍然想说谢谢你让我走到这一步。
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.
我还没有时间完整地评论我的代码,如果它可以工作,我可能不会,因为这让我转了一圈。在用正确的类名识别 href 之后,我必须循环查找下一个父级,然后检查 href 的最终父级是否具有适当的类名。
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.
我可能应该加入错误处理,并且可能会更多地了解“借用”的代码。其中一些我不明白-即使像 0 这样的基础知识也与 0& 不同。那个把我扔了一会儿。