Auto Testing Browser Control Applications

Build Environment: VC 6.0
Runtime Environment: Any Windows computer with IE 4.0 or later

Introduction

When an application is hosting the browser control, and significant parts of the user interaction are happening in HTML inside the browser control, it can be difficult to write test scripts for that application. For instance, test tools such as SilkTest cannot determine where the controls or user interface elements are located inside the HTML. This article will describe a way to programmatically test such applications, without adding code to the application itself.

You can use the techniques and tools included here to interact with Internet Explorer, but there are also other ways to do that.

Technique

The technique we use is based on the Accessibility DLL, OLEACC.dll. A Technical Overview is available from MSDN. The classes provided hide the interaction with OLEACC.dll. Any program using OLEACC.dll directly should check for its presence before calling into it.

The general approach with the accessibility APIs used here gives an interface pointer to an object through a SendMessage() call. In the case of the browser control, the interface we get is IHTMLDocument2. (This is assuming IE 5.0 or 5.5. The approach will work with IE 4.0 and later, but the version of IHTMLDocument that you get back may be different.) The SendMessage() call must go directly to the browser control window, not any of the wrapper Shell or ATL windows, such as "Shell Embedding" or "Shell DocObject View." Fortunately, EnumChildWindows recurses through nested child windows, so it isn't too difficult to find the control window proper.

The steps required to get the IHTMLDocument interface are as follows:

  1. Load the OLEACC.dll and find the GetObjectFromLresult function.
  2. Find the control window, classname of "Internet Explorer_Server."
  3. Send the window the WM_HTML_GETOBJECT message. This is a registered Windows message.
  4. Pass the result of SendMessage to GetObjectFromLresult.

Assuming these calls succeed, you now have a pointer to an IHTMLDocument2 interface.

If you start using this IHTMLDocument2 interface to access the document displayed in the browser control, you may not get the results you expect. This is because the interface does not represent the documents that are currently loaded. It represents the virtual frame containing your document. This is true even if the HTML document does not use frames. It has something to do with the internal data structures of IE. To get from the document interface that was retrieved from GetObjectFromLresult, to a document interface that represents the loaded HTML document, follow these steps:

  if (spDocIntf)
  {
    CComPtr<IDispatch> spDisp;
    CComQIPtr<IHTMLWindow2> spWin;
    CComPtr<IHTMLDocument2> spDoc;

    if (FAILED(spDocIntf->get_Script(&spDisp)))
      return E_FAIL;
    spWin = spDisp;    //Get IHTMLWindow2 thru QueryInterface
    if (spWin)
      spWin->get_document(&spDoc);
    //Now do your thing with spDoc...
  }

If you don't use ATL and the nifty CComQIPtr class, you must manually call QueryInterface() on spDisp to get the IHTMLWindow2 interface.

Accessing Elements

Once you have a pointer to the IHTMLDocument2 interface, accessing individual elements inside your HTML document is fairly straightforward, by using the Document Object Model. There is no semantical difference between accessing the DOM through scripting, through COM, or even when writing the HTML in the first place. To find an element, we use the all property of the IHTMLDocument2 object, then look for a named element using the item method of IHTMLElementCollection.

About HTMLDrv

The HTMLDrv project wraps the functionality described above, and more, in a couple of convenient classes. It builds as a DLL with exported functions. This makes it easy to use from SilkTest and other testing tools.

Our goal was to test our browser control hosting application using automated SilkTest scripts. There were several options we could have pursued. We chose to have HTMLDrv issue commands directly to the HTML elements inside our documents. Because we were going to test the navigational and purely functional aspects of our application, this was a good and simple approach. If you are testing sophisticated user interactions, such as significantly reacting to events like OnMouseOver and OnKeyDown, you may want to have your test tool perform the input simulations directly on the interface elements. Using the IHTMLDocument2 interface can still be useful because you can get information about where the elements are on the screen (not to mention what elements are present). In that scenario, you will have to do a little more legwork not described in this article.

HTMLDrv contains two classes. WebCtlHandler is the main class. It attaches to a top-level window and dispatches commands to the browser control inside that window. Each WebCtlHandler instance can only be attached to one particular window. The object is attached through a GetDocInterface() call. This method takes a handle to a window; this should be the top-level window. GetDocInterface() performs the steps described above under Technique. Because we wanted HTMLDrv to be compatible with SilkTest, we had to call CoInitialize/CoUninitialize from within the DLL. For this reason, WebCtlHandler ensures that all calls are on the same thread. Otherwise, the COM calls would fail. WebCtlHandler has a couple of methods to invoke commands, one basic InvokeCommand() method, and a second method that is necessary if the HTML document uses frames. In the latter case, WebCtlHandler must know in which frame to dispatch the command.

CHTMLCommand is a class that represents a command. It is responsible for finding a particular control and interacting with it depending on the desired directive. A control, or HTML element, is identified through its name or ID. There are a dozen or so possible directives. The most obvious one is "Click," which will initiate the OnClick event on the HTML element. The rest involves getting or setting the text, getting or setting the value (specific to input elements), setting or clearing check marks, and so forth.

The third source file in HTMLDrv, HTMLDrv.cpp, contains the entry points for the dll, including DllMain(). All the entry points, along with directives and error codes, are documented in ReadMe.txt.

Sample Files

Included in the samples is a DllTest project. This serves two purposes: It is a sample of how to use HTMLDrv from a Windows application, and it also serves as a testing and debugging tool. test.html is a sample test file to play with. To get up and running, perform these simple steps:

  1. Download the projects.
  2. Build HtmlDrv and DllTest (binaries should go in the same directory; the DllTest project file is set up so that the executable ends up in the same directory as HTMLDrv.dll, assuming DllTest is a subdirectory to HTMLDrv).
  3. Double-click test.html.
  4. Run DllTest. Enter "IEFrame" under "Control-id," and click "Get Wnd."
  5. Enter "IdTestBtn" under "Control-id" and click the "Click" button.
  6. Click the "Value" button.

Your window should now look like the picture above.

The file excerpt.inc shows a way to use the HTMLDrv dll from a SilkTest script.

Summary

By using the techniques described in this article, you can get information about, and programmatically interact with, user interfaces displayed in the IE Browser Control. Since these techniques does not require a modification of the application itself, you can even use them to interact with third-party software that uses the browser control. You can use the HTMLDrv dll as is, add to it or modify it, or you can extract the WebCtlHandler and CHPHTMLCommand classes; the latter in particular is generic enough to be used in other contexts.

Downloads

Download HTMLDrv source - 11 Kb
Download DllTest source - 8 Kb
Download binary files (includes ReadMe.txt) - 37 Kb


Comments

  • Firefox

    Posted by Jean-Guillot on 05/08/2005 04:59am

    Hi, Do you know how to make it work with FIREFOX ? Best regards

    Reply
  • Automated Instant XML/SOAP Security

    Posted by Legacy on 12/01/2003 12:00am

    Originally posted by: Roboo

    Automated Instant XML/SOAP Security
    - Automatically builds security gateway to protect web services/apps
    - Instantly shifts your SOAP/XML security burden to ready-to-use Roboo
    - Monitors request/response message traffic and business activities
    - Centralized authentication/authorization/access control (AAA) policy
    - Generates single sign-on (SSO) security tokens using SAML standard
    - XML-encryption/signature for privacy, integrity, non-repudiation
    - Traffic filtering and deep content analysis (schema/size/origin/DoS)
    - Supports XML web services security standard WS-Security
    - Load balancing and session state caching for server cluster/web farm
    - Logging, reporting, alerting, and service-usage business analytics
    - Centralized policy administration GUI tool for local or remote use

    Reply
  • How to set a value in a Select Control

    Posted by Legacy on 08/22/2003 12:00am

    Originally posted by: Dimitrios Alexandropoulos

    How to set a value in a select Control ? I tried the Invoke with the value 7, but the program crashes.

    Reply
  • No Japanese or Chinese character support.

    Posted by Legacy on 07/11/2003 12:00am

    Originally posted by: Jan Raysan

    When I key in Japanese or Chinese characters into Text box. I got only "?????".

    Reply
  • SilkTest extension

    Posted by Legacy on 07/09/2003 12:00am

    Originally posted by: tks

    Hi,

    I haven't downloaded or looked into the sample code provided.

    But, as far as it concern for using this in SilkTest automation, isn't it better to enable SilkTest extension which supports IE Browser Control within a Browser or embeded in an application?? SilkTest does all the hard work for you so that you can "see" all html-like controls.

    Any comments?

    Reply
  • How to get the Control_id(Name of controls) programatically ?

    Posted by Legacy on 04/03/2003 12:00am

    Originally posted by: HuJunXi

    Could you please add more interface functions into the DLL. For example, return a list of Input text box name and index, return a list of checkbox name and index. And also for Radio, Texterea, button, drop down list etc.

    Reply
  • To Get Text from TextArea

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: Henri Hein

    You should be able to get the text inside the text area with a 'GetText' directive. That's command id #2.

    Reply
  • how to set and get text from "Text Area " ?

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: HuJunXi

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • With the average hard drive now averaging one terabyte in size, the fallout from the explosion of user-created data has become an overwhelming volume of potential evidence that law-enforcement and corporate investigators spend countless hours examining. Join Us and SANS' Rob Lee for our 45-minute webinar, A Triage and Collection Strategy for Time-Sensitive Investigations, will demonstrate how to: Identify the folders and files that often contain key insights Reduce the time spent sifting through content by …

Most Popular Programming Stories

More for Developers

RSS Feeds