Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Basic Webpage Controls with JavaScript / COM - Tutorial


  • Please log in to reply
335 replies to this topic
Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
I suggest to refer to Internet Explorer Architecture.
And, yes, tank's code will work. If already obtained pwb, however, it's a bit pointless to go through that step, IMO. My suggestion was to demonstrate the means to obtain directly Window or Document objects and start from them. Obtaining Document object has been used many times in the forum, but obtaining Window object has been used little.
<!-- m -->http://www.autohotke... ... 6&start=28<!-- m -->

pacc := COM_AccessibleObjectFromWindow(hIESrv) ; window handle of Internet Explorer_Server
pwin := COM_QueryService(pacc, "{332C4427-26CB-11D0-B483-00C04FD90119}")
COM_Release(pacc)


tank
  • Administrators
  • 4345 posts
  • AutoHotkey Foundation
  • Last active: May 02 2019 09:16 PM
  • Joined: 21 Dec 2007
@Sean the reason I avoid this method is because it requires the tab to be the active one. Obtaining the pwb avoids this and allows multiple tabs within a single browser to be automated without regard to being selected or not

However you are correct if it doesn’t matter if the tab is the active one yours is more direct to the point.

I might also suggest that in my own production environment with 300’ish users that this (MSAA) method can fail (not often and not consistently). I have yet to identify why. :oops: This is the other reason checking the shellWindows collection is my preferred choice
:wink:

I will point out i hadnt seen AccessibleObjectFromPoint demonstrated any where else and my knowledge of MSAA is very weak

The iweb functions are the effort of 2 years attempting to get the best stability with ahk to automate on clients spread across 3 physical sites wehre the end user can also mess things up in browser settings. They are not full proof by any means but i Saw a 60+ percent drop in automation failure reports when I moved accessability out of the equation
Never lose.
WIN or LEARN.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

@Sean the reason I avoid this method is because it requires the tab to be the active one.

This method doesn't require the tab to be the active one. It just needs to obtain the correct window handle of the corresponding Internet Explorer_Server which I agree may sometimes be hard to identify in practice though.

I might also suggest that in my own production environment with 300’ish users that this (MSAA) method can fail (not often and not consistently). I have yet to identify why. :oops: This is the other reason checking the shellWindows collection is my preferred choice
:wink:

Yes, MSAA is too old, I also found it awkward often. Anyway, I didn't suggest to replace already established methods with this, just demonstration that it's possible when mentioning conceptually Window object is the top level one in DOM.

I will point out i hadnt seen AccessibleObjectFromPoint demonstrated any where else

IIRC, I used it many times in the posted scripts. Anyway it may be useful to obtain directly IHTMLElement in DOM. As a matter of fact, my first plan for IE HTML Element Spy was utilizing it, however, it didn't provide the fine granularity needed, so I took a different/current route.

The iweb functions are the effort of 2 years attempting to get the best stability with ahk to automate on clients spread across 3 physical sites wehre the end user can also mess things up in browser settings. They are not full proof by any means but i Saw a 60+ percent drop in automation failure reports when I moved accessability out of the equation

Oh I see. Thanks for valuable info that I can't possibly gain myself.

tank
  • Administrators
  • 4345 posts
  • AutoHotkey Foundation
  • Last active: May 02 2019 09:16 PM
  • Joined: 21 Dec 2007

Yes, MSAA is too old, I also found it awkward often. Anyway, I didn't suggest to replace already established methods with this, just demonstration that it's possible when mentioning conceptually Window object is the top level one in DOM.

I am glad you did i forgot about it since I abandoned ahklerners injectJS function

This method doesn't require the tab to be the active one. It just needs to obtain the correct window handle of the corresponding Internet Explorer_Server which I agree may sometimes be hard to identify in practice though.

Can you think of any that don’t require it to be the top most tab?

Oh I see.

FYI though I wrote them I have yet to widely use iWeb_clickHref iWeb_clickValue Mostly I use the iWeb_getwin and the iWeb_setDomObj and iWeb_getDomObj functions. so the others aren’t well tested

Anyway it may be useful to obtain directly IHTMLElement in DOM.

At any rate I see how its useful to retrieve info with this but am unsure how one would go about using it in a larger script that uses multiple pages and or sites.
The reason for iWeb_DomWin is because there are in limited cases sites where there are script permissions and doing this bypass that security restriction I believe its related to the security zone setting allow websites to use restricted protocols for active content But I cant prove it

It seems we mainly agree on the point of using the pwb might be the most effective way to go even if it isn’t the only way
Another way to grab an element would be to try and find unique text and possibly request an offset of elements using the find method. Since this method traverses frames if unique text is avail it may be the easiest for users to use with minimal understanding of DOM and no understanding of JavaScript

There have been members of our team new to scripting that rely on this for the automation jobs
element:=IE_Find("Some unique text") 
Com_invoke(element,"click") 

IE_Find(needle,win="A",property="",offset=0) 
{ 
   If   (win=="A") 
      WinGetTitle,win,%win% ahk_class IEFrame 
   StringSplit,wins,win,- 
   _autotrim:=A_AutoTrim 
   AutoTrim,On    
   wins1=%wins1% 
   AutoTrim,%_autotrim% 
   If   psh   :=   COM_CreateObject("Shell.Application") { 
      If   psw   :=   COM_Invoke(psh,   "Windows") { 
         Loop, %   COM_Invoke(psw,   "Count") 
            If   pwb   :=   (InStr(COM_Invoke(psw,"Item[" A_Index-1 "].LocationName"),wins1)   && InStr(COM_Invoke(psw,"Item[" A_Index-1 "].FullName"), "iexplore.exe")) ? COM_Invoke(psw,"Item", A_Index-1) : 
               Break 
         COM_Release(psw) 
      } 
      COM_Release(psh) 
   } 
   If   !pwb 
      Return 
   If   !pWin:=COM_QueryService(pwb,   "{332C4427-26CB-11D0-B483-00C04FD90119}",   "{332C4427-26CB-11D0-B483-00C04FD90119}") 
   { 
      COM_Release(pwb) 
      Return 
   }    
   If   oRange:=COM_Invoke(pWin,"document.body.createTextRange") 
   { 
      COM_Invoke(oRange,"findText",needle) 
      _res:=property ? COM_Invoke(pWin,"Document.all.item[" COM_Invoke(oRange,"parentElement.sourceIndex")+offset "]." property) :  COM_Invoke(pWin,"Document.all.item", COM_Invoke(oRange,"parentElement.sourceIndex")+offset) 
      COM_Release(oRange) 
   }    
   COM_Release(pWin) 
   COM_Release(pwb) 
   Return   _res 
}

Never lose.
WIN or LEARN.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Can you think of any that don’t require it to be the top most tab?

It can be used sort of as the replacement of ShellWindows whose cons is that it requires Explorer shell. As I switched to 64bit Win7 a few days ago, I became curious and tested it also on 64bit IE8 on 64bit Win7, it still worked flawlessly.
DetectHiddenWindows, On
WinGet, ControlList, ControlList, ahk_class IEFrame
RegExMatch(ControlList, "(?<=Internet Explorer_Server)\d+(?!.*Internet Explorer_Server)", nCount)
Loop, %	nCount
{
	ControlGet, hWnd, hWnd,, Internet Explorer_Server%A_Index%, ahk_class IEFrame
	window := COM_Enwrap(COM_QueryService(COM_AccessibleObjectFromWindow(hWnd), "{332C4427-26CB-11D0-B483-00C04FD90119}"))
;	oWeb := COM_Enwrap(COM_QueryService(window, "{0002DF05-0000-0000-C000-000000000046}"))
	MsgBox % window.document.url
}

The reason for iWeb_DomWin is because there are in limited cases sites where there are script permissions and doing this bypass that security restriction I believe its related to the security zone setting allow websites to use restricted protocols for active content But I cant prove it

OK, I see your point. I also had to do the similar for cross domains across frames in IE HTML Element Spy script.

tank
  • Administrators
  • 4345 posts
  • AutoHotkey Foundation
  • Last active: May 02 2019 09:16 PM
  • Joined: 21 Dec 2007
Very interesting Sean i learned something from you that i previously hadnt known (as usual) For the sake of learing im gonna play with the code you have as it relates to IE 7(i hope) as well
Never lose.
WIN or LEARN.

tank
  • Administrators
  • 4345 posts
  • AutoHotkey Foundation
  • Last active: May 02 2019 09:16 PM
  • Joined: 21 Dec 2007
Jethrow given some recent questions since I have used these methods on all of the OS's and browsers including Vista and 7

COM DOM and HTML are Industry Standards They are supported in IE versions 4.0 and up and I have tested AHK and Sean's COM library on Windows 2k XP Vista and 7 as well as each of the versions of IE(I had 4.0 on a Bart PE implementation for a while) Obviously tabs were only introduced as of IE7. If COM does not work for your web automation needs it is not specifically related to the browser version or OS. It could perhaps be due to BHO's common in my workplace as a problem or specific security settings in a corp. environment.
BHO’s by far make up the majority of the items that cause COM to fail to automate a browser when the code is correct. Usually the interference is in the form of creating hidden framesets. The most common I encounter in a secure environment is OWS which comes with office and is a BHO for SharePoint. Some Antivirus install BHO's as well but as of yet I have not seen those create such a problem
Never lose.
WIN or LEARN.

tank
  • Administrators
  • 4345 posts
  • AutoHotkey Foundation
  • Last active: May 02 2019 09:16 PM
  • Joined: 21 Dec 2007
the iWebBrowser2 Learner has been updated to give example iWeb function calls with frame references
the iWeb functions have been updated to allow frame references and bypass cross domain restrictions I made this update only because Sinkfaze put so much effort himself into trying

Posted Image

I also noticed a bug and corrected it with identifying the page title
Never lose.
WIN or LEARN.

UncleScrooge
  • Members
  • 75 posts
  • Last active: Sep 22 2010 09:12 PM
  • Joined: 14 Apr 2009
Hi all

and compliments for the job done, even though I often understand less tan 30% of it... but it had been very stimulating: spent a couple of evenings surfing through HTML DOM learning pages at W3Schools.

wouldnt bother anybody with stupid questions but I'm stuck in this:

I got the Sean function IE_DocumentComplete working flawlessly to get my particular URL open
sUrl :=   "http://127.0.0.1:81/Upload.html"
COM_Init()
pweb :=   COM_CreateObject("InternetExplorer.Application")
sink :=   COM_ConnectObject(pweb, "IE_")
bComplete := False
COM_Invoke(pweb, "Navigate", sUrl)
While !bComplete
      Sleep, 500
MsgBox, 36, , Do you want it visible?
IfMsgBox, Yes
   COM_Invoke(pweb, "Visible", True)
I work in industrial automation - process control and our controllers' network exchange informations thru a MODBUS and interface with the outer world via a WEB server (each controller owns one).
Here the address h t t p :// 127.0.0.1:81/ refers to a controller simulator running on my PC. The "Upload.html" document is the user interface to handle files back and forth:

Posted Image

now, before I start doing anything at all, as Sean'function tells me that the url loading procedures has terminated, I want to make it sure the document is showing me the expected things. This will also tell me that I'm really communicating to a controller (each controller owns a document such as http:\\xxx.xxx.xxx.xxx\Upload.html, if the document doesn't load up correctly we might have a network problem -cabling or else).
So I thought the easiest way was to check if the browser can return the document header contents (<h1>) that I know to be:
File management
(see fig)

As shown earlier in this post I imagined it would had been easy to ask the IE object to retrieve it, and in fact when I try to inject javascript through the URL address control like this:
javascript:alert(document.getElementsByTagName("h1")[0].innerHTML)
I got the message box beeping me: "File management"
wow: I figured it was a piece o'cake to push it in my AHK as suggested like this:
MsgBox % COM_Invoke(pwb, "document.getElementsByTagName[color=red]([/color][h1][color=red])[/color].item[0].innerHTML")
no such luck... all I got is a barrage of messages from COM telling that all the components (functions) of the COM_Invoke param (document getElementsByTagName item and innerHTML cannot be found:

Posted Image

what am I doing wrong?
thnx for any answer

Ps: I've tried all the variants:
[color=red]document.getElementsByTagName[h1].item[0].innerHTML[/color]
[color=brown]document.getElementsByTagName[h1][0].innerHTML[/color]
with no better results: all I got is a higher or lower number of error messages from COM

sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008
Try this:

COM_Invoke(pwb,"document.all[h1].innerHTML")


tank
  • Administrators
  • 4345 posts
  • AutoHotkey Foundation
  • Last active: May 02 2019 09:16 PM
  • Joined: 21 Dec 2007

Try this:

COM_Invoke(pwb,"document.all[h1].innerHTML")

um h1 is a tag not an id so that shouldnt work
COM_Invoke(pwb,"document.all.tags[h1].item[0].innerHTML")
COM_Invoke(pwb,"document.getElementsByTagName[h1].item[0].innerHTML")
should simlarly work but havent tested it
Never lose.
WIN or LEARN.

sinkfaze
  • Moderators
  • 6367 posts
  • Last active: Nov 30 2018 08:50 PM
  • Joined: 18 Mar 2008

um h1 is a tag not an id so that shouldnt work


Ugh...and I read that before I answered, too...sorry.

jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009

all I got is a barrage of messages from COM telling that all the components (functions) of the COM_Invoke param (document getElementsByTagName item and innerHTML cannot be found
... Ps: I've tried all the variants:

[color=red]document.getElementsByTagName[h1].item[0].innerHTML[/color]
[color=brown]document.getElementsByTagName[h1][0].innerHTML[/color]
with no better results: all I got is a higher or lower number of error messages from COM

If you are getting a COM Error when trying to invoke the document object, the document isn't accessible (perhaps not loaded).

sUrl :=   "http://127.0.0.1:81/Upload.html" 
COM_Init() 
pweb :=   COM_CreateObject("InternetExplorer.Application") 
sink :=   COM_ConnectObject(pweb, "IE_") 
bComplete := False 
COM_Invoke(pweb, "Navigate", sUrl) 
While !bComplete 
      Sleep, 500 
MsgBox, 36, , Do you want it visible? 
IfMsgBox, Yes 
   COM_Invoke(pweb, "Visible", True)

If this is all your code, you aren't using Sean's DocumentComplete method correctly. Please post all your code (or at least all your code that is relevant - ie. How is your While-loop breaking?).

UncleScrooge
  • Members
  • 75 posts
  • Last active: Sep 22 2010 09:12 PM
  • Joined: 14 Apr 2009

...........
If this is all your code, you aren't using Sean's method correctly. Please post all your code (or at least all your code that is relevant - ie. How is your While-loop breaking?).


no, obviuosly not. sorry my mistake, here is the complete thing.
#Persistent
sUrl :=   "http://127.0.0.1:81/Upload.html"
COM_Init()
pweb :=   COM_CreateObject("InternetExplorer.Application")
sink :=   COM_ConnectObject(pweb, "IE_")
bComplete := False
COM_Invoke(pweb, "Navigate", sUrl)
While !bComplete
      Sleep, 500
MsgBox, 36, , Do you want it visible?
IfMsgBox, Yes
   COM_Invoke(pweb, "Visible", True)

;MsgBox % COM_Invoke(pwb, "document.getElementsByTagName[h1][0].innerHTML")
;MsgBox % COM_Invoke(pwb,"document.all.tags[h1].item[0].innerHTML")
MsgBox % COM_Invoke(pwb,"document.getElementsByTagName[h1].item[0].innerHTML")
MsgBox, 36, , Do you want to close it?
IfMsgBox, Yes
   [color=red]COM_Invoke(pweb, "Quit")[/color]

COM_DisconnectObject(sink)
COM_Release(pweb)
COM_Term()
#q::ExitApp     ;WIN key + q to terminate this script
Return

OnComplete:
bComplete := True
Return

IE_DocumentComplete(prms, sink)
{
   If   NumGet(NumGet(prms+0)+24) = NumGet(sink+12)
      SetTimer, OnComplete, -10
/* more rigorous way
   COM_Release(punk1:=COM_QueryInterface(NumGet(NumGet(prms+0)+24),0))
   COM_Release(punk2:=COM_QueryInterface(NumGet(sink+12),0))
   If   (punk1 = punk2)
      SetTimer, OnComplete, -10
*/
}

IEReady(hIESvr = 0)
{
   If Not   hIESvr
   {
      Loop,   50
      {
         ControlGet, hIESvr, hWnd, , Internet Explorer_Server1, A ; ahk_class IEFrame
         If   hIESvr
            Break
         Else   Sleep 100
      }
      If Not   hIESvr
         Return   """Internet Explorer_Server"" Not Found."
   }
   Else
   {
      WinGetClass, sClass, ahk_id %hIESvr%
      If Not   sClass == "Internet Explorer_Server"
         Return   "The specified control is not ""Internet Explorer_Server""."
   }

   COM_Init()
   If   DllCall("SendMessageTimeout", "Uint", hIESvr, "Uint", DllCall("RegisterWindowMessage", "str", "WM_HTML_GETOBJECT"), "Uint", 0, "Uint", 0, "Uint", 2, "Uint", 1000, "UintP", lResult)
   &&   DllCall("oleacc\ObjectFromLresult", "Uint", lResult, "Uint", COM_GUID4String(IID_IHTMLDocument2,"{332C4425-26CB-11D0-B483-00C04FD90119}"), "int", 0, "UintP", pdoc)=0
   &&   pdoc && pweb:=COM_QueryService(pdoc,IID_IWebBrowserApp:="{0002DF05-0000-0000-C000-000000000046}")
   {
      While,   COM_Invoke(pweb, "ReadyState") <> 4
      Sleep,   500
      While,   COM_Invoke(pweb, "document.readyState") <> "complete"
      Sleep,   500
      COM_Release(pweb)
   }
   COM_Release(pdoc)
   COM_Term()
   Return   pweb ? "DONE!" : False
}

that line in red is actually quitting the browser session so I guess the pwb object is open and responding correctly... not when I try to access the document though. and as you can see by the commented lines not even tank's suggestions work (btw I love that pissed off Yoda. just great)
the code on top (autoexec section) is of course just a bunch of lines to test functionalities so to manipulate IE (the finished code shouldn't even make IE object visible).

I've tried these and they work wonders (of-bloody-course):
;js_Snippet = javascript`:document`.getElementsByTagName`(`"h1`"`)`[0`]`.innerHTML`;
js_Snippet = javascript`:alert`(document`.getElementsByTagName`(`"h1`"`)`[0`]`.innerHTML`)`;
COM_Invoke(pweb, "Navigate", js_Snippet)
which I seem to understand means that, since pweb (the IE object) works fine, I need another handle to reference the document object to start using its exposed functions (properties and methods).
Am I getting it right?...
Intel Centrino @ 2.8GHz
4 GB RAM
WIN XP SP3

jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009
:shock: - Why are you using pweb & pwb? Should be:
MsgBox % COM_Invoke([color=red]pweb[/color],"document.getElementsByTagName[h1].item[0].innerHTML")
On a side-note, this might be a little simpler for your javascript:
js_Snippet := "[color=olive]javascript:alert(document.getElementsByTagName('h1')[0].innerHTML)[/color]"