Jump to content

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

Basic Ahk v1.1+ COM Tutorial for Webpages


  • Please log in to reply
138 replies to this topic

Poll: Did this guide help you? (92 member(s) have cast votes)

Did this guide help you?

  1. Ya! COM ftw rawr! (111 votes [86.72%])

    Percentage of vote: 86.72%

  2. No. I am now dumber for having read this... (17 votes [13.28%])

    Percentage of vote: 13.28%

Vote Guests cannot vote
Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010

This guide is not intended to replace the great work that other (more advanced) members have put into other guides. It is designed to help non-coders (n00bz) grasp the basic concepts of AutoHotkey v1.1+ COM.

This guide will focus on giving the reader a baseline understanding of the use of the Component Object Model(COM) which can be used to manipulate an application with designed with Document Object Model or DOM such as Internet Explorer.

Q: What is COM?
The Component Object Model is a collection of automation objects that allows a user to interface with various methods and properties of an application.

Q: How do I use it in my script?
There is no easy answer to this question. Why? Because there are different commands to every type of COM object. For instance the methods for Internet Explorer are completely different from MS Office.

In this tutorial I will focus on using COM to script simple commands that will be used to automate IE. Before you can do anything with the IE DOM you have to create a handle to the application.

wb := ComObjCreate("InternetExplorer.Application")

"wb" or Web Browser is the common name for your handle to IE. Your script doesn't care what you name your handle you could name it "WhatAboutBob" if you really wanted to.
Now that you have a handle you need to do something with it. Think of it like the steering wheel of a car. Just because you have the wheel in your hands doesn't mean your driving the car.

wb.Visible := True

This is the next line of code you will HAVE to have. Your code can work without it but you won't be able to see anything going on. By default IE starts off in invisible mode. I'll go ahead and point out that ".Visible" is NOT AHK code. It is a built in method in the DOM. You can access it through any COM compatible language (Ruby,C++,ect).

wb.Navigate("Google.com")

At this point we have actually done something. That is open up an instance of IE and navigated to google.com. You could also store an address in a variable.

URL = Google.com
wb.Navigate(URL)

What about accessing an already open web browser? Good Q! There is no simple command for this. Instead you will have to rely on a function (I did not write) to do this for you.

IEGet(Name="")        ;Retrieve pointer to existing IE window/tab
{
    IfEqual, Name,, WinGetTitle, Name, ahk_class IEFrame
        Name := ( Name="New Tab - Windows Internet Explorer" ) ? "about:Tabs"
        : RegExReplace( Name, " - (Windows|Microsoft) Internet Explorer" )
    For wb in ComObjCreate( "Shell.Application" ).Windows
        If ( wb.LocationName = Name ) && InStr( wb.FullName, "iexplore.exe" )
            Return wb
} ;written by Jethrow

If your thinking "Holy Heart Attack Batman!" fear not you don't need to understand how this function works to implement it into your script. What this function does is looks through the windows on your computer for IE and then for the tab name of the page you sent it OR default to the last activated tab.

wb := IEGet() ;Last active window/tab
;OR
wb := IEGet("Google") ;Tab name you define can also be a variable

Now that we can open IE and navigate to pages we can manipulate things now right? Wrong!

notice the loading bar at the bottom of the page or wherever your browser has it? That is your first adversary. You are going to have to tell your script to wait till your page is done loading.

IELoad(wb) ;You must send the function your Handle for it to work

This is a function that is based of the iWeb function Tank built. If you don't care to understand how it works then just know that you must have this (or another similer) function on any script that deals with IE.

IELoad(wb)    ;You need to send the IE handle to the function unless you define it as global.
{
    If !wb    ;If wb is not a valid pointer then quit
        Return False
    Loop    ;Otherwise sleep for .1 seconds untill the page starts loading
        Sleep,100
    Until (wb.busy)
    Loop    ;Once it starts loading wait until completes
        Sleep,100
    Until (!wb.busy)
    Loop    ;optional check to wait for the page to completely load
        Sleep,100
    Until (wb.Document.Readystate = "Complete")
Return True
}

Now that you can access an existing page or launch a new one we can actually do something useful. Let's fill the search field. We are going to use the "Name" element to do this.

wb.Document.All.q.Value := "site:autohotkey.com tutorial"

You can also set a form to a variable:

Website = site:autohotkey.com tutorial
wb.Document.All.q.Value := Website

You've just manipulated your first DOM object! Let's go over what we did just now.
This line of code probably doesn't mean anything to you at the moment so let's break it down to something that we all can understand.

Think of "wb" in our code as the top floor or roof of the building you happen to be on. You however want to do something in the basement since it's too wet and rainy to do anything on the roof. However to get to the basement you must travel through all the other floors between.

"Document" refers the the room directly below you in this example. The Document object holds EVERYTHING on any web page you see. But you must specify where your looking on the Document.

"All" specifies all the items including forms, tables, buttons, ect that you can control.

What is "q"??? Q happens to be the name of the search form. To figure out what the name of anything on a web page you should use a tool such as "Ahk Web Recorder." It's a handy tool that shows the information of any IE window you hover the mouse cursor over.

Now that we have the address of the element we want to work with "Value" is a method that alows you to change the content within.

Everything to the right side of the ":=" is what you want placed within the value you have selected. In this case we are going to search through the website "autohotkey.com" using the "site:" tool that is built into the Google search engine. That way Google will only search through a single website instead of browsing the entire WWW.

Now let's click the "Search" button.

wb.Document.All.btnG.Click()

Here we use the "Click()" method. "Value" gives us access to data held within a form whereas "Click" activates a button.

So far we have covered: how to get a(n) existing or created IE handle, sleep till a page has loaded, set a form, and click a button. Here's what your code SHOULD look like. Minus the functions:

wb := IEGet("Google") ;IE instance already open and tab named google exists
wb.Document.All.q.Value := "site:autohotkey.com tutorial"
wb.Document.All.btnG.Click()
IELoad(wb)
;or
wb := ComObjCreate("InternetExplorer.Application") ;create a IE instance
wb.Visible := True
wb.Navigate("Google.com")
IELoad(wb)
wb.Document.All.q.Value := "site:autohotkey.com tutorial"
wb.Document.All.btnG.Click()
IELoad(wb)

This guide is a work in progress I will continue to add more material as soon as I get more time and after editing the work I have done so far.
Please keep posts on this page to either requests for explanations or pointing out errors I have made here.
Thanks for reading.

Other Resources:

For a more advanced tutorial on this subject please see jethrow's tutorial at: Basic Webpage Controls with JavaScript / COM This is what I first started out with. grin.png

 

 

EDITS by jethrow:

- changed Pwb to wb

- changed _L to v1.1+

 



Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010
Updated: 11/10/11

After a long break I've finally decided on what I believe to be a valuable skill when working with IE. Looping through web pages.
The advantages of using this method include dealing with dynamicly created elements, checking if a specific element exists, and using elements without having the element's name.
Most of the data used to make this guide was either provided by members of the community or the MSDN.

While working through this tutorial understand that it's only as complicated as you make it. Because the concepts of this are a little harder to wrap one's mind around I decided to use the forums for testing instead of good ole Google.

For starters let's get a handle to an open IE window:
For IE in ComObjCreate("Shell.Application").Windows ; for each open window
If InStr(IE.FullName, "iexplore.exe") ; check if it's an ie window
break ; keep that window's handle
; this assumes an ie window is available. it won't work if not
This snippet is based on sean's IEGet() Function.
If you'd like to see more on the Shell Application and the method's available check out this post by jethrow.
The reason I chose to use this method is so that you don't have to constantly open up new browsers while testing. ;)

Moving on. Now that we have established our handle we can access the elements inside.
IE.Navigate("http://www.autohotkey.com/forum/") ; go to the ahk forum page

While IE.Busy ; simple wait for page to load
Sleep, 100 ; 1/10 second
For testing purposes we want to make sure that we always start in the same place.

Now this page doesn't have many forms or buttons like we had in the previous section. Instead were going to access the Links (or Hyperlinks) collection.
Links := IE.Document.Links ; collection of hyperlinks on the page
Now that we have all the links we can loop through them. First let's see what the collection contains.
Loop % Links.Length ; check each link
If ((Link := Links[A_Index-1].InnerText) != "") ; if the link is not blank
Msg .= A_Index-1 . " " . Link . "`n" ; add item to the list
MsgBox % Msg ; display the list
What weve done here is use the .Length property to see how many items are in the collection. Then we looped through it and stored each links text using the .InnerText method that aren't empty into our variable so we can see what's all in the collection.The number to the left of each item is the index within the collection. From this we can determine which of the links is blank because it would have been skipped.

Now that we can retrieve the links from a webpage let's do something with them. Were going to search the page for your favorite tutorial writer's link and go to his profile.
ComObjError(false)
Links := IE.Document.Links
Loop % Links.Length
	If (Links[A_Index-1].InnerText = "Mickers") { ; replace my name with whatever text you'd rather search for
		;or If InStr(Links[A_Index-1].InnerText, "Mickers") ; 
		Links[A_Index-1].Click()
		break
	}
Ok now were getting somewhere. Weve looped through a page and chose a link based on it's display text.

Now let's search for a specific post within General Chat.
Now we could use the same method of parsing through the links untill we find the one we want but sometimes just knowing the link's innertext isn't good enough. For example say we want to select a link only when a specific username is present within the row of text.
To do this we are going to look at a new property of the Document.All object. The Tags() property returns a collection of elements specified by what is between the "(--tag--)". Now what's a tag you say? It's an HTML term for a specific body part of any web page. We are going to use 2 of these: Tables and Rows. Test this snippet of code to see what I mean:
MsgBox %  IE.Document.All.Tags("table").Length
Place it after the first wait after you navigate to the forum from the first part of this section.
The MsgBox should have given you a 9. This is the total number of tables on that specific page. Now tables by themselves arn't going to be of much use to us in this tutorial so let's take it one step further. :wink:

Were now going to access the Rows property of the Tables collection.
MsgBox %  IE.Document.All.Tags("table").Rows.Length
Uh oh what happend? It says Rows is an unknown name what gives? Sorry I led you astray but you have just learned something very important that will save you frustration in the future. You can't access a property of a collection without specifying a single item of that collection.
Let me explain say you have a collection of say.... Girls. Now you want a specific girl from the collection of all the girls in your school. However to choose one you want to know things like what hair color they have, if their a cheerleader ect. But in this example you can't simply say give me a girl that's a cheerleader and a redhead. Why because you have a COLLECTION and you can't just say give me one that fits this profile GO. Let me show you via some pseudo code:
Girls := ComObjCreate("Girls.Everywhere") ; here's our handle to the object

CollegeGirls := Girls.Document.All.Tags("College") ; this is our tables example
Loop % (Cheerleaders := CollegeGirls[Cheerleaders].Length) ; cheerleaders is our row example
	If ((DreamGirl := Cheerleaders.Girl[A_Index]) = "Redhead") ; here the girl property let's us look at each girl individually
		break ; break the loop once we find one
MsgBox % DreamGirl.Name ; use the name property to see the name of your future girlfriend
;if your female replace girls with boys and cheerleaders for football player
To save any uneccesary confusion this is a fake object and none of these properties apply to IE.
Now we know that to access the text of a table we have to specify which table then look at each row one at a time like so:
Rows := IE.Document.All.Tags("table")[4].Rows ; table 4 holds all the post information
	Loop % Rows.Length ; for each post
		Msg .= Rows[A_Index].InnerText . "`n" ; save the post data
MsgBox % Msg
Now we can see the text inside each row. At this point you can use simple Regex or InStr functions to look for specific data.

Let's search for a specific post:
Rows := IE.Document.All.Tags("table")[4].Rows
	Loop % Rows.Length
		If InStr(Rows[(Row := A_Index)].InnerText, "What's on your mind?") and InStr(Rows[A_Index].InnerText, "tomoe_uehara")
			break
MsgBox % Rows[Row].InnerText
I chose to search for this particular post because it's the most popular one on the site and is always on the first page.
Now that weve proven that we can locate a post based on the name and author let's try to navigate to that post. I haven't had any luck using the Click() method to follow the hyperlink so instead were going to use a work around:
URL := IE.LocationURL ; grab the current url
Rows := IE.Document.All.Tags("table")[4].Rows ; table 4 holds all the post information

Loop % Rows.Length
	If InStr(Rows[A_Index].InnerText, "What's on your mind?") and InStr(Rows[A_Index].InnerText, "tomoe_uehara") { ; if post title and author match
		HTML := Rows[A_Index].Cells[1].InnerHTML ; pull the html off the page
		Needle := "viewtopic.php?t=" ; string to search for
		StringGetPos, Pos, HTML, %Needle% ; get starting position of search string
		Pos += 17 ; search 17 characters to the right
		StringMid, Post, HTML, Pos, 5 ; pull the unique post number out of the html
		StringTrimRight, URL, URL, 13 ; cut off forum.php?f=
		URL .= "topic.php?t=" . Post ; set to topic + unique post number
		IE.Navigate(URL) ; navigate to the post
		break
	}
Here is what the actual InnerHTML looks like:

<SPAN class=topictitle><A class=topictitle href="viewtopic.php?t=57174">« What's on your mind? »</A></SPAN><SPAN class=gensmall><BR>[ <IMG title="Goto page" alt="Goto page" src="templates/subSilver/images/icon_minipost.gif">Goto page: <A href="viewtopic.php?t=57174&start=0">1</A> ... <A href="viewtopic.php?t=57174&start=885">60</A>, <A href="viewtopic.php?t=57174&start=900">61</A>, <A href="viewtopic.php?t=57174&start=915">62</A> ] </SPAN>

You can see that the post's unique number is in there multiple times. I spent several hours trying to get the Click() method to work for this example but sometimes it's just easier to stick with what you know. :wink:

For the last part of this tutorial we are going to factor in one last thing. What if the post were searching for is not on the first page? I have set up a post on a later page of General Chat for us to search for.
ComObjError(false) ; if post is not found it will cause a com error
Found := false ; initialize to false
URL := IE.LocationURL

Loop {
	Rows := IE.Document.All.Tags("table")[4].Rows

	Loop % Rows.Length
		If InStr(Rows[A_Index].InnerText, "IE Test Post") and InStr(Rows[A_Index].InnerText, "Mickers") { ;this is the test post i put together
			HTML := Rows[A_Index].Cells[1].InnerHTML
			Needle := "viewtopic.php?t="
			StringGetPos, Pos, HTML, %Needle%
			Pos += 17
			StringMid, Post, HTML, Pos, 5
			StringTrimRight, URL, URL, 13
			URL .= "topic.php?t=" . Post
			IE.Navigate(URL)
			Found := true ; set to found
			break
		}


	If (Found = false) { ; if post wasn't located
		Links := IE.Document.Links
		Loop % Links.Length
			If (Links[A_Index-1].InnerText = "Next") { ; find the "next" link
				Links[A_Index-1].Click() ; and click it
				break
			}
	}
} until (Found = true) ; until post is located
All weve done here is add a check to see if we located our post. If it's not located in a cycle it will hit the "Next" button and begin searching that page until found.

That concludes the new secion of my guide. I know this was a little more complex then simply filling forms and clicking buttons that you know exist on a we page. However if you want to move onto more complicated IE COM scripts being able to search for items and test if they exist on the page is very useful.
Here is the complete script which should run stand alone:
#SingleInstance force

ComObjError(false)
Found := false

For IE in ComObjCreate("Shell.Application").Windows
   If InStr(IE.FullName, "iexplore.exe")
      break

IE.Navigate("http://www.autohotkey.com/forum/")

While IE.Busy
   Sleep, 100

Links := IE.Document.Links
Loop % Links.Length
   If (Links[(A_Index - 1)].InnerText = "General Chat") {
      Links[(A_Index - 1)].Click()
      break
   }

While IE.Busy
   Sleep, 100

URL := IE.LocationURL

Loop {
   Rows := IE.Document.All.Tags("table")[4].Rows

   Loop % Rows.Length
      If InStr(Rows[(A_Index - 1)].InnerText, "IE Test Post") and InStr(Rows[(A_Index - 1)].InnerText, "Mickers") {
         HTML := Rows[(A_Index - 1)].Cells[1].InnerHTML
         Needle := "viewtopic.php?t="
         StringGetPos, Pos, HTML, %Needle%
         Pos += 17
         StringMid, Post, HTML, Pos, 5
         StringTrimRight, URL, URL, 13
         URL .= "topic.php?t=" . Post
         IE.Navigate(URL)
         Found := true
         break
      }


   If (Found = false) {
      Links := IE.Document.Links
      Loop % Links.Length
         If (Links[(A_Index - 1)].InnerText = "Next") {
            Links[(A_Index - 1)].Click()
            break
         }
   }
} until (Found = true)
ExitApp
Please leave me some feedback or questions you may have. Thanks for reading! :wink:

Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010
Reserved.

a4u
  • Guests
  • Last active:
  • Joined: --
Nice & simple - I like it :D . Couple things though - you mention that this is for non-coders. That is good, but make sure you infomation is still accurate. COM (Component Object Model) is not a set of functions - though you could refer to the ComObj Functions. Also, techniques are ok to show by themselves - but if you are displaying exact code that someone else wrote, you should give credit (and perhaps reference to where you got it). I'm pretty sure the authors won't mind though :wink: . Finally, to say this is a COM tutorial is misleading, imo. Perhaps AHKL COM Tutorial for Webpages, or something of the sort? Anyways, thanks for the resource :)

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

That is good, but make sure you infomation is still accurate.

+1

Basicly COM is a set of functions that allows you to access and manipulate any application with a DOM hierarchy.

What you're referring to is more specifically known as OLE Automation, something which is based on COM. It allows you to access and manipulate any application which supports it, and is not limited to those with a "DOM". Furthermore, "hierarchy" is redundant - the hierarchy of objects is part of the document object model (or at least the most common models).

Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010
I'll do a rewrite here soon and give credit to whom it's do as well as update any Inaccuracies.
Thanks! :wink:

shingrons
  • Members
  • 1 posts
  • Last active: Mar 16 2011 04:57 AM
  • Joined: 16 Mar 2011
i set the commands as F11 for example,
this 1st time is a success, then i close the IE by manually by clicking 'x'

after that i press F11 again
it will wont type in the google page. and when i close the IE, there is an error message.

may i know how to deal with this situation?

I need F11 always works.

thank you

Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010
I know your using the above code but can I see the exact code your using? I'll take a look in the meantime.

Q: Are you creating a new instance of IE each time you press F11 or are you using the IEGet() function?
If you are creating a new instance each time it is run than I'd need to see your code. If you are using IEGet then you need to have an instance already open for it to retrieve a handle to it. IEGet does not create windows. Lemme know. :wink:

nimda
  • Members
  • 4368 posts
  • Last active: Aug 09 2015 02:36 AM
  • Joined: 26 Dec 2010
the loading code can be simplified greatly with an adaption from VBScript code I often use:
While PWB.readystate <> 4

   sleep 50
... I think

Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010
Some websites react oddly to the "readystate' method I've found. "busy" tends to react faster to changes in IE loading. Don't ask me why that is. I use the "readystate" method at the end because "busy" doesn't wait for a page to completely load into your browser 100% of the time. I'd like to know what the real difference between the two methods are. :?

sinkfaze
  • Moderators
  • 6367 posts
  • Last active:
  • Joined: 18 Mar 2008
I don't see any reason why using all three available detection methods shouldn't be encouraged:

Loop
	Sleep, 50
Until	(pwb.readyState=4 && pwb.document.readyState="complete" && !pwb.busy)


nimda
  • Members
  • 4368 posts
  • Last active: Aug 09 2015 02:36 AM
  • Joined: 26 Dec 2010

I don't see any reason why using all three available detection methods shouldn't be encouraged:

Loop
	Sleep, 50
Until	(pwb.readyState=4 && pwb.document.readyState="complete" && !pwb.busy)

While pwb.readyState!=4 || pwb.document.readyState!="complete" || pwb.busy
   Sleep 50
I'm a golfer

Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010

I don't see any reason why using all three available detection methods shouldn't be encouraged:

Loop
	Sleep, 50
Until	(pwb.readyState=4 && pwb.document.readyState="complete" && !pwb.busy)

That's a really interesting snippet there. I'll have to try it out!

nimda
  • Members
  • 4368 posts
  • Last active: Aug 09 2015 02:36 AM
  • Joined: 26 Dec 2010
Mine is 1 line shorter ;)

Mickers
  • Members
  • 1239 posts
  • Last active: Sep 25 2015 03:03 PM
  • Joined: 11 Oct 2010
I do love small compact scripts. :lol:

I'm working on a section for this guide where I search through the list of pages the search pull up and selects this. I think that would be a useful tool for people trying to learn how to automate IE.