Get the URL of the current (active) browser tab

Post your working scripts, libraries and tools for AHK v1.1 and older
mc-lemons
Posts: 6
Joined: 19 Jan 2016, 18:22

Re: Get the URL of the current (active) browser tab

12 Feb 2016, 12:45

Antonio,

I do agree that the code you proposed is very easy to use/adapt. However, from an organizational standpoint, I would prefer a solution with the #IfWinActive. That way, I can organize my code by website, like in the example below:

#IfURLActive, Website 1 url

^a::do action 1a
^b::do action 1b

#IfURLActive

#IfURLActive, Website 2 url

^a::do action 2a
^b::do action 2b

#IfURLActive

Doing it the other way would create a potentially long If/Then string for each hotkey, which would continue to grow for any additional website added in the future. So if you don't mind sharing the code, I'd really appreciate. Otherwise, I'll just go with your initial solution.

Thanks,
Michael
User avatar
atnbueno
Posts: 89
Joined: 12 Oct 2013, 04:45
Contact:

Re: Get the URL of the current (active) browser tab

16 Feb 2016, 14:15

Hello again.

I'm sorry but I what I have consumes a measurable amount of CPU and it's part of a very specific script. It would require significant time to isolate that particular functionality and to ensure it works in a general scenario.


Regards,
Antonio
mc-lemons
Posts: 6
Joined: 19 Jan 2016, 18:22

Re: Get the URL of the current (active) browser tab

16 Feb 2016, 16:30

Understood. Thanks again for the help!
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

17 Feb 2016, 15:29

Thank you for this terrific code. I'd like to use it in a script that behaves differently based on the URL in the active window. As such, I'd like to call GetActiveBrowserURL() within a 250 millisecond loop. Something like this:

Code: Select all

SetTimer, MainLoop, 250	

MainLoop:
   sURL := GetActiveBrowserURL()
   DoSomething(sURL)
Return
However, I suspect that for non-ACC browsers, this might involve a performance hit/or possibly not be very green if it is being run every 250 ms.

Is is possible to tweak the DDE version the way that you tweaked the ACC version in the June 2014 version in order to speed it up? Alternatively, could some of the code be pulled out into an initialization function and other code be pulled into an OnExit function such that each time I check the active URL it doesn't have to recreate and re-destroy hServer, hTopic, hItem and hConv, etc?

If not, that's fine. I can just have it only watch when I'm using Chrome or another ACC browser:

Code: Select all

SetTimer, MainLoop, 250	

MainLoop:
	WinGetClass, sClass, A
	If sClass In Chrome_WidgetWin_1,Chrome_WidgetWin_0,Maxthon3Cls_MainFrm {
	   sURL := GetBrowserURL_ACC(sClass)
	   DoSomething(sURL)
	}
Return
I'll be honest, while I do use FireFox about half the time, it wouldn't be so hard to switch when I need this functionality. But it certainly would be nice to be able to do it quickly all of the time.

Thanks for any help and thanks again for the great code!
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

18 Feb 2016, 14:31

I've been running the above code and it has been working terrifically from a performance perspective. However, occasionally I do get an error from it. Both times seemed to happen when the URL was changing when the code was being called. The first time was from a redirect caused by the server. The second time was when I was typing in a search term in Chrome. After I pressed enter, I got the following error from AHK:

[attachment=0]Capture.PNG[/attachment]


A little Googling suggests that error 0x80004005 may be a not uncommon error related to COM, but COM is a little above me, so I can't get much farther in interpreting it.
Attachments
Capture.PNG
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

21 Feb 2016, 13:58

I just got the exact same error again (I checked and the screenshots are identical). This time it happened in Chrome when I closed a Google Calendar tab and it switched to a Google drive tab.

A hypothesis would be that the problem arises from the following code:

Code: Select all

	For nChild, accChild in Acc_Children(accObj)
		If IsObject(accAddressBar := GetAddressBar(accChild))
			Return accAddressBar
As it is looping through Acc_Children(accObj), accObj might be changing, leading to an inconsistent state. In other words, if the browser is redirected (as in my Feb 18 post) or a window is closed (as in today's post) while the code is looping through the above loop, it attempts to access a key-value pair that no longer exists. Acc_Children is called within the above loop, and that is where it crashes.

Perhaps it could be fixed by checking to see if the state is still consistent around line 116? Line 116 is shown in the screenshot - that's where it crashes.

I seem to get an error every 3 days or so and so I'm thinking of asking for help in the Ask for Help subforum, as error 0x80004005 seems to be a pretty deep COM error and they may be able to help with it. If anyone else has any thoughts at all, I'd love to hear them.

Edit: happened again on 2/22 and 2/23
User avatar
Snow
Posts: 4
Joined: 27 Feb 2016, 10:43
Contact:

Re: Get the URL of the current (active) browser tab

27 Feb 2016, 11:06

Hey guys, first post here.

Anyways, I did a little updating to this script for AHK V2 (slowly doing the same for other scripts). It's a little bit messy as I don't really program, but it does function well with V2 and I haven't had any issues with it, though my full integration/purpose for the check isn't written.

Anyways, the code below will wait for Chrome to be active, get the URL and store it. If the active window name changes (this includes refreshes) then update and display new URL.

Code: Select all

; AutoHotkey Version: AutoHotkey V2alpha
; Language:           English
; Platform:           Win7 SP1 / Win8.1 / Win10
; Author:             Antonio Bueno, V2 update by Snow
; Short description:  Gets the URL of the current (active) browser tab for most modern browsers
; Last Mod:           2016-02-27
#SingleInstance Force
Menu, Tray, Icon, % A_WinDir "\system32\netshell.dll", 86 ; Shows a world icon in the system tray
SetTitleMatchMode, 2
CoordMode, Mouse, Screen
sClass:= "Hello!"
oldSURL:=""




while (true)
{
	WinWaitActive("Chrome")
	WinGetTitle, tabName, A
	WinWaitNotActive(tabName)
	;browser := BrowserCheck()
	global sURL := GetActiveBrowserURL()
	WinGetClass, sClass, A
	global oldSURL:=sURL

}


	
	
GetActiveBrowserURL() 
{
	WinGetClass, sClass, A
	If InStr(sClass, 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) {
	WinGetClass, sServer, ProcessName, % "ahk_class " sClass
	SubStr(sServer, 1, -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")
	StrSplit , sWindowInfo, csvWindowInfo, `" ; " ; this comment is here just to fix a syntax highlighting bug
	Return sWindowInfo2
}
 
GetBrowserURL_ACC(sClass) 
{
	global nWindow, accAddressBar
	;MsgBox, 1 , %nWindow%, Internally Accessed l114
	If (nWindow != WinExist("ahk_class " sClass)) ; reuses accAddressBar if it's the same window
	{
		nWindow := format("0x{1:X}",WinExist("ahk_class " sClass))
		
		;MsgBox, Class - %sClass%`n   nWindow - %nWindow% `n Line 120
		accAddressBar := GetAddressBar(Acc_ObjectFromWindow(nWindow))
		;MsgBox %nWindow%
	}
	;MsgBox, accAddressBar.accValue(0)
	Try sURL := accAddressBar.accValue(0)
	;catch e
	;{
	;	; For more detail about the object that e contains, see Catch.
	;	MsgBox, 16,, % "Exception thrown!`n`nwhat: " e.what "`nfile: " e.file
	;. "`nline: " e.line "`nmessage: " e.message "`nextra: " e.extra
	;}
	
	;MsgBox, % "AccAddressBar is  Named" . accAddressBar.accName
	If (sURL == "") 
	{
		WinGetControls, nWindows, List, % "ahk_class " sClass ; In case of a nested browser window as in CoolNovo
		If (nWindows > 1) 
		{
			accAddressBar := objectReturn 
			Try sURL := accAddressBar.accValue(0)
			;MsgBox, sURL - %sURL% Line 130
		}
	}
	If ((sURL != "") and (SubStr(sURL, 1, 4) != "http")) ; Chromium-based browsers omit "http://"
		sURL := "http://" sURL
	TrayTip
	TrayTip, URL,%sURL%
	Sleep(10)
	Return sURL
}
 
; "GetAddressBar" based in code by uname
; Found at http://autohotkey.com/board/topic/103178-/?p=637687
 
GetAddressBar(accObj) 
{
	Try If ((accObj.accName(0) != "") and IsURL(accObj.accValue(0)))
		Return accObj
	Try If ((accObj.accName(0) != "") and IsURL("http://" accObj.accValue(0))) ; Chromium omits "http://"
		Return accObj
	For nChild, accChild in Acc_Children(accObj)
		If IsObject(accAddressBar := GetAddressBar(accChild))
			Return accAddressBar
		;MsgBox, % "AccObj is  Named" . accObj.accName " & Value " . accObj.accValue
		
	;MsgBox, Exiting
}
 
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")
	;MsgBox, %h% ;Go to function directly below
}
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
	
		ComObject := ComObject(9,pacc,1), ObjAddRef(pacc)
	
	;ObjAddRef(pacc)
	;Traytip ,Line 184, "Pacc Variable is %pacc% line 173"
	Return ComObject
}
Acc_Query(Acc) 
{
	Try Return ComObject(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Acc_Children(Acc) 
{
	;MsgBox, 262144, %Acc%, Line 173
	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.Push(NumGet(varChildren,i-8)=9?Acc_Query(child):child), 
			}
			ObjRelease(child):
			Return Children
		} Else
			ErrorLevel := "AccessibleChildren DllCall Failed"
	}

}
^!z::Pause
Ignore the comments, first ever messing with AHK scripts and it took a lot of debugging to update/alter. There's also some tweaks to be made and eventual finalizing into hotkeys, hotstrings or other scripts running only on certain domains.
User avatar
atnbueno
Posts: 89
Joined: 12 Oct 2013, 04:45
Contact:

Re: Get the URL of the current (active) browser tab

27 Feb 2016, 14:14

@JoeSchmoe, with the arrival of Edge and Firefox x64, I now recommend leaving DDE only for legacy browsers (specifically, Internet Explorer and Opera 12). The script I use in my computer now uses AAC for everything else:

Code: Select all

DDE_Browsers := "IEFrame,OperaWindowClass"
ACC_Browsers := "Chrome_WidgetWin_1,Chrome_WidgetWin_0,Slimjet_WidgetWin_1,Maxthon3Cls_MainFrm,ApplicationFrameWindow,MozillaWindowClass"
GetActiveBrowserURL() {
	global ACC_Browsers, DDE_Browsers
	WinGetClass, sClass, A
	If sClass In % ACC_Browsers
		Return GetBrowserURL_ACC(sClass)
	Else If sClass In % DDE_Browsers
		Return GetBrowserURL_DDE(sClass) ; empty string if DDE not supported (or not a browser)
	Else
		Return ""
}
I'll try to reproduce the 0x80004005 error. It has never happened to me.

@Snow what did you had to add/change for the script to work with V2?

I am currently testing a somewhat improved version of the script. I'll try to publish it soon.


Regards,
Antonio
User avatar
Snow
Posts: 4
Joined: 27 Feb 2016, 10:43
Contact:

Re: Get the URL of the current (active) browser tab

28 Feb 2016, 11:54

atnbueno wrote:@JoeSchmoe, with the arrival of Edge and Firefox x64, I now recommend leaving DDE only for legacy browsers (specifically, Internet Explorer and Opera 12). The script I use in my computer now uses AAC for everything else:

Code: Select all

DDE_Browsers := "IEFrame,OperaWindowClass"
ACC_Browsers := "Chrome_WidgetWin_1,Chrome_WidgetWin_0,Slimjet_WidgetWin_1,Maxthon3Cls_MainFrm,ApplicationFrameWindow,MozillaWindowClass"
GetActiveBrowserURL() {
	global ACC_Browsers, DDE_Browsers
	WinGetClass, sClass, A
	If sClass In % ACC_Browsers
		Return GetBrowserURL_ACC(sClass)
	Else If sClass In % DDE_Browsers
		Return GetBrowserURL_DDE(sClass) ; empty string if DDE not supported (or not a browser)
	Else
		Return ""
}
I'll try to reproduce the 0x80004005 error. It has never happened to me.

@Snow what did you had to add/change for the script to work with V2?

I am currently testing a somewhat improved version of the script. I'll try to publish it soon.


Regards,
Antonio
I don't really remember in entirety; but I did have to update function identifiers/syntax, COM upload and retrieval and some others. And then of course modification for it to update on tab/url change which still needs refinement.
User avatar
fump2000
Posts: 313
Joined: 04 Oct 2013, 17:31

Re: Get the URL of the current (active) browser tab

29 Feb 2016, 04:25

Is it possible to use the code to activate a tab with a specific title/URL?
User avatar
atnbueno
Posts: 89
Joined: 12 Oct 2013, 04:45
Contact:

Re: Get the URL of the current (active) browser tab

01 Mar 2016, 08:45

Hello fump2000.

No, this code starts by looking for the active window, and then checking if it's a browser.

I'm not sure it's possible to access the list of tabs of a browser in a general way. If possible, each browser would probably require a different approach.


Regards,
Antonio
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

01 Mar 2016, 22:13

Thank you, Antonio. I'll update my script so that it uses ACC more widely as well. Thank you also for looking into the error I'm getting. I suspect that I am getting the error because my main loop runs every 250 milliseconds, whenever my computer is on. As a result, I get the error almost every day. It would stand to reason, though, that someone who didn't run the code as often would get the same error, just far less frequently.
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

02 Mar 2016, 17:10

Edit: Please ignore the following post for the moment. There may be some problems with it.

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():

Code: Select all

	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 := GetAddressBar(Acc_ObjectFromWindow(nWindows2))
			Try sURL := accAddressBar.accValue(0)
		}
	}
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):

Code: Select all

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))
}
QueryPerformanceCounter 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.

Here is the output:
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
Here is my full GetActiveBrowserURL 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
; 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.
Last edited by JoeSchmoe on 03 Mar 2016, 22:23, edited 1 time in total.
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

02 Mar 2016, 20:14

fump2000 wrote:Is it possible to use the code to activate a tab with a specific title/URL?
Hi fump2000. Try this thread: https://autohotkey.com/boards/viewtopic.php?t=9071
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

03 Mar 2016, 22:13

I just wanted to note that the issue that I described two posts up isn’t showing up today (the sURL == "" one). I’ll continue to keep an eye out to see if it starts again. If not, it could have been a problem that I introduced when benchmarking (if so, I apologize). The other issue, described higher up, is still happening. Update: The problem has returned in that all of a sudden the library seemed incapable of identifying the URL in chrome windows. It appears that I can make the problem go away by restarting the script. I will continue observing the problem so I can speak with more confidence.

When the function runs on an active window that has a new tab with an empty addressbar, the script sometimes finds a link elsewhere. For example, I think I recall it finding a link in the “Mozilla Start Page” today from a promotion that Mozilla was running. One can see how your algorithm might lead to this. Also, right now, if I open up a new tab with an blank addressbar, it finds the URL from an inactive window and presents that as the URL of the active window. One can see how this would happen based on how you made the code language independent. Another approach would be to make a language-specific version that will hopefully avoid these issues. The idea would be to do something similar to uname's code, where the name of the addressbar ACC element (or even it's path) is hard-coded in for the main browsers. It might speed things up and if it failed, it could always fall back on the "IsURL()" approach that is currently in use.

I'm even dreaming of a version that could use your IsURL() version to learn ACC paths of the address bar for various browsers, storing them in an array indexed by the ahk_class or ahk_id of the window. (By ACC path, I'm referring to the thing that is circled in the attached screenshot.)

Also, I may add “file://” to the protocol section of IsURL() because when I double-click on local PDFs or HTML, they often open up in Chrome.

Thanks again for writing this. ACC feels so much more manageable with your code to read, and it's pretty cool that it works in any language!
Last edited by JoeSchmoe on 06 Mar 2016, 13:28, edited 1 time in total.
Folu

Re: Get the URL of the current (active) browser tab

05 Mar 2016, 09:46

Hi All,

Though I know about programming, I don't know anything about AutoHotKeys programming.
I downloaded the program, created an exe and ran it. I see the icon on my status bar and then open different browsers. I don't see any message box showing me the URL of the active broser tab.
Please is someone able to help point me in the right direction on what to do?

Many thanks for your helps and for posting this program
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: Get the URL of the current (active) browser tab

05 Mar 2016, 10:48

Hi Folu,

Just FYI, my impression is that this library is really designed for people who are familiar with AHK. With that said, near the top is a line that says "#u::". This means, press the Windows key and u to get it to start.
Kudos
Posts: 7
Joined: 23 Aug 2015, 05:52

Re: Get the URL of the current (active) browser tab

30 Mar 2016, 06:06

Hi Antonio,

I still can't get it to work in MS Edge. I get the error: The URL couldn't be determined (ApplicationFrameWindow)

Also Slimjet is a chromium browser so should call the ACC function. I edited the below line to make it work:
If sClass In Chrome_WidgetWin_1,Chrome_WidgetWin_0,Maxthon3Cls_MainFrm,Slimjet_WidgetWin_1

All other browsers work brilliantly. I would love to have a solution for MS Edge.

Thanks!
Kudos
User avatar
haichen
Posts: 631
Joined: 09 Feb 2014, 08:24

Re: Get the URL of the current (active) browser tab

10 Apr 2016, 12:47

Hi, there will be a probably a problem with one of the next Firefox-versions. They built a multi-process option/version. It's already activated in the nightly's. I run in this lately. May be you can find a solution. https://autohotkey.com/boards/viewtopic.php?f=5&t=15734
Thanks for your function!!
User avatar
atnbueno
Posts: 89
Joined: 12 Oct 2013, 04:45
Contact:

Re: Get the URL of the current (active) browser tab

13 Apr 2016, 14:04

Hello haichen.

Right now I can't test it with Firefox Nightly, but you can try the modification I suggested a few weeks ago.

As I said there, now I'd only use DDE for legacy browsers.


Kudos, finally I have a Win10 machine and I've just been able to reproduce the problem. Now I can look for a solution :-)

Also in the code mentioned above, Slimjet is now listed in ACC_Browsers :-)


Regards,
Antonio

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 118 guests