I've been trying to see if I could make any progress on this, so I've been exploring and benchmarking the code. I thought that I'd share what I found and the code I used to get it.
Most of the time for the ACC code seems to be taken up by traversing the ACC tree to find the index of the address bar. This involves a great deal of DLL calls, so GetAddressBar() can take between 100 and 600 milliseconds on my computer.
I'm not sure why, but for me when I run chrome, it has to run the "GetAddressBar()" function every time GetBrowserURL_ACC() runs. Based on the June 2015 update, I didn't think it would need to do this. What appears to be happening is that the "; In case of a nested browser window as in CoolNovo" conditional runs from within GetBrowserURL_ACC(). This causes GetAddressBar() to be called even though the window hasn't changed.
I'm very curious as to why this GetAddressBar() runs every time GetBrowserURL_ACC() runs for me in Chrome. Aside from the fact that I like my computer to run quickly, I have reason to believe that this may be involved in the errors I have described above. 100-600 milliseconds is long enough that the browser could change state while GetAddressBar() is running. This could lead to an inconsistent internal state in AHK which could cause the errors.
The extra calles to GetAddressBar() with Chrome seem to be caused by the following lines of code in GetBrowserURL_ACC():
For some reason, sURL == "" is evaluating as true even when the Chrome window hasn't changed. Any idea why this might be?
To gather my data, I noted that there are two times places in GetBrowserURL_ACC where GetAddressBar is called. For each of those, I inserted a bit of code that gathers a bit of data and prints it to a log. Here is the main code that I inserted (my full code is further down in the post):
is just a terrific little DLL for getting timing on things. It's very fast and microsecond accurate.
Other code I have running calls GetActiveBrowserURL() every second. I ran it while switching browser windows every couple of seconds. Note that whenever Chrome is active, it runs GetAddressBar every second and that it is the "nested window" portion of GetBrowserURL_ACC() that calls it.
GetAddressBar() run. Current Time: 16:42:06.732 Reason it was called: different window. Time to run: 2094975 microseconds. ACC Index of the address bar: 43032 WinClass=MozillaWindowClass WinProcess=firefox.exe WinTitle=AutoHotkey - Mozilla Firefox WinID=0xc255c0 DDLTime=196118 RETime=0
GetAddressBar() run. Current Time: 16:42:02.792 Reason it was called: nested window. Time to run: 158800 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=39652 RETime=0
GetAddressBar() run. Current Time: 16:42:01.763 Reason it was called: nested window. Time to run: 129820 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=50657 RETime=0
GetAddressBar() run. Current Time: 16:42:00.794 Reason it was called: nested window. Time to run: 160226 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=74098 RETime=0
GetAddressBar() run. Current Time: 16:42:00.298 Reason it was called: nested window. Time to run: 201079 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=44920 RETime=0
GetAddressBar() run. Current Time: 16:42:00.093 Reason it was called: different window. Time to run: 459829 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=163897 RETime=0
GetAddressBar() run. Current Time: 16:41:53.342 Reason it was called: different window. Time to run: 707370 microseconds. ACC Index of the address bar: 43032 WinClass=MozillaWindowClass WinProcess=firefox.exe WinTitle=AutoHotkey - Mozilla Firefox WinID=0xc255c0 DDLTime=151660 RETime=0
GetAddressBar() run. Current Time: 16:41:52.051 Reason it was called: nested window. Time to run: 416432 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=135304 RETime=0
GetAddressBar() run. Current Time: 16:41:51.076 Reason it was called: nested window. Time to run: 441719 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=130966 RETime=0
GetAddressBar() run. Current Time: 16:41:50.227 Reason it was called: nested window. Time to run: 407834 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=71712 RETime=0
GetAddressBar() run. Current Time: 16:41:49.799 Reason it was called: different window. Time to run: 165490 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=62353 RETime=0
GetAddressBar() run. Current Time: 16:41:47.768 Reason it was called: nested window. Time to run: 133006 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=54699 RETime=0
GetAddressBar() run. Current Time: 16:41:46.791 Reason it was called: nested window. Time to run: 157311 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=40447 RETime=0
GetAddressBar() run. Current Time: 16:41:46.309 Reason it was called: nested window. Time to run: 187657 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=62081 RETime=0
GetAddressBar() run. Current Time: 16:41:46.119 Reason it was called: different window. Time to run: 486120 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Get the URL of the current (active) browser tab - Page 2 - AutoHotkey Community - Google Chrome WinID=0x50c3a DDLTime=151155 RETime=0
GetAddressBar() run. Current Time: 16:41:29.202 Reason it was called: different window. Time to run: 568684 microseconds. ACC Index of the address bar: 43032 WinClass=MozillaWindowClass WinProcess=firefox.exe WinTitle=AutoHotkey - Mozilla Firefox WinID=0xc255c0 DDLTime=115625 RETime=0
GetAddressBar() run. Current Time: 16:41:28.073 Reason it was called: nested window. Time to run: 436906 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=71719 RETime=0
GetAddressBar() run. Current Time: 16:41:27.047 Reason it was called: nested window. Time to run: 411827 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=196384 RETime=0
GetAddressBar() run. Current Time: 16:41:26.298 Reason it was called: nested window. Time to run: 450865 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=81585 RETime=0
GetAddressBar() run. Current Time: 16:41:25.845 Reason it was called: different window. Time to run: 211397 microseconds. ACC Index of the address bar: 4123253 WinClass=Chrome_WidgetWin_1 WinProcess=chrome.exe WinTitle=Meet Google Drive – One place for all your files - Google Chrome WinID=0xd5cea DDLTime=134734 RETime=0
GetAddressBar() run. Current Time: 16:41:06.268 Reason it was called: different window. Time to run: 634184 microseconds. ACC Index of the address bar: 43032 WinClass=MozillaWindowClass WinProcess=firefox.exe WinTitle=AutoHotkey - Mozilla Firefox WinID=0xc255c0 DDLTime=122318 RETime=0
Code: Select all
; AutoHotkey Version: AutoHotkey 1.1
; Language: English
; Platform: Win7 SP1 / Win8.1 / Win10
; Author: Antonio Bueno <user atnbueno of Google's popular e-mail service>
; Short description: Gets the URL of the current (active) browser tab for most modern browsers
; Last Mod: 2015-10-18
Menu, Tray, Icon, % A_WinDir "\system32\netshell.dll", 86 ; Shows a world icon in the system tray
#u::
nTime := A_TickCount
sURL := GetActiveBrowserURL()
WinGetClass, sClass, A
If (sURL != "")
MsgBox, % "The URL is """ sURL """`nEllapsed time: " (A_TickCount - nTime) " ms (" sClass ")"
Else If sClass In IEFrame,ApplicationFrameWindow,MozillaWindowClass,OperaWindowClass,Chrome_WidgetWin_1,Chrome_WidgetWin_0,Slimjet_WidgetWin_1,Maxthon3Cls_MainFrm
MsgBox, % "The URL couldn't be determined (" sClass ")"
Else
MsgBox, % "Not a browser or browser not supported (" sClass ")"
Return
GetActiveBrowserURL() {
WinGetClass, sClass, A
If sClass In Chrome_WidgetWin_1,Chrome_WidgetWin_0,Maxthon3Cls_MainFrm
Return GetBrowserURL_ACC(sClass)
Else
Return GetBrowserURL_DDE(sClass) ; empty string if DDE not supported (or not a browser)
}
; "GetBrowserURL_DDE" adapted from DDE code by Sean, (AHK_L version by maraskan_user)
; Found at http://autohotkey.com/board/topic/17633-/?p=434518
GetBrowserURL_DDE(sClass) {
WinGet, sServer, ProcessName, % "ahk_class " sClass
StringTrimRight, sServer, sServer, 4
iCodePage := A_IsUnicode ? 0x04B0 : 0x03EC ; 0x04B0 = CP_WINUNICODE, 0x03EC = CP_WINANSI
DllCall("DdeInitialize", "UPtrP", idInst, "Uint", 0, "Uint", 0, "Uint", 0)
hServer := DllCall("DdeCreateStringHandle", "UPtr", idInst, "Str", sServer, "int", iCodePage)
hTopic := DllCall("DdeCreateStringHandle", "UPtr", idInst, "Str", "WWW_GetWindowInfo", "int", iCodePage)
hItem := DllCall("DdeCreateStringHandle", "UPtr", idInst, "Str", "0xFFFFFFFF", "int", iCodePage)
hConv := DllCall("DdeConnect", "UPtr", idInst, "UPtr", hServer, "UPtr", hTopic, "Uint", 0)
hData := DllCall("DdeClientTransaction", "Uint", 0, "Uint", 0, "UPtr", hConv, "UPtr", hItem, "UInt", 1, "Uint", 0x20B0, "Uint", 10000, "UPtrP", nResult) ; 0x20B0 = XTYP_REQUEST, 10000 = 10s timeout
sData := DllCall("DdeAccessData", "Uint", hData, "Uint", 0, "Str")
DllCall("DdeFreeStringHandle", "UPtr", idInst, "UPtr", hServer)
DllCall("DdeFreeStringHandle", "UPtr", idInst, "UPtr", hTopic)
DllCall("DdeFreeStringHandle", "UPtr", idInst, "UPtr", hItem)
DllCall("DdeUnaccessData", "UPtr", hData)
DllCall("DdeFreeDataHandle", "UPtr", hData)
DllCall("DdeDisconnect", "UPtr", hConv)
DllCall("DdeUninitialize", "UPtr", idInst)
csvWindowInfo := StrGet(&sData, "CP0")
StringSplit, sWindowInfo, csvWindowInfo, `" ; " ; this comment is here just to fix a syntax highlighting bug
Return sWindowInfo2
}
GetBrowserURL_ACC(sClass) {
global nWindow, accAddressBar
If (nWindow != WinExist("ahk_class " sClass)) ; reuses accAddressBar if it's the same window
{
nWindow := WinExist("ahk_class " sClass)
accAddressBar := NewAddressBar(Acc_ObjectFromWindow(nWindow), "different window")
}
Try sURL := accAddressBar.accValue(0)
If (sURL == "") {
WinGet, nWindows, List, % "ahk_class " sClass ; In case of a nested browser window as in CoolNovo
If (nWindows > 1) {
accAddressBar := NewAddressBar(Acc_ObjectFromWindow(nWindows2), "nested window")
Try sURL := accAddressBar.accValue(0)
}
}
If ((sURL != "") and (SubStr(sURL, 1, 4) != "http")) ; Chromium-based browsers omit "http://"
sURL := "http://" sURL
Return sURL
}
NewAddressBar(Object, caller){
Global ll_QPCFreq, foundindices, RegExTime, DLLTime
foundindices := ""
RegExTime := DLLTime := 0
WinGet, WinID, ID, A
WinGetTitle, WinTitle, ahk_id %WinID%
WinGetClass, WinClass, ahk_id %WinID%
WinGet, WinProcess, ProcessName, ahk_id %WinID%
DllCall("QueryPerformanceCounter", "Int64 *", GAB_QPC_Bef)
GetAddressBar(Object, "")
DllCall("QueryPerformanceCounter", "Int64 *", GAB_QPC_Aft)
GABTime := Round(((GAB_QPC_Aft-GAB_QPC_Bef)*1000000)/ll_QPCFreq)
Log("GetAddressBar() run. Current Time: " A_Hour ":" A_Min ":" A_Sec "." A_MSec " Reason it was called: " caller ". Time to run: " GABTime " microseconds. ACC Index of the address bar: " foundindices " WinClass=" WinClass " WinProcess=" WinProcess " WinTitle=" WinTitle " WinID=" WinID " DDLTime=" Round((DLLTime*1000000)/ll_QPCFreq) " RETime=" Round((RETime*1000000)/ll_QPCFreq))
}
Log(string) {
global MLM
MLM.Log(string)
}
; "GetAddressBar" based in code by uname
; Found at http://autohotkey.com/board/topic/103178-/?p=637687
GetAddressBar(accObj, indices) {
global foundindices, MLM
Try If ((accObj.accName(0) != "") and IsURL(accObj.accValue(0))) {
foundindices .= indices ;","
Return accObj
}
Try If ((accObj.accName(0) != "") and IsURL("http://" accObj.accValue(0))) { ; Chromium omits "http://"
foundindices .= indices ;","
Return accObj
}
For nChild, accChild in Acc_Children(accObj) {
LoopLog( indices nchild)
If IsObject(accAddressBar := GetAddressBar(accChild, indices nChild))
Return accAddressBar
}
}
IsURL(sURL) {
global RegExTime
DllCall("QueryPerformanceCounter", "Int64 *", RE_QPC_Bef)
RetVal := RegExMatch(sURL, "^(?<Protocol>https?|ftp)://(?<Domain>(?:[\w-]+\.)+\w\w+)(?::(?<Port>\d+))?/?(?<Path>(?:[^:/?# ]*/?)+)(?:\?(?<Query>[^#]+)?)?(?:\#(?<Hash>.+)?)?$")
DllCall("QueryPerformanceCounter", "Int64 *", RE_QPC_Aft)
RegExTime += (RE_QPC_Aft - RE_QPC_Bef)
Return RetVal
}
; The code below is part of the Acc.ahk Standard Library by Sean (updated by jethrow)
; Found at http://autohotkey.com/board/topic/77303-/?p=491516
Acc_Init()
{
static h
If Not h
h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromWindow(hWnd, idObject = 0)
{
Acc_Init()
If DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
Return ComObjEnwrap(9,pacc,1)
}
Acc_Query(Acc) {
Try Return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Acc_Children(Acc) {
Global DLLTime
If ComObjType(Acc,"Name") != "IAccessible"
ErrorLevel := "Invalid IAccessible Object"
Else {
Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
DllCall("QueryPerformanceCounter", "Int64 *", DLL_QPC_Bef)
temp := DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)
DllCall("QueryPerformanceCounter", "Int64 *", DLL_QPC_Aft)
DLLTime += (DLL_QPC_Aft - DLL_QPC_Bef)
If (temp=0) {
Loop %cChildren%
i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i), Children.Insert(NumGet(varChildren,i-8)=9?Acc_Query(child):child), NumGet(varChildren,i-8)=9?ObjRelease(child):
Return Children.MaxIndex()?Children:
} Else
ErrorLevel := "AccessibleChildren DllCall Failed"
}
}
Any thoughts on why sURL == "" is evaluating as true even when the Chrome window hasn't changed? I'd very much like to be able to speed this code up.