Page 1 of 1

Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 24 Dec 2016, 15:44
by jeeswg
If one does a for loop.

Code: Select all

for oWB in ComObjCreate("Shell.Application").Windows
Can one retrieve the hWnds for the 'Internet Explorer_Server' controls?
Thank you.

[EDIT:] This issue is resolved here:
Cast COM Object IWebBrowser2 to IServiceProvider - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=67163

Re: Internet Explorer: get WB hWnds via object loop

Posted: 24 Dec 2016, 16:34
by lexikos
No.

You need to send a message directly to the control. There are examples in other threads.

Re: Internet Explorer: get WB hWnds via object loop

Posted: 24 Dec 2016, 17:17
by jeeswg
When I want to retrieve the urls and titles for websites I do 2 loops.

I loop through the 'Internet Explorer_Server' controls, and grab LocationName, LocationURL and the text from the 'Frame Tab' controls (as a check to confirm LocationName is correct).
These are in the order last accessed, with the most recently accessed (active tab), first (I believe).
[EDIT: document.title/document.url are preferable to LocationName/LocationURL. They are not lost when you clear the IE cache/cookies etc.]

I do the object loop described above, to the get LocationName and LocationURL, in the order the tabs were opened (I believe).

Then I try to combine the two lists (I want them in object loop order).
Is there any identifier that could be retrieved on both loops to tie up the two lists.
Currently I use the url, but you can have the same url more than once with different titles.

Re: Internet Explorer: get WB hWnds via object loop

Posted: 29 Dec 2016, 07:24
by phaleth
You could check for the combination of both the URL and Website's title (tab's name).

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 29 Dec 2016, 16:26
by jeeswg
- Thank you. The script basically does everything that it can to ensure the matching is as good as is possible.
- It's just that if there were unique window object IDs to tie the two lists together, I could do a much simpler rewrite, gain an important universal technique, and would have something easier for people to understand, use, and learn from.
- I've always been curious as to whether there are unique IDs retrievable during both the control loop and object loop that would allow information from both loops to be tied together.

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 30 Dec 2016, 14:24
by phaleth
For more uniqueness you can always query the webpage for example on how many <a>, <p> and <script> tags does it have and add that to the composite string.

If you want it to look like non-sense you can always feed the resulting string into some sort of converter that generates for example a MD5 or SHA1 hash. There are some scripts that can do this on these boards.

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 31 Dec 2016, 06:01
by jeeswg
@phaleth Thank you, that's the curious thing, there's so much information you can get from a document object, you wonder if there is some actual object ID available that would give certainty, that would tie together the items in the two separate loops, furthermore you don't know if the number of tags will change between loops.
Also for example you could have the same url, but say a timestamp in the title that changed between the two loops, so here a common ID would be very useful.

I've provided a basic version of each loop to demonstrate how the initial data is retrieved. Getting the title via the frame tab is security against object Internet titles that have 'gone wrong' e.g. by clearing IE's history, which usually makes the 'title' the same as the url.
Also the frame tab control might say 'hello - Google Search' where as the document object says less usefully: 'Google'.
It should be noted that the frame tab text is usually truncated at 95 characters.
If there is some clash, between the title texts, a prefix such as '[ERROR]' can be added to the front of the retrieved title, for the user to then double-check the title themselves, or perhaps even use a function to download the url to a variable and retrieve the title.

Code: Select all

WinGet, hWnd, ID, ahk_class IEFrame
vOutput := "[ORDER 1: CONTROL ORDER]`r`n"

;get url + title, control order
Loop
{
	ControlGet, hCtl, Hwnd,, % "Internet Explorer_Server" A_Index, % "ahk_id " hWnd
	if (hCtl = "")
		break

	;Frame Tab\TabWindowClass\Shell DocObject View\Internet Explorer_Server
	ControlGet, hCtl, Hwnd,, % "Internet Explorer_Server" A_Index, % "ahk_id " hWnd
	hCtl2 := DllCall("user32\GetParent", "Ptr",hCtl, "Ptr")
	, hCtl3 := DllCall("user32\GetParent", "Ptr",hCtl2, "Ptr")
	ControlGetText, vTitle,, % "ahk_id " hCtl3
	vIsV1 := !!SubStr(1, 0)
	if (SubStr(vTitle, vIsV1-20) = " - Internet Explorer")
		vTitle := SubStr(vTitle, 1, -20)
	vOutput .= vTitle "`r`n"

	;WBGet: https://autohotkey.com/board/topic/47052-basic-webpage-controls-with-javascript-com-tutorial/
	oWB := WBGet("ahk_id " hWnd, A_Index)
	;vOutput .= oWB.LocationName "`r`n"
	;vOutput .= oWB.LocationUrl "`r`n"
	vOutput .= oWB.document.title "`r`n"
	vOutput .= oWB.document.url "`r`n"
	oWB := ""
	vOutput .= "`r`n"
}

;===============

vOutput .= "[ORDER 2: OBJECT ORDER]`r`n"

;get url + title, object order
for oWB in ComObjCreate("Shell.Application").Windows
{
	if !(hWnd = oWB.Hwnd)
		continue
	;vOutput .= oWB.LocationName "`r`n"
	;vOutput .= oWB.LocationUrl "`r`n"
	vOutput .= oWB.document.title "`r`n"
	vOutput .= oWB.document.url "`r`n"
	vOutput .= "`r`n"
}
oWB := ""

vIsV1 := !!SubStr(1, 0)
if (SubStr(vOutput, vIsV1-4) = "`r`n`r`n")
	vOutput := SubStr(vOutput, 1, -2)

Clipboard := vOutput
MsgBox, % "done"
return

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 01 Jan 2017, 11:14
by phaleth
The following code will say "they match" only when a single IE window with single IE tab is opened. On the comparison it only deals with the last out entries from each loop and so this code needs more work. Right now it's just good enough to give you the idea of what to compare to get pretty decent uniqueness.

Code: Select all

WinGet, Hwnd, ID, ahk_class IEFrame
vOutput := "[ORDER 1: CONTROL ORDER]`r`n"

;get url + title, control order
Loop
{
	ControlGet, hCtl, Hwnd, , Internet Explorer_Server%A_Index%, ahk_id %Hwnd%
	if (hCtl = "")
		break

	;Frame Tab\TabWindowClass\Shell DocObject View\Internet Explorer_Server
	ControlGet, hCtl, Hwnd, , Internet Explorer_Server%A_Index%, ahk_id %Hwnd%
	hCtl2 := DllCall("GetParent", UInt, hCtl), hCtl3 := DllCall("GetParent", UInt, hCtl2)
	ControlGetText, vTitle, , ahk_id %hCtl3%
	
	if (SubStr(vTitle, 1-20) = " - Internet Explorer")
		StringTrimRight, vTitle, vTitle, 20
	
	if (oWB.LocationUrl ~= "blank")
		continue
	
	vOutput .= vTitle "`r`n"

	;WBGet: https://autohotkey.com/board/topic/47052-basic-webpage-controls-with-javascript-com-tutorial/
	oWB := WBGet("ahk_id " Hwnd, A_Index)
	vOutput .= oWB.LocationName "`r`n"
	vOutput .= oWB.LocationUrl "`r`n"
	vOutput .= "`r`n"
	controlOuterHTML := oWB.document.documentElement.outerHTML
	oWB := ""
}

;===============

vOutput .= "[ORDER 2: OBJECT ORDER]`r`n"

;get url + title, object order
for oWB in ComObjCreate("Shell.Application").Windows
{
	objectHwnd := ""
	try {
		objectHwnd := oWB.Hwnd
	}
	
	if (Hwnd != objectHwnd)
		continue
	
	vOutput .= oWB.LocationName "`r`n"
	vOutput .= oWB.LocationUrl "`r`n"
	vOutput .= "`r`n"
	objectOuterHTML := oWB.document.documentElement.outerHTML
}
oWB := ""

if (SubStr(vOutput, 1-4) = "`r`n`r`n")
	StringTrimRight, vOutput, vOutput, 2

match := % (controlOuterHTML = objectOuterHTML) ? "they match" : "they don't match"

MsgBox,
(LTrim
	%vOutput%
	
	--------------------
	
	%match%
)
Return

WBGet(WinTitle="ahk_class IEFrame", Svr#=1) {               ;// based on ComObjQuery docs
   static msg := DllCall("RegisterWindowMessage", "str", "WM_HTML_GETOBJECT")
        , IID := "{0002DF05-0000-0000-C000-000000000046}"   ;// IID_IWebBrowserApp
;//     , IID := "{332C4427-26CB-11D0-B483-00C04FD90119}"   ;// IID_IHTMLWindow2
   SendMessage msg, 0, 0, Internet Explorer_Server%Svr#%, %WinTitle%
   if (ErrorLevel != "FAIL") {
      lResult:=ErrorLevel, VarSetCapacity(GUID,16,0)
      if DllCall("ole32\CLSIDFromString", "wstr","{332C4425-26CB-11D0-B483-00C04FD90119}", "ptr",&GUID) >= 0 {
         DllCall("oleacc\ObjectFromLresult", "ptr",lResult, "ptr",&GUID, "ptr",0, "ptr*",pdoc)
         return ComObj(9,ComObjQuery(pdoc,IID,IID),1), ObjRelease(pdoc)
      }
   }
} ; by jethrow

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 01 Jan 2017, 12:35
by jeeswg
Thank you phaleth, using the outerHTML is an interesting idea, out of interest does it slow down the script much, or use much CPU?

I have various methods for matching up the window lists, and I'm quite happy with what I have. My main concern is, if anybody happens to know if there is some sort of unique ID that can be retrieved on both loops (or happens to know that there isn't), and possibly any other details to retrieve from the objects that may help establish uniqueness/semi-uniqueness, possibly a date created/opened.

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 01 Jan 2017, 16:13
by phaleth
By milliseconds maybe. Use A_TickCount variable to find out the difference in runtime.

I don't think digging any deeper into this is worth the while, but maybe you will hit onto something else if you try.
https://msdn.microsoft.com/cs-cz/librar ... 85%29.aspx

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 01 Jan 2017, 16:39
by jeeswg
Great link. By milliseconds? Retrieve milliseconds from where? Is it possible to retrieve the times of events in that list?

I've found a few times, sneaky useful things on IE documents, from things not easily findable in MSDN, like the % zoom.

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 02 Jan 2017, 11:13
by phaleth
Yeah, it's possible to retrieve the run time.

Code: Select all

startTime := A_TickCount
Sleep, 1000
MsgBox, 0, Run time, % endTime := A_TickCount - startTime

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 02 Jan 2017, 12:05
by jeeswg
I meant if there were some method like GetProcessTimes (which retrieves when a process was opened), for the document objects, the object loop seems to retrieve them in such an order.

[EDIT: I misread the link, it appears it tells you when events happen, at the time they happen, I don't know if you can subsequently retrieve when they happened.]

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 09 Jan 2017, 00:20
by jeeswg
It appears that oWB.document.title gives the same text as the Frame Tab control, and what's more, it's not truncated.
Thus everything can be achieved via the object loop, with no reference to controls needed.

Thus I can achieve what I want very simply, retrieving webpage titles and urls, as below:

Code: Select all

^q::
WinGet, hWnd, ID, ahk_class IEFrame
vOutput := ""
for oWB in ComObjCreate("Shell.Application").Windows
{
	if (hWnd = oWB.HWND)
		vOutput .= oWB.document.title "`r`n" oWB.LocationUrl "`r`n`r`n"
}
oWB := ""
vOutput := SubStr(vOutput, 1, -2) ;trim right
Clipboard := vOutput
MsgBox, % "done"
return
Note: oWB.document.title is preferable to oWB.LocationName, because it always gives the proper title, e.g. 'Google' v. 'google hello - Google Search', and is not susceptible, to clearing IE's history (in which case LocationName becomes the same as LocationURL).

Note: this code retrieves information for the tabs on one specific IE window.
To get all tabs, either perform the loop for all IEFrame windows, or use the condition: if (oWB.Name = "Internet Explorer").
(The latter would give you information from all the tabs whenever they were created, regardless of which IEFrame window they are in.)

I would still be interested if either the win objects or the document objects have some kind of unique ID number, or retrievable creation date. Clearly the object loop is accessing a list that is in date-created order.

It seems very odd for items not to have some kind of ID, when processes/windows/controls clearly do.

Re: Internet Explorer: get WB hWnds via object loop (+ do win objects have unique object IDs)

Posted: 04 Oct 2017, 19:54
by jeeswg
It looks like there is a way to do this, if you add a custom property to a tab.

Code: Select all

;WBGet function - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=39869

vCount := 0

q:: ;internet explorer tab - set property
vCount++
WinGet, hWnd, ID, A
oWB := WBGet("ahk_id " hWnd)
oWB.PutProperty("MyProperty", vCount)
oWB := ""
return

w:: ;internet explorer tab - get property
WinGet, hWnd, ID, A
oWB := WBGet("ahk_id " hWnd)
MsgBox, % oWB.GetProperty("MyProperty")
oWB := ""
return

e:: ;internet explorer tab - set property
DetectHiddenWindows, On
WinGet, vWinList, List, ahk_class IEFrame
Loop % vWinList
{
	hWnd := vWinList%A_Index%
	WinGet, vCtlList, ControlList, % "ahk_id " hWnd
	Loop Parse, vCtlList, % "`n"
	{
		ControlGet, hCtl, Hwnd,, % "Internet Explorer_Server" A_Index, % "ahk_id " hWnd
		if !hCtl
			break
		oWB := WBGet("ahk_id " hWnd, A_Index)
		oWB.PutProperty("MyProperty", hCtl)
		oWB := ""
	}
}
return