for this excellent function. On a Windows 10 64-bit system (with latest updates as of 2020-05-14) running both Chrome (Version 81.0.4044.138) and Internet Explorer 11, AHK Version 1.1.32.0. With both Chrome and IE running, even if I click on IE last to make it active, or with all instances of Chrome gone, the script defaults to checking Chrome when running from Visual Studio Code.
Window Spy reports VS Code as having Chrome_WidgetWin_1 as ahk_class. I'm unsure if that caused issues, but that was the class the script kept getting. [
] I needed it to get the ahk_class IEFrame. I don't fully understand all the code, but I'm unsure if it checks against ahk_exe and combines with ahk_class.
I am curious why the variables LegacyBrowsers and ModernBrowsers sit outside the function instead of inside?
I added a parameter and supporting code to "force" it to check the legacy browser first. Comments and notes are included in the code.
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
; Change log:
; 2020-05-13 1) Added legacy parameter and code to "force" checking legacy
; browser first. 2) Replaced "Last mod:" field in the description
; with "Change log:" to track changes. 3) Commented out and added instruction
; in the demo section so current users can do an in-place update to their lib.
; Thomas T. Thai <thomast.t.hai@gmail.com>
; 2016-05-19 Last update by Antonio Bueno <atnbueno@gmail.com>
; Move these to variables to the top so they are automatically defined when this
; file is included and the demo is commented out. @thomastthai
ModernBrowsers := "ApplicationFrameWindow,Chrome_WidgetWin_0,Chrome_WidgetWin_1,Maxthon3Cls_MainFrm,MozillaWindowClass,Slimjet_WidgetWin_1"
LegacyBrowsers := "IEFrame,OperaWindowClass"
; --- <Demo> -------------------------------------------------------------------
;
; Instructions
; ------------
; To run the demo, uncomment the two lines indicated by the <--- arrows below.
;
/* ; <--- remove the */ to uncomment to run the demo
Menu, Tray, Icon, % A_WinDir "\system32\netshell.dll", 86 ; Shows a world icon in the system tray
^+!u:: ; Ctrl-Alt-Shift u
nTime := A_TickCount
sURL := GetActiveBrowserURL(True)
WinGetClass, sClass, A
If (sURL != "")
MsgBox, % "The URL is """ sURL """`nEllapsed time: " (A_TickCount - nTime) " ms (" sClass ")"
Else If sClass In % ModernBrowsers "," LegacyBrowsers
MsgBox, % "The URL couldn't be determined (" sClass ")"
Else
MsgBox, % "Not a browser or browser not supported (" sClass ")"
Return
*/ ; <--- remove the */ to uncomment to run the demo
; --- </Demo> ------------------------------------------------------------------
GetActiveBrowserURL(legacy = False) {
global ModernBrowsers, LegacyBrowsers
; @thomastthai - added parameter legacy and added the code below to handle
; systems where modern browser and legacy browsers are both running.
; My Windows 10 Pro system defaults to checking Chrome instead of
; Internet Explorer 11. and we want to check on the legacy active window.
if legacy {
WinGetClass, sClass, ahk_class IEFrame
If (sClass = "")
WinGetClass, sClass, ahk_class OperaWindowClass
}
Else
WinGetClass, sClass, A
If sClass In % ModernBrowsers
Return GetBrowserURL_ACC(sClass)
Else If sClass In % LegacyBrowsers
Return GetBrowserURL_DDE(sClass) ; empty string if DDE not supported (or not a browser)
Else
Return ""
}
; "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, `" ;"; comment to avoid a syntax highlighting issue in autohotkey.com/boards
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 := GetAddressBar(Acc_ObjectFromWindow(nWindow))
}
Try sURL := accAddressBar.accValue(0)
If (sURL == "") {
WinGet, nWindows, List, % "ahk_class " sClass ; In case of a nested browser window as in the old CoolNovo (TO DO: check if still needed)
If (nWindows > 1) {
accAddressBar := GetAddressBar(Acc_ObjectFromWindow(nWindows2))
Try sURL := accAddressBar.accValue(0)
}
}
If ((sURL != "") and (SubStr(sURL, 1, 4) != "http")) ; Modern browsers omit "http://"
sURL := "http://" sURL
If (sURL == "")
nWindow := -1 ; Don't remember the window if there is no URL
Return sURL
}
; "GetAddressBar" based in code by uname
; Found at http://autohotkey.com/board/topic/103178-/?p=637687
GetAddressBar(accObj) {
Try If ((accObj.accRole(0) == 42) and IsURL(accObj.accValue(0)))
Return accObj
Try If ((accObj.accRole(0) == 42) and IsURL("http://" accObj.accValue(0))) ; Modern browsers omit "http://"
Return accObj
For nChild, accChild in Acc_Children(accObj)
If IsObject(accAddressBar := GetAddressBar(accChild))
Return accAddressBar
}
IsURL(sURL) {
Return RegExMatch(sURL, "^(?<Protocol>https?|ftp)://(?<Domain>(?:[\w-]+\.)+\w\w+)(?::(?<Port>\d+))?/?(?<Path>(?:[^:/?# ]*/?)+)(?:\?(?<Query>[^#]+)?)?(?:\#(?<Hash>.+)?)?$")
}
; 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) {
If ComObjType(Acc,"Name") != "IAccessible"
ErrorLevel := "Invalid IAccessible Object"
Else {
Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
If DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=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"
}
}