Wednesday, July 24, 2013

Automating Internet Explorer (Add IE Controls)

After having built out a shell for the Internet Explorer (IE) object in a class module, we are now ready to start adding some useful functionality. It is important to note that without adding these methods the IE Agent will not be able to perform any functions other than being created as the actual IE object is hidden behind the IE Agent (see my previous post about setting up an IE Agent).

Some key features we will focus on is wrapping up code blocks inside methods that would normally have to be written out every time you would call the basic IE methods on the reference IE object discussed previously. Other methods will focus around navigating the DOM, opening up tabs, and opening up webpages.

Adding Basic Browsing Functionality

Some of the basic functions of IE are 1) opening web pages, 2) opening web pages in new tabs, 3) changing the state of the webpage through clicking on buttons, table cells, etc., and 4) downloading files from a remote server. Each of these functions are performed in a browser via mouseclicks and keyboard input. But during automation you cannot do any of these things using a mouse or a keyboard. Thus we are given a framework from which we can do all of these things programmatically.

Opening Web Pages

The most basic function of the browser is to open a webpage. This is a relatively straightforward event for someone using a browser, and so it is also fairly straightforward for automation. However, there are some issues that a human will be able to overcome intuitively that a script automation must account for, such as waiting for a page to load.

To begin, pull up the IEAgent we developed in the previous post. Since IE needs to load the page completely before any actions can occur, it is necessary to write a wait method. Add this code to your module below:

Sub waitForLoad()
    'pauses the exection of the code until the webpage has loaded
    Do
          If Not ie.Busy And ie.ReadyState = 4 Then
                     application.Wait (Now + TimeValue("00:00:01"))
                     If Not ie.Busy And ie.ReadyState = 4 Then
                             Exit Do
                    End If
       End If
       DoEvents
    Loop
End Sub

The above code enters a loop that checks the state of IE every millisecond (which is an eternity in computer time) and won’t give up control of the program until IE is ready. Note that the IE object was defined during the setup of the IEAgent. This function will be used multiple times within the IEAgent as well as in the parts of a script where the IEAgent is used.

Next we will add a method that will accept an URL Address and then opens the page. This will employ the previous method so that nothing will happen to the page until it is fully loaded.

Sub openpage(url As String)
      'opens the specified url in internet explorer
       ie.navigate url
       waitForLoad
End Sub

The function called on the IE object (navigate) is a predefined function written by Microsoft to aid developers. You don’t have to worry at all about what is going on underneath, just know that it will open any page that you normally would through regular browsing. It then calls the waitForLoad function, thus allowing the page to be in a ready state before any other functions are called.

Advanced Page Loading

Microsoft has provided many ways for someone to write code to automate a process. This is nice as it lends itself to be highly extendable to other areas, but it also means that some things aren’t available in one method that is available in another. The following is a means to open up a webpage using what is called a shell object. For most uses the previous version will suffice, but use of the shell opens up possibilities such as opening multiple windows or tabs at once. Other advantages include using an already open instance of IE by grabbing it’s handle (handles discussed in previous post).

Don’t worry about knowing what a shell is or how it is implemented, but if you want to do some of these things, then adding this code is necessary. My suggestion is to add it as it is not a whole lot of code and will give your code more flexibility to work in the future.

To start, add these two lines above the variables defined at the top of the IEAgent.

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long

The top of your IEAgent should now look something like this:



These are additional APIs used to provide access to the shell objects built in to Windows and to provide a function to stall the execution of the program. These have many parameters that are needed, but the provided example will do the job in opening up additional tabs within an instance of IE.

Sub openPageInTab(url as String, newHandle as long)
                Sleep 1000
                ShellExecute newHandle, "open", url, vbNullString, vbNullString, vbMaximizedFocus
End Sub

This article provides additional information on the ShellExecute function. For our purposes, this will take a URL and open up a tab in the browser with the given handle. To open up a new tab using the IEAgent, you will call this code as follows:

openPageInTab(“www.google.com”  , handle)

The url will lead to google.com, and the handle is the handle defined at the top of the IEAgent as shown in the picture above. Remember that this handle was set upon creation of the IEAgent, so you don’t have to worry about getting the handle in any way. Simply place the handle variable into this method’s parameter and it will open a tab inside the IE browser.

Note that if you want to open up a URL in another instance of a browser (say you wanted to use Firefox or Google Chrome), you can call that browser using this function and open up the tabs in that browser. This is not always a good practice to do as it breaks a concept called encapsulation in some ways, but for our purposes we will leave this option open. Because our task is automating processes, it may be necessary to occasionally open up another browser for whatever reason. Therefore we keep this method here as it doesn’t make much sense to make a separate module to do this. This is an example of when good form gives way to practical design.

Other Basic IE Functions

Part of automating processes is deciding what parts of the process the user needs to see. Microsoft has developed a means to turn off window visibility for IE, allowing you to navigate in IE without the user ever seeing the browsing window. This has advantages as it doesn’t require IE to render anything, so turning off visibility will create time savings.

To turn on and off visibility with the IEAgent, add the following code:

'Set visibility of ie
Public Property Let visible(theValue As Boolean)
          ie.visible = theValue
End Property

To call this code, do the following:

ieAgent.visible = True

You can also set it False. This is a property function, meaning that whatever you can set parameters in VBA using the = and not the .() pattern. Personally I don’t like this method, but I thought that I would add it in here to show a new way of setting variables in VBA. You may come across this pattern in the future. I would avoid this and instead do the following:

'Set visibility of ie
Sub visible(theValue As Boolean)
          ie.visible = theValue
End Property

To call this code, do the following:

Call ieAgent.visible(True)

You can use the first pattern to set any property of a class object and also to run code. You can also get properties (like the handle for the IE object in the IEAgent) by using the following code:

Public Property Get getHandle() As Long
    getHandle = handle
End Property

Handle has been defined at the top of the IEAgent, and so this is simply returning the variable.

Completed IE Tools

This concludes the portion of filling out the basic IE functions you will need to automate IE. The next blog post will discuss adding methods to navigate across the DOM.

No comments:

Post a Comment