The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Win32::ActAcc - `Active Accessibility' for task automation, GUI testing

SYNOPSIS

  • Active Accessibility client API

    use Win32::OLE;
    use Win32::ActAcc;
    Win32::OLE->Initialize();
    $ao = AccessibleObjectFromPoint(320,240);
    @children = $ao->AccessibleChildren();
    $hwnd = $ao->WindowFromAccessibleObject();
    $ao2 = AccessibleObjectFromWindow($hwnd);
  • IAccessible

    print $ao->get_accName() . "\n";
    print $ao->get_accValue() . "\n";
    print $ao->get_accDefaultAction() . "\n";
    $statebits = $ao->get_accState();
    $rolenum = $ao->get_accRole();
    $ch1 = $ao->accNavigate(NAVDIR_FIRSTCHILD());
    $p = $ao->get_accParent();
  • WinEvents

    $aoNotepad = Win32::ActAcc::waitForEvent(
       +{ 'event'=>EVENT_OBJECT_SHOW(),
          'name'=>qr/Notepad/,
          'role'=>ROLE_SYSTEM_WINDOW()});
  • Iterators

    Win32::ActAcc's iterators can drill into menus and outlines, in addition to the Active Accessibility object hierarchy.

    # display children of $ao
    my $iter = $ao->iterator();
    $iter->open();
    my $aoi;
    while ($aoi = $iter->nextAO())
    {
        print $aoi->describe() . "\n";
    }
    $iter->close();
  • Finding accessible objects

    # find any descendant
    $notepadTextArea = $aoNotepad->drill("{editable text}", +{'max'=>1, 'min'=>1});
    
    # find immediate child
    $AimConversation = $aoIM->dig( +[ +{'role'=>ROLE_SYSTEM_WINDOW(), 'name'=>qr/\<HTML\>/ } ] );
    
    # follow path
    $tray = Win32::ActAcc::Desktop()->dig([ 
               +{'role'=>Win32::ActAcc::ROLE_SYSTEM_WINDOW(), 'name'=>'', 'state'=>+{'mask'=>Win32::ActAcc::STATE_SYSTEM_INVISIBLE(), 'value'=>0}}, 
               +{'role'=>Win32::ActAcc::ROLE_SYSTEM_WINDOW(), 'name'=>'', 'state'=>+{'mask'=>Win32::ActAcc::STATE_SYSTEM_INVISIBLE(), 'value'=>0}}, 
               +{'role'=>Win32::ActAcc::ROLE_SYSTEM_WINDOW(), 'name'=>'', 'state'=>+{'mask'=>Win32::ActAcc::STATE_SYSTEM_INVISIBLE(), 'value'=>0}}, 
               +{'role'=>Win32::ActAcc::ROLE_SYSTEM_WINDOW(), 'name'=>'Tray', 'state'=>+{'mask'=>Win32::ActAcc::STATE_SYSTEM_INVISIBLE(), 'value'=>0}}, 
               +{'role'=>Win32::ActAcc::ROLE_SYSTEM_PAGETABLIST(), 'state'=>+{'mask'=>Win32::ActAcc::STATE_SYSTEM_INVISIBLE(), 'value'=>0}} ], 
           +{'max'=>1,'min'=>1} );
  • Task-automation conveniences

    use Win32::OLE;
    use Win32::ActAcc;
    use Win32::ActAcc::Shell2000;
    #
    Win32::OLE->Initialize();
    $menu = Win32::ActAcc::Shell2000::StartButtonMenu();
    Win32::ActAcc::clearEvents();
    $menu->menuPick([ qr/^Programs/, qr/Accessories/i, qr/Notepad/i ]);
    $aoNotepad = Win32::ActAcc::waitForEvent(
       +{ 'event'=>EVENT_OBJECT_SHOW(),
          'name'=>qr/Notepad/,
          'role'=>ROLE_SYSTEM_WINDOW()});
    $aoNotepad->menuPick(+["File", "Exit"]);
  • Tools and samples

    • aaDigger.pl shows how to traverse the tree of accessible objects.

    • aaEvents.pl shows you the WinEvents that reflect what you're doing with the GUI.

    • aaWhereAmI.pl shows how to link a pixel location with its accessible object.

    • elizaNotepad.pl, just for fun, uses Notepad as the user interface to Chatbot::Eliza.

    See under "Tools" for more about aaDigger, aaEvents, and aaWhereAmI.

You'll want to additionally use Win32::GuiTest, and other Win32 modules, if you want your script to click, type, manipulate the clipboard, etc.

DESCRIPTION

Active Accessibility and Win32::ActAcc

Active Accessibility is an API from Microsoft. See "SEE ALSO" for a pointer to the documentation on Active Accessibility.

Win32::ActAcc is based on Active Accessibility 1.3 on Windows 2000. With other Active Accessibility implementations, your mileage may vary.

In keeping with Perl's culture of laziness and impatience, Win32::ActAcc smoothes out some of the bumps in Active Accessibility for Perl programmers. For example, a standard Active Accessibility client must carry around two keys to each "accessible" thing: an IAccessible* and a child-ID. What a bore! Luckily, with Win32::ActAcc, an accessible object is an accessible object (Win32::ActAcc::AO); no pairs of keys.

Active Accessibility has three parts -- the "client API", which is not object-based; IAccessible, which is an API that each "accessible object" is supposed to implement; and WinEvents, which tell you what's going on.

Active Accessibility client API: overview

The client API is mainly useful for getting an "accessible object" corresponding to a "real-world" on-screen object like a window or pushbutton. Then you can go use the accessible object using the "Win32::ActAcc::AO" methods.

(The other two ways of getting an accessible object are: asking one for another, as with "get_accParent", and monitoring "WinEvents" as windows appear, disappear, move, and so forth.)

Win32::ActAcc provides access to these Active Accessibility client API functions: "AccessibleChildren", "AccessibleObjectFromEvent", "AccessibleObjectFromPoint", "AccessibleObjectFromWindow", "GetRoleText", "GetStateText", "WindowFromAccessibleObject".

IAccessible: overview

Win32::ActAcc::AO and its subclasses provide access to these IAccessible functions: "accDoDefaultAction", "accLocation", "accNavigate", "accSelect", "get_accChild", "get_accChildCount", "get_accDefaultAction", "get_accDescription", "get_accFocus", "get_accHelp", "get_accKeyboardShortcut", "get_accName", "get_accParent", "get_accRole", "get_accState", "get_accValue".

Win32::ActAcc does not yet implement get_accSelection (which does not operate on text edits anyway).

WinEvents: overview

You can wait for something to happen by using waitForEvent. And, you can open your own "cursor" on the stream of WinEvents, using "createEventMonitor".

There's more than one way to do it

In keeping with the Perl principle of TMTOWTDI ("There's more than one way to do it"), and the Perl culture of laziness and impatience, three innovations were necessary, extending raw Active Accessibility:

  • Object-orientation. Whenever Win32::ActAcc gives you an AO, what you get is actually a subclass specific to the AO's role. For example, if the role is ROLE_SYSTEM_OUTLINE, you get an object from package Win32::ActAcc::Outline, which inherits from AO. See "AO Subclasses". This allows polymorphism, which in turn helps us write programs that do the right thing, despite themselves. Object-orientation is a slippery slope and led to the further innovations:

  • Conveniences. Such as "menuPick" and outline navigation: things you might suppose would be built-in to Active Accessibility in an obvious and straightforward way, but aren't.

  • Iterators. Active Accessibility gives you three ways to enumerate the "children" of an accessible object. And darned if they don't sometimes yield conflicting results! Worse, you can't switch freely among them: one gives you a list, one gives you random access, and one requires that you step from one child to the next. So Win32::ActAcc wraps the navigation techniques, and others, with iterators that all work alike. Write your algorithm once and change iterators any time.

Active Accessibility has some minor imperfections. Of course, Win32::ActAcc itself also has imperfections. To aid the reader in distinguishing the two, we have taken the liberty of calling out some "Active Accessibility weirdnesses" as such. All other parts of this document describe the idiosyncracies of Win32::ActAcc.

Win32::ActAcc

If this is your first reading, you may want to read about Desktop and AccessibleObjectFromWindow in this section, then skip to "Win32::ActAcc::AO".

Desktop

Obtain an "accessible object" representing the desktop, so you can call the object's Active Accessibility methods:

$accObj = Desktop();

The Desktop is a natural starting-point for traversing the tree of Accessible Objects.

Once you've got an accessible object, see "Win32::ActAcc::AO" on how to use it.

If you do not have a clear picture in mind of the accessible-object "tree" of which Desktop is the root, go try out the "aaDigger.pl" tool.

AccessibleObjectFromWindow

If you have an HWND, you can get an Accessible Object for it:

$accObj = AccessibleObjectFromWindow($hwnd);

AccessibleObjectFromWindow's optional second parameter defaults to OBJID_WINDOW. See "OBJID constants".

AccessibleObjectFromPoint

AccessibleObjectFromPoint checks the screen at the specified point and returns the foremost accessible object occupying that location.

my $accObj = AccessibleObjectFromPoint($x, $y);

Speaking of ($x, $y), how do you figure out where the mouse is? You can subscribe to the WinEvents stream and watch for mouse location-change events. See sample aaEvents.pl to see how this works.

click

Win32::ActAcc::click($xpix, $ypix, \$eh);

click() "clicks" somewhere on the screen, but first, it activates the optional event monitor, so you can capture the consequences of the click. See "activate" and "menuPick".

You should use Win32::GuiTest for any extensive GUI manipulation. Win32::ActAcc's "click" method is not as flexible.

GetRoleText

Returns localized name of a role-number.

my $chRole = Win32::ActAcc::GetRoleText($accObj->get_accRole());

GetStateText

Returns localized name of a state-number.

my $statebit = STATE_SYSTEM_FOCUSED();
my $statename = Win32::ActAcc::GetStateText($statebit);

Normally, you'll want to call GetStateTextComposite instead, since GetStateText works with only one state bit. States are combinations of state-bits such as STATE_SYSTEM_FOCUSED (see "STATE constants").

GetStateTextComposite

Returns a localized string of state texts, representing all of the turned-on state bits in the argument. "describe" uses GetStateTextComposite.

$stateDesc = Win32::ActAcc::GetStateTextComposite( $accObj->get_accState() );

StateConstantName

my $n = Win32::ActAcc::StateConstantName(STATE_SYSTEM_INVISIBLE());

Returns the C constant name for a state-bit defined in OleAcc.h (see "STATE constants").

ObjectIdConstantName

my $objname = Win32::ActAcc::ObjectIdConstantName($$e{'idObject'});

Returns the C constant name for an object ID defined in OleAcc.h (see "OBJID constants").

waitForEvent (Win32::ActAcc)

Waits for an event, using the default event cursor. See the "waitForEvent" method of Win32::ActAcc::EventMonitor.

clearEvents (Win32::ActAcc)

Discards any unconsumed events on the default event cursor. See the "clear" method of Win32::ActAcc::EventMonitor.

Win32::ActAcc::AO - properties and actions

Here are the methods on an "accessible object". This package includes the IAccessible methods, some of which have funny names.

Active Accessibility weirdness note: There is no guarantee in Active Accessibility that an onscreen object shall be represented by one-and-only-one IAccessible*. In fact, the Active Accessibility server helper built into Windows to represent standard controls seems to generally return a new object in response to any query. So, there is no sure-fire way to compare Accessible Objects.

Even when an Active Accessibility server returns the same IAccessible* in response to two different calls, Win32::ActAcc may make them into discrete Win32::ActAcc::AO objects.

describe

Produces human-readible (appropriate for debugging) description of an AO. It's just like what you get from the aaDigger utility. Here's an example, with the fields labeled.

window:emacs: ActAcc.pod {sizeable+moveable+focusable,(4,44,1009,663),id=0,000402e2}
^      ^--title/text      ^-- 'state' bits            ^               ^--ID                   
|                                                     |                    ^-- HWND
+-role                                                +-(left,top,width,height)

describe() isn't supposed to die. If something goes wrong, it returns an incomplete or empty string.

print $accObj->describe();

If your script displays the results of describe() to its user, you might also want to print out describe_meta() at least once. It names the fields.

print Win32::ActAcc::AO::describe_meta();

For outline items, describe() includes '>' symbols corresponding to the hierarchy depth, immediately to the left of the name.

WindowFromAccessibleObject

Reverses "AccessibleObjectFromWindow":

$hwnd = $accObj->WindowFromAccessibleObject(); 

If no HWND corresponds to the object, WindowFromAccessibleObject dies, so you might want to run it inside an eval().

get_accName

Returns the 'name' property of the accessible object. For editable text and combo box objects, it appears this is the label Windows supposes the object has: it is usually identical to the text of the immediately-preceding text object. For windows, this is the title. For client areas, this is the same as the title of the enclosing window.

$name = $accObj->get_accName();

Returns undef if the object doesn't have this property.

get_accRole

$role = $accObj->get_accRole();

Returns a number, one of the Active Accessibility ROLE_ constants (see "ROLE Constants"). You can convert the number to a string with "GetRoleText". Returns undef if the object doesn't have this property.

get_accState

$state = $accObj->get_accState();

Returns a number composed of bits defined by the Active Accessibility STATE_ constants ("STATE Constants"). See "GetStateTextComposite".

Returns undef if the object doesn't have this property.

get_accValue

Returns the 'value' of the accessible object: the stuff in an editable text control, the outline-level of an outline item, etc.

$value = $accObj->get_accValue();

Returns undef if the object doesn't have this property.

accLocation

my ($left, $top, $width, $height) = $accObj->accLocation();

Returns the accessible object's location on the screen, in pixels. (0,0) is at the top left. Dies if the object doesn't have this property.

get_accDescription

$desc = $accObj->get_accDescription();

Returns undef if the object doesn't have this property. If you're trying to debug your script, "describe" is probably more appropriate, since it appears most accessible objects don't define their description.

get_accHelp

$help = $accObj->get_accHelp();

Returns undef if the object doesn't have this property.

get_accParent

$p = $accObj->get_accParent();

Returns the parent object. Returns undef if the object has no parent.

get_accDefaultAction

$da = $accObj->get_accDefaultAction();

An object's default action may depend on the mouse location, which window is topmost, which shift/control keys are pressed, etc. Returns undef if the object doesn't have this property.

get_accKeyboardShortcut

$ks = $accObj->get_accKeyboardShortcut();

Returns undef if the object doesn't have this property.

get_accFocus

$f = $accObj->get_accFocus();

accDoDefaultAction

$accObj->accDoDefaultAction();

The effect of accDoDefaultAction is supposed to reflect "get_accDefaultAction".

Win32::ActAcc calls a hook function before doing the default action. Initially, the hook is undef, but you can install different code if you like. The sample displays a record of the actions:

$Win32::ActAcc::AO::accDoDefaultActionHook = sub 
{
    my $ao = shift;
    print "accDoDefaultAction(".$ao->describe().")\n";
};

get_itemID

$plusOrDot = (CHILDID_SELF() == $ch[$i]->get_itemID()) ? '+' : '.';

get_itemID() returns the item-ID that is part of the identity of the accessible object.

accSelect

$accObj->accSelect(SELFLAG_TAKEFOCUS());

See "SELFLAG constants".

click (center of Accessible Object)

$accObj->click(\$eh);

click() "clicks" the center of the accessible object, but first, it activates the optional event monitor, so you can capture the consequences of the click. See "activate" and "menuPick".

Release

$accObj->Release();

Accessible objects are COM objects, so each one must be Released when you're done with it. Perl's garbage collector and Win32::ActAcc::AO conspire to automatically Release the accessible objects, so you should not need to call Release in your scripts.

Win32::ActAcc::AO - comparison

match

Match is how 'drill' and 'dig' decide whether they've found what you are looking for. match checks the AO against specified criteria and returns a true value if none of the criteria is inconsistent with the AO's attributes.

$b = $ao->match( CRITERIA );

CRITERIA is flexible:

$b = $ao->match("Close"); # Is AO's name exactly Close?
$b = $ao->match( +{'name'=>"Close"} ); # ... using a hash.
#
$b = $ao->match(qr/Close/); # Does AO's name match that regexp?
$b = $ao->match( +{'name'=>qr/Close/} ); # ... using a hash.
#
$b = $ao->match("{ROLE_SYSTEM_PUSHBUTTON}Close"); # Is AO a pushbutton named Close?
$b = $ao->match("{push button}Close"); # ... using localized 'role text'
$b = $ao->match("{Pushbutton}Close"); # ... using ActAcc package name
$b = $ao->match( +{'role'=>ROLE_SYSTEM_PUSHBUTTON(), 'name'=>"Close"} ); # ...
$b = $ao->match( +{'rolename'=>'Pushbutton', 'name'=>"Close"} ); # ...
#
$b = $ao->match( sub{...} ); # Does sub($ao) return a true value?
$b = $ao->match( +{'code'=>sub{...} } ); # ... using a hash.
#
# Find only visible windows.
$b = $ao->match( +{'state'=>+{'mask'=>STATE_SYSTEM_INVISIBLE(), 'value'=>0} } ); 
#
# Find only INvisible windows.
$b = $ao->match( +{'state'=>+{'mask'=>STATE_SYSTEM_INVISIBLE(), 'value'=>STATE_SYSTEM_INVISIBLE()} } ); 

So you see: For the discriminator, you can use a string, a regexp, a code reference, or a hash.

Only in the "{ROLE_SYSTEM_PUSHBUTTON}Close" case can you combine a role with a name in one string. (If you want to avoid that interpretation of the braces, use the hash instead.)

Hash members:

  • rolename - a localized 'role text' (as though received from GetRoleText), or an ActAcc package name with or without the Win32::ActAcc:: part, or the name of a role constant with or without the ROLE_SYSTEM_ part.

  • role - the value of a role constant.

  • name - a regexp or a string (if a string, then case matters and it must match the entire name).

  • state - a hash with 'mask' and 'value' members. match takes the state of each AO, does a bitwise AND with the mask, then compares with the value.

  • code - a coderef invoked with two arguments: the subject AO (also in $_), and the criterion. If your code needs additional parameters or needs to squirrel away information for the caller of match, use closures. The code is called only if the other criteria all match. Return a true value to indicate a match.

For those frustrating occasions where you're right and the computer is wrong, match can tell you what about the criteria is inconsistent with the AO.

my $reasonForMismatch;
$b = $ao->match( CRITERIA, \$reasonForMismatch );
print $reasonForMismatch;

Equals

my $dt = Win32::ActAcc::Desktop();
my $e1 = $dt->Equals($dt);
die unless $e1;

Equals returns a true value if, and only if, at least one of these conditions holds.

  • the IAccessible* and ID of the two accessible objects both match.

  • the accessible objects both reify an HWND, and their HWNDs are equal.

Active Accessibility weirdness note: Because Active Accessibility servers usually return a distinct Accessible Object for each method call, and not all Accessible Objects have an HWND, it's not wise to rely very heavily on Equals except under not very general circumstances.

There are other ways to guess about the "equality" of two accessible objects. For example, the AONavIterator class regards $a and $b equal if $a->describe() gives the same string as $b->describe(): thereby taking into consideration name, role, state, location, default action, HWND, and whatnot. Architectural note: Yes, relying on describe() is gross.

Win32::ActAcc::AO - navigation by iterator

Iterators are devices that give you access to all or some of an Accessible Object's child-objects. Win32::ActAcc's iterators are implemented on top of Active Accessibility, which itself does not use the iterator concept.

You can use an iterator with a loop:

my $iter = $ao->iterator();
$iter->open();
my $aoi;
while ($aoi = $iter->nextAO())
{
    print $aoi->describe() . "\n";
}
$iter->close();

Or you can use an iterator to populate a list:

my $iter = $ao->iterator();
my @L = $iter->all();
print join($_->describe(), @L) . "\n";

You can pass flags to the iterator() method if you have special needs:

my $iter = $ao->iterator( +{'lax'=>1} ); # enumerate client's children along with client (like 'dig')
my $iter = $ao->iterator( +{'active'=>1} ); # treat outline items and menu items hierarchically
my $iter = $ao->iterator( +{'perfunctory'=>1} ); # use speedier AOIterator rather than AONavIterator
my $iter = $ao->iterator( +{'nav'=>1} ); # use NavIterator rather than AONavIterator

Active iteration on outline items, menu items, and menu buttons involves "clicking" on the GUI and could cause certain programs to take action in a way you wouldn't expect. For example, using active iteration on the outline in the Windows Explorer can cause Explorer to prompt you for the password to network resources, or to complain that there is no disk in your A: drive. Active iteration on menus doesn't work unless the menu is on the active (topmost) window.

Iterators

AOIterator

Uses "AccessibleChildren"

NavIterator

Uses "accNavigate"(NAVDIR_FIRSTCHILD), then repeatedly accNavigate(NAVDIR_NEXT)

AONavIterator

Combines AOIterator with NavIterator. The point is to find even those children that one technique or the other misses. This is the default iterator for Win32::ActAcc::AO. This iterator considers two AO's as equal if describe() describes them with equal strings. This iterator is slow, but thorough.

DelveClientIterator

Folds an AO's client's children in with the AO's children (the title bar and whatnot), in effect collapsing layers of the hierarchy that sometimes seem to get underfoot. (Uses AONavIterator.) This is the default iterator for Win32::ActAcc::Window.

"MenuIterator"

Follows a "logical" hierarchy of menu items, just as humans perceive it. Default iterator for Win32::ActAcc::Menubar and Win32::ActAcc::MenuItem.

OutlineIterator

Opens, traverses and closes outline items. Follows a "logical" hierarchy of outline items, just as humans perceive it. This is the default iterator for Win32::ActAcc::Outline and Win32::ActAcc::OutlineItem. (Iterating the Outline is the same as iterating its root item.)

Each AO has a default iterator type, which you get an instance of by calling $ao->iterator(). But if you absolutely need a certain type of iterator, you can be explicit:

my $oi = new Win32::ActAcc::NavIterator($ao); # choose non-default iterator

Only AOIterator, NavIterator, and AONavIterator are faithful to Active Accessibility's notion of parent/child relationships. The other iterators fudge. For example, MenuIterator makes submenus seem to be children of menus. OutlineIterator makes sub-items appear to be children of items. DelveClientIterator makes controls that are technically in a window's client area, appear to be children of the window itself.

Only AOIterator and NavIterator are faithful to Active Accessibility's order of child items. (AONavIterator comes close.)

MenuIterator opens, traverses (with AONavIterator), and closes menus.

"Opening" a menu may not work unless the menu is in the topmost active window; it seems to cause the menu to actually open on-screen, as though you'd clicked it.

Note: In Active Accessibility, standard menus work differently from custom (Office) menus, which in turn work differently from the Start menu. Yuck! But Win32::ActAcc tries to smooth over the differences, so they all work the same from the point of view of Perl scripts.

Win32::ActAcc::AO - Hunting for children and descendants

drill

drill takes the AO as the root of a subtree of accessible objects, applies a criterion to each object in the tree, and returns a list of the objects that match the criterion.

@list = $ao->drill( CRITERIA );
@list = $ao->drill( CRITERIA, OPTIONS );

drill finds "invisible" objects as well as visible ones. If you're looking for something visible, remember to specify so in the criteria:

... +{'role'=>Win32::ActAcc::ROLE_SYSTEM_WINDOW(), 
      'name'=>'', 
      'state'=>+{'mask'=>Win32::ActAcc::STATE_SYSTEM_INVISIBLE(), 'value'=>0}}, ...

drill prunes menu bars and outlines unless you specify a prunes list. That is, drill normally doesn't examine the children of menu bars and outlines, since they require active iteration, which could cause side effects.

 @btnClose = $wNotepadApp->drill( 
	sub{	
		my $n = $_->get_accName(); 
		(defined($n) && $n eq "Close") && 
			($_->get_accRole() == ROLE_SYSTEM_PUSHBUTTON()) 
	});

You can customize drill by specifying a hash of options. The hash keys are:

  • 'min'=>n -- make drill die if it doesn't find at least n matches.

  • 'max'=>n -- make drill return as soon as it finds n matches. If max is 1, then drill returns a scalar value instead of a list.

  • 'pruneOnMatch'=>1 -- make drill not examine the children of an AO that matches.

  • 'prunes'=>+[CRITERIA, CRITERIA, etc.] -- make drill not examine the children of an AO that matches at least one of the criteria. If the options hash does not have a 'prunes' key, then the default is to prune at Menubar and Outline objects. To probe those, you can specify 'prunes' as an empty list.

dig

dig finds Accessible Objects by following a path from a starting point. The path specifies the criterion to match at each step.

@aos = $ao->dig( +[ CRITERIA, CRITERIA, etc. ] );
@aos = $ao->dig( +[ CRITERIA, CRITERIA, etc. ], +{ OPTIONS } );

Here's one way to find the Start button. We're telling dig to start at the desktop; from there, find a window (by any name), within it a window called Start, and in turn within it a pushbutton called Start. How did we know?! We used aaDigger and went exploring. Humility gets you a long way in the business of automating usage of GUIs.

$btnStart = Desktop()->dig([ "{window}", "{window}Start", "{push button}Start" ] );

dig lets you be somewhat imprecise (for example, the above quest for the Start button did not specify the name of the top-level window, so dig might have to hunt through several). dig is more efficient, the more precise the parameters are.

dig is also useful finding a control on a dialog:

$accObjOk = $accObjDlg->dig([ "OK" ]);

If you like, you can specify the path as an array of hashes instead of an array of strings. Each hash must have a 'role' entry and a 'name' entry. The role is a string like "menu bar", which aaDigger can tell you. The name must be either a string or a regexp.

dig uses the default iterator of each AO, and therefore automatically traverses "client" windows without being told to. Windows with border ornaments typically have a title-bar, a menu-bar, and a "client" area; the interesting stuff is within the client area.

You can customize dig by specifying a hash of options. The hash keys are:

  • 'min'=>n -- make dig die if it doesn't find at least n matches.

  • 'max'=>n -- make dig return as soon as it finds n matches. If max is 1, then dig returns a scalar value instead of a list.

  • 'trace'=1 -- make dig display information on STDERR about what it is doing.

tree

The tree method performs a depth-first traversal of the Accessible Object hierarchy. At each Accessible Object, tree() calls code that you specify.

For example, we can display the Accessible Object hierarchy:

$ao->tree( sub{my ($ao,$tree)=@_; print ' 'x($tree->level()).$ao->describe."\n";} );

tree() uses the default iterator for each AO it visits. You can supply iterator flags as a second argument to tree():

$ao->tree( sub{print "*";}, +{'lax'=>1, 'active'=>1});

What can the "sub" do? It gets two arguments ($ao, $tree). The $tree argument gives access to the tree-context object, which has the following methods:

level() returns 0 for the root (i.e., the AO whose 'tree' method was called), 1 for the root's children, 2 for the root's grandchildren, etc.

prune() causes tree not to enumerate the children of $ao; to skip them and go on with $ao's siblings.

stop() causes tree to return to its caller without enumerating any more accessible objects.

Win32::ActAcc::AO - navigation methods

These are methods for exploiting the parent/child relationships among Accessible Objects. You will probably want to use the iterators instead, to get around the problems with accessible-object navigation in Active Accessibility --

  • AccessibleChildren and accNavigate disagree -- they often return different results!

  • there is no convenient way to get at the "logical children" of a menu or outline

  • AccessibleChildren returns a list, whereas accNavigate makes you loop; you can't write a single algorithm that can work with either data source.

Win32::ActAcc fills in those gaps with iterator algorithms. The iterators are polymorphic, so you can use them all the same way, and write your own algorithms that can operate on any iterator.

But, in case iterators don't suit your need, here are the more primitive alternatives.

AccessibleChildren

@ch = $accObj->AccessibleChildren();

Returns a list of the accessible objects that are children of $accObj.

By default, in Win32::ActAcc, AccessibleChildren omits the invisible children; that's because the no-argument form of AccessibleChildren is short for

@ch = $accObj->AccessibleChildren(STATE_SYSTEM_INVISIBLE(), 0);

The first parameter is a bit mask with 1's for the bits that matter as criteria, and the second parameter is the bit values to find in each of the '1' positions in the mask. So you can see that the default parameters indicate to find only the children whose STATE_SYSTEM_INVISIBLE state bit holds 0.

To find the complement, i.e., only the invisible children, you can use:

@ch = $accObj->AccessibleChildren(
   STATE_SYSTEM_INVISIBLE(), 
   STATE_SYSTEM_INVISIBLE());

which means that the INVISIBLE bit should be included in the comparison, and it must be 1. The second parameter is 1 shifted into the bit position for STATE_SYSTEM_INVISIBLE. See "STATE constants".

To find all the children, visible or not, you can use:

@ch = $accObj->AccessibleChildren(0,0); # no state bit matters, so all children qualify

Active Accessibility weirdness note: You will probably want to use AccessibleChildren() instead of get_accChildCount() and get_accChild(). AccessibleChildren probably calls those and then improves the results. But, AccessibleChildren frequently returns fewer children than get_accChildCount says it should.

Active Accessibility weirdness note: Some objects report 1 child with AccessibleChildren, yet accNavigate reveals more children. You can work around this problem by using AONavIterator or NavIterator (each of which may have its own drawbacks!).

In the Active Accessibility SDK, AccessibleChildren() is part of the API, not part of IAccessible.

accNavigate

my $smch = $accObj->accNavigate(NAVDIR_FIRSTCHILD());
while (defined($smch))
{
	my $n = $smch->get_accName();
	print STDERR "$n\n";
	$smch = $smch->accNavigate(NAVDIR_NEXT());
}

Returns an Accessible Object representing one of the base object's relations. See "NAVDIR constants".

Returns undef if asked for something that does not exist, provided that the server follows the spec and returns S_FALSE in that case.

accNavigate does not move focus, nor perform any other action on behalf of the user.

get_accChildCount

$nch = $accObj->get_accChildCount();

See "AccessibleChildren".

get_accChild

$ch = $accObj->get_accChild(3);

See "AccessibleChildren".

AO Subclasses

Perl scripts can treat accessible objects polymorphically, since Win32::ActAcc establishes a subclass for each Role of accessible object.

This is how Menubar's iterator method works differently from Outline's; but from the caller's perspective, they do the same thing and there is no need to distinguish them.

These packages derive from Win32::ActAcc::AO, inherit all its methods, and are implemented using its methods. (The Active Accessibility API does not have helper methods specific to certain kinds of accessible objects.)

Role constantActAcc Package
ROLE_SYSTEM_TITLEBAR

"Win32::ActAcc::Titlebar"

ROLE_SYSTEM_MENUBAR

"Win32::ActAcc::Menubar"

ROLE_SYSTEM_MENUITEMWin32::ActAcc::MenuItem
ROLE_SYSTEM_MENUPOPUPWin32::ActAcc::MenuPopup
ROLE_SYSTEM_SCROLLBARWin32::ActAcc::Scrollbar
ROLE_SYSTEM_GRIPWin32::ActAcc::Grip
ROLE_SYSTEM_SOUNDWin32::ActAcc::Sound
ROLE_SYSTEM_CURSORWin32::ActAcc::Cursor
ROLE_SYSTEM_CARETWin32::ActAcc::Caret
ROLE_SYSTEM_ALERTWin32::ActAcc::Alert
ROLE_SYSTEM_WINDOW

"Win32::ActAcc::Window"

ROLE_SYSTEM_CLIENTWin32::ActAcc::Client
ROLE_SYSTEM_TOOLTIPWin32::ActAcc::Tooltip
ROLE_SYSTEM_APPLICATIONWin32::ActAcc::Application
ROLE_SYSTEM_DOCUMENTWin32::ActAcc::Document
ROLE_SYSTEM_PANEWin32::ActAcc::Pane
ROLE_SYSTEM_CHARTWin32::ActAcc::Chart
ROLE_SYSTEM_DIALOGWin32::ActAcc::Dialog
ROLE_SYSTEM_BORDERWin32::ActAcc::Border
ROLE_SYSTEM_GROUPINGWin32::ActAcc::Grouping
ROLE_SYSTEM_SEPARATORWin32::ActAcc::Separator
ROLE_SYSTEM_TOOLBARWin32::ActAcc::Toolbar
ROLE_SYSTEM_STATUSBARWin32::ActAcc::StatusBar
ROLE_SYSTEM_TABLEWin32::ActAcc::Table
ROLE_SYSTEM_COLUMNHEADERWin32::ActAcc::ColumnHeader
ROLE_SYSTEM_ROWHEADERWin32::ActAcc::RowHeader
ROLE_SYSTEM_COLUMNWin32::ActAcc::Column
ROLE_SYSTEM_ROWWin32::ActAcc::Row
ROLE_SYSTEM_CELLWin32::ActAcc::Cell
ROLE_SYSTEM_LINKWin32::ActAcc::Link
ROLE_SYSTEM_HELPBALLOONWin32::ActAcc::HelpBalloon
ROLE_SYSTEM_CHARACTERWin32::ActAcc::Character
ROLE_SYSTEM_LISTWin32::ActAcc::List
ROLE_SYSTEM_LISTITEMWin32::ActAcc::ListItem
ROLE_SYSTEM_OUTLINE

"Win32::ActAcc::Outline"

ROLE_SYSTEM_OUTLINEITEM

"Win32::ActAcc::OutlineItem"

ROLE_SYSTEM_PAGETABWin32::ActAcc::PageTab
ROLE_SYSTEM_PROPERTYPAGEWin32::ActAcc::PropertyPage
ROLE_SYSTEM_INDICATORWin32::ActAcc::Indicator
ROLE_SYSTEM_GRAPHICWin32::ActAcc::Graphic
ROLE_SYSTEM_STATICTEXTWin32::ActAcc::StaticText
ROLE_SYSTEM_TEXTWin32::ActAcc::Text
ROLE_SYSTEM_PUSHBUTTONWin32::ActAcc::Pushbutton
ROLE_SYSTEM_CHECKBUTTONWin32::ActAcc::Checkbox
ROLE_SYSTEM_RADIOBUTTONWin32::ActAcc::Radiobutton
ROLE_SYSTEM_COMBOBOXWin32::ActAcc::Combobox
ROLE_SYSTEM_DROPLISTWin32::ActAcc::DropList
ROLE_SYSTEM_PROGRESSBARWin32::ActAcc::ProgressBar
ROLE_SYSTEM_DIALWin32::ActAcc::Dial
ROLE_SYSTEM_HOTKEYFIELDWin32::ActAcc::HotKeyField
ROLE_SYSTEM_SLIDERWin32::ActAcc::Slider
ROLE_SYSTEM_SPINBUTTONWin32::ActAcc::SpinButton
ROLE_SYSTEM_DIAGRAMWin32::ActAcc::Diagram
ROLE_SYSTEM_ANIMATIONWin32::ActAcc::Animation
ROLE_SYSTEM_EQUATIONWin32::ActAcc::Equation
ROLE_SYSTEM_BUTTONDROPDOWNWin32::ActAcc::ButtonDropDown
ROLE_SYSTEM_BUTTONMENUWin32::ActAcc::ButtonMenu
ROLE_SYSTEM_BUTTONDROPDOWNGRIDWin32::ActAcc::ButtonDropDownGrid
ROLE_SYSTEM_WHITESPACEWin32::ActAcc::Whitespace
ROLE_SYSTEM_PAGETABLISTWin32::ActAcc::PageTabList
ROLE_SYSTEM_CLOCKWin32::ActAcc::Clock

Win32::ActAcc::Window

Returns an AO for the main menu in the window. Dies if there isn't one. This method relies on the characteristic "name" of the menus served by the default Active Accessibility in Windows. Apps that make their own custom menus, such as Microsoft Word, may call the menu something different, in which case mainMenu won't find it.

systemMenu

Returns an AO for the system menu in the window. Dies if there isn't one. The system menu is the inobtrusive thingy in the title bar, which if you click it shows you choices including Close, Maximize, etc.

titlebar

Returns an AO for the window's titlebar. Dies if it doesn't have one.

Invokes a command from the application's main menu bar. See "menuPick" for Menubar.

Win32::ActAcc::Titlebar

btnMaximize

btnMinimize

btnClose

btnRestore

Win32::ActAcc::MenuItem

$aoNotepad->menuPick(+["File", "Exit"]);

menuPick uses the same CRITERIA as dig. You can specify each step as a string, a regexp, a CODE block, a hash, and whatnot.

menuPick will not find menu items that are hidden since they are so boring that you have not chosen them recently. So, before you run a script that relies on menuPick, you should "prime" the menu items the script will need, if they've disappeared.

menuPick will not find menu items that are scrolled out of view.

Win32::ActAcc::Outline

getRoot

my $rootitem = $outline->getRoot();

The root item is the top item in the outline. It is also the only item at the lowest outline-level in the outline.

Active Accessibility weirdness note: Outline items are all peers, from the point of view of Active Accessibility. They are all directly children of the outline object. Hierarchy resides in the value of an outline item. Higher values nest inside lower values. Relatedly, the dig, drill, AccessibleChildren, etc., methods are not useful for traversing an outline.

Win32::ActAcc::OutlineItem

getLevel

Returns the outline-level of the outline item. The level is the Active Accessibility "value" of the item; this method just improves the legibility of scripts that would otherwise be calling get_accValue.

Outer levels appear to have lower numbers than inner levels.

WinEvents

Using WinEvents, a Perl script can detect the consequences of its actions. For example, having "started" Notepad, the script must wait until Notepad comes up and latch onto the new Notepad window (as opposed to some pre-existing Notepad window) before typing stuff into it.

Windows is continually emitting "events" such as EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, etc (see "EVENT constants"). The ActAcc DLL receives these events and stores them in a circular buffer. Perl scripts may poll that buffer using Win32::ActAcc::EventMonitor. An EventMonitor is a cursor (or iterator) on the stream of events.

Normally you'll use the default EventMonitor. If you need more than one cursor on the stream of events, you can call "createEventMonitor" to make a new event cursor. Refer to the aaEvents sample.

Each Perl process with an active EventMonitor installs an Active Accessibility "in-proc" event hook that records all events in a fixed-size circular buffer. There's no overrun indicator on the circular buffer, so try to keep your EventMonitors reasonably up-to-date if it's important not to miss any events. Circular buffer size (in events) for this build of ActAcc:

5000

EventMonitors don't seem to pick up any WinEvents from Command Prompt windows in Windows 2000. Perhaps these windows are owned by a process that resists in-proc event hooks. Hmm.

createEventMonitor

my $ehDlg = createEventMonitor(1);

createEventMonitor creates a Win32::ActAcc::EventMonitor object, which the script can poll for WinEvents using waitForEvent() or getEvent().

The "1" means the EventMonitor is immediately activated, which is how you generally want it.

EventMonitor

Here are the methods on the EventMonitor object you get from createEventMonitor.

waitForEvent

Sometimes, you want to see whether something happens. Other times, you know what will happen, but you want to find out which Accessible Object it happens to. waitForEvent() meets both needs.

Here's a sample of how to wait (for up to 30 seconds) for a Notepad window to appear.

$aoNotepad = $eh->waitForEvent(
 +{ 'event'=>EVENT_OBJECT_SHOW(),
    'name'=>qr/Notepad/,
    'role'=>ROLE_SYSTEM_WINDOW() }, 30);

waitForEvent blocks the script until a matching event arrives, or the timeout expires. The return value is the accessible object of the winning event, or undef if the timeout expired. So: You see how the sample cunningly not only learns that a Notepad windows has appeared, but actually gets its Accessible Object for later use (such as to close the window).

You can omit the timeout, in which case waitForEvent patiently waits for a matching event... which might never arrive.

The hash's entries are:

  • event: an event constant (remember the parentheses--see sample above). See "EVENT Constants".

  • name (optional): a regexp or string to match the name of the accessible object of the event. If it's a string, it must match exactly--case matters and it must match the entire name.

  • role (optional): a role constant that must match the accessible object of the event, e.g., ROLE_SYSTEM_WINDOW(). See "ROLE Constants".

  • hwnd (optional): only events on the specified HWND match.

  • aoToEqual (optional): only events on the specified AO (according to "Equals") match. If you're waiting for EVENT_OBJECT_DESTROY, use the "hwnd" member instead of aoToEqual.

  • code (optional): a CODE block that takes one argument, the event. Only events where the code block returns a true value match.

If your desires don't fit the hash mold, you can give a code-reference instead. waitForEvent returns when the code returns any non-undef. waitForEvent returns what the code returned.

$eh->waitForEvent(sub{print Win32::ActAcc::Event::evDescribe(@_)."\n";undef}, $secs);

You can find out whether the event is already in the circular buffer (and not already retrieved by this EventMonitor) by specifying 0 for the timeout.

eventLoop

eventLoop is like waitForEvent, but takes a reference to a LIST of event criteria whereas waitForEvent takes just one.

To decide between waitForEvent and eventLoop, you may:

  • Always use eventLoop, the more flexible.

  • Model your need in the form of one of these three English sentences: (1) "Stand here and wait for a guy wearing an orange hat", (2a) "Stand here and wait until you see either a guy wearing an orange hat or a kangaroo with a lollypop", (2b) "Stand here until the alligator eats the butterfly and greet each kangaroo that passes by wearing an orange hat". Sentences 2a and 2b would require eventLoop.

$eh->eventLoop( +[ EVENTCRITERIA, EVENTCRITERIA, etc. ], $timeoutSeconds );

You can put the event-response code into the 'code' key of the criteria hash, and make that code return a false value except when the eventLoop should end.

getEvent

my $e = $eh->getEvent();
if (defined($e)) { print $e->evDescribe() . "\n"; }

You will usually want to use waitForEvent. But, just in case, there is a way to retrieve one event at a time.

getEvent retrieves an event from the event monitor, or undef if no event is ready.

See "Win32::ActAcc::Event".

dropHistory

$eh->dropHistory(2000); # drop all but the most recent 2 seconds' events

clear

$eh->clear();

Erases the backlog of events on the event monitor.

synch

synch() lets you bookmark an EventMonitor and return later to the bookmarked point.

$eh1->synch($eh2);

"Synchronizes" $eh1 with $eh2 by setting $eh1's event-buffer cursor to $eh2's, so that $eh1->getEvent() will return the same event as $eh2->getEvent(). synch() can move the monitor forward or backward; in other words, it can both advance and rewind. (But, when rewinding, watch out for buffer overrun. The spot you rewind to, may have been re-used since the time the event was written that you think you are rewinding to.)

isActive

$a = $eh->isActive();

Returns a true value if the event monitor is active, a false value if it is latent.

activate

$eh->activate(1); # activate
$eh->activate(0); # deactivate

Activating a monitor makes it "catch up" with all events received so far, and makes it sensitive to future events. Activating an already-active monitor has no effect on it.

Deactivating a monitor makes it useless, until it is reactivated.

getEventCount

my $ec = $eh->getEventCount();

Returns a cumulative total number of events caught by the event hook installed by Win32::ActAcc. (All EventMonitor objects feed from this same event hook.)

debug_spin

This debug function displays the EventMonitor's events for a certain number of seconds.

$eh->debug_spin(60);

Win32::ActAcc::Event

An event is an object of type Win32::ActAcc::Event. It's a hash with fields as described in the API documentation:

event
hwnd
idObject
idChild
dwmsEventTime

$e = $eh->getEvent();
print $$e{'event'} . "\n";

``Event'' is a constant (see "EVENT Constants"). You can test it like this:

next unless EVENT_OBJECT_VALUECHANGE()==$$e{'event'};

getAO

$accObj = $e->getAO();

Returns the accessible object that the event pertains to.

Active Accessibility weirdness note: getAO sometimes dies with an access-violation error. You may want to put your calls to getAO into an eval.

AccessibleObjectFromEvent

Same as getAO.

evDescribe

print $e->evDescribe() . "\n";

Good for debugging - returns some information about the event.

EventConstantName

Returns the C constant name for a WinEvent number defined in WinAble.h. See "EVENT Constants".

AccessibleObjectFromEvent

Obtain an "accessible object" from information in a WinEvent. (You may prefer to use the object-oriented $e->"getAO"() way instead.)

my $accObj = AccessibleObjectFromEvent($$e{'hwnd'}, $$e{'idObject'}, $$e{'idChild'});

Tuneable Parameters

$Win32::ActAcc::MenuItem::BetweenClicksToCloseThenOpen = 0.25; # seconds
$Win32::ActAcc::MenuItem::MenuPopupRetrospective = 750; # milliseconds
$Win32::ActAcc::MenuItem::MenuStartTimeout = 3; # seconds
$Win32::ActAcc::MenuIterator::HoverDwell = 0.25; # seconds

Windows Shell

StartButton

StartButton obtains an "accessible object" representing the Start button:

$ao = Win32::ActAcc::StartButton();

StartButtonMenu

use Win32::ActAcc::Shell2000;
#
Win32::OLE->Initialize();
$menu = Win32::ActAcc::Shell2000::StartButtonMenu();
Win32::ActAcc::clearEvents();
$menu->menuPick([ qr/^Programs/, qr/Accessories/i, qr/Notepad/i ]);

StartButtonMenu clicks the Start button, waits for a menu to come up, and returns the menu object.

$ao = Win32::ActAcc::StartButtonMenu();

For working with menus, see "Win32::ActAcc::Menubar".

Tools

aaDigger.pl

aaDigger lets you navigate the hierarchy of accessible objects, rooted at the Desktop window. With aaDigger, you can see the world the way ActAcc sees it -- which can be quite helpful when you are trying to write a Perl script. aaDigger has its own manpage.

You can invoke aaDigger from within a script, in case the script is getting to a certain point ok but you need to figure out what it should do next:

use Win32::ActAcc::aaExplorer;  

my $ao = ...
Win32::ActAcc::aaExplorer::aaExplore($ao);

You can also invoke aaDigger's "tree" feature from within a script:

$ao->debug_tree();

aaEvents.pl

aaEvents makes a continuous display of your system's WinEvents. When you're casting about for clues about which event your script should be keying to a real-world occurrence, aaEvents can help you make up your mind.

You can invoke aaEvents from within a script, in case the script is getting to a certain point ok but you need to figure out what it should do next:

# you must already have $eh: eg, $eh = Win32::ActAcc::createEventMonitor(1);
$eh->debug_spin(60);  # look at the events for 60 seconds

aaWhereAmI.pl

aaWhereAmI continuously describes the accessible object under the cursor at any given moment.

Active Accessibility weirdness note: Not all accessible objects have a place in the hierarchy that has Desktop at its root. You'll discover some things with aaWhereAmI that you might not find any other way.

BUGS, LIMITATIONS, AND SHORTCOMINGS

menuPick can't choose commands that are helpfully hidden because you have not used them recently, nor commands that you'd have to scroll to expose.

You can't use an "accessible object" with Win32::OLE. Especially with Microsoft Office, it would be nice to get a "native object model" IDispatch* from AccessibleObjectFromWindow, and hand it off to Win32::OLE to make Office-specific OLE Automation method calls.

There's no overrun indicator on an EventMonitor. You can't tell it to ignore mouse-move events - not even redundant or superseded ones.

Win32::ActAcc probably doesn't work multi-threaded.

EventMonitors install in-proc hooks. Maybe that's why they don't pick up any events from Command Prompt windows.

Win32::ActAcc doesn't do Unicode. If you run aaDigger.pl and point it to the Russian version of Windows Media Player, the text all comes back as question marks.

Event monitors require Perl scripts to use a Giant Switch technique that gets ugly. A callback veneer should be applied on top.

There's no "brake pedal" for those awkward moments when you want to stop or pause the script, but it keeps tearing the mouse away from you. (You can generally derail a script with the Secure Attention Sequence Ctrl+Alt+Del, since Win32::ActAcc can't operate the logon desktop.)

A single IAccessible* + object ID may be referenced by innumerable Win32::ActAcc::AO objects.

The Perl thread isn't shielded against IAccessible* calls crashing or returning nonsense data causing the Perl thread to crash. (It's surprising how seldom this seems to happen!, but someday we should shore up the defenses.)

INSTALLATION

You can install Win32::ActAcc by compiling it from source code, or, if you have ActivePerl, you can install it using PPM, in which case you don't need a C++ compiler.

Installation from source code

perl makefile.pl
nmake 
nmake install
nmake test

Yes, you have to install it before you test it. Otherwise it can't find its DLL. Probably someone will figure out how to fix this.

The test is tenuous. It will probably work only with Windows 2000, since the design and operation of Notepad changes subtly from version to version. You must have Win32::GuiTest 0.6 or later.

Prerequisites:

  • You may need Visual C++ 6.0 SP 4. The C/C++ part of Win32::ActAcc might not be portable to other compilers.

  • You need the July 2000 "Platform SDK". Earlier versions of the Active Accessibility SDK could give problems compiling.

  • The test suite requires Notepad.exe and Explorer.exe on the path. Also, it requires Win32::GuiTest.

  • The Eliza-AOLInstantMessenger sample requires up-to-date HTML parsing modules. It will tell you if yours are missing or out-of-date.

Installation for ActivePerl users (PPM)

ActivePerl users can install Win32::ActAcc using PPM.

  1. Unzip the zip (Win32-ActAcc-n.n.zip). Make sure your unzip program preserved the directory tree: for example, you should see Win32-ActAcc.tar.gz in an "x86" subdirectory under the directory that contains ActAcc.html (the documentation).

  2. Open a command prompt window.

  3. In the command prompt, "cd" to the directory that contains ActAcc.html.

  4. In the command prompt, issue the following command.

    ppm install --location=. Win32-ActAcc

To check the installation, you may try aaDigger.pl. The test suite (nmake test) doesn't seem to work if you do the ppm installation.

Files Installed

  • In bin: aaDigger.bat and aaDigger.pl, aaEvents.bat and aaEvents.pl, aaWhereAmI.bat and aaWhereAmI.pl

  • In site\lib\Win32: aaDigger.pl, aaEvents.pl, aaWhereAmI.pl, ActAcc.pm, ActAcc.pod

    In site\lib\Win32\ActAcc: aaExplorer.pm

  • In site\lib\auto\Win32\ActAcc: ActAcc.dll, ActAccEM.dll

APPENDIX - CONSTANTS

These are the standard Active Accessibility "constants", made accessible to Perl scripts. Use Win32::ActAcc constants as though they were functions:

die unless (0 == CHILDID_SELF());

EVENT constants

EVENT_SYSTEM_SOUND, EVENT_SYSTEM_ALERT, EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MENUSTART, EVENT_SYSTEM_MENUEND, EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPEND, EVENT_SYSTEM_CAPTURESTART, EVENT_SYSTEM_CAPTUREEND, EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, EVENT_SYSTEM_CONTEXTHELPSTART, EVENT_SYSTEM_CONTEXTHELPEND, EVENT_SYSTEM_DRAGDROPSTART, EVENT_SYSTEM_DRAGDROPEND, EVENT_SYSTEM_DIALOGSTART, EVENT_SYSTEM_DIALOGEND, EVENT_SYSTEM_SCROLLINGSTART, EVENT_SYSTEM_SCROLLINGEND, EVENT_SYSTEM_SWITCHSTART, EVENT_SYSTEM_SWITCHEND, EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZEEND, EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, EVENT_OBJECT_REORDER, EVENT_OBJECT_FOCUS, EVENT_OBJECT_SELECTION, EVENT_OBJECT_SELECTIONADD, EVENT_OBJECT_SELECTIONREMOVE, EVENT_OBJECT_SELECTIONWITHIN, EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_DESCRIPTIONCHANGE, EVENT_OBJECT_VALUECHANGE, EVENT_OBJECT_PARENTCHANGE, EVENT_OBJECT_HELPCHANGE, EVENT_OBJECT_DEFACTIONCHANGE, EVENT_OBJECT_ACCELERATORCHANGE

OBJID constants

OBJID_WINDOW, OBJID_SYSMENU, OBJID_TITLEBAR, OBJID_MENU, OBJID_CLIENT, OBJID_VSCROLL, OBJID_HSCROLL, OBJID_SIZEGRIP, OBJID_CARET, OBJID_CURSOR, OBJID_ALERT, OBJID_SOUND

STATE constants

STATE_SYSTEM_NORMAL, STATE_SYSTEM_UNAVAILABLE, STATE_SYSTEM_SELECTED, STATE_SYSTEM_FOCUSED, STATE_SYSTEM_PRESSED, STATE_SYSTEM_CHECKED, STATE_SYSTEM_MIXED, STATE_SYSTEM_INDETERMINATE, STATE_SYSTEM_READONLY, STATE_SYSTEM_HOTTRACKED, STATE_SYSTEM_DEFAULT, STATE_SYSTEM_EXPANDED, STATE_SYSTEM_COLLAPSED, STATE_SYSTEM_BUSY, STATE_SYSTEM_FLOATING, STATE_SYSTEM_MARQUEED, STATE_SYSTEM_ANIMATED, STATE_SYSTEM_INVISIBLE, STATE_SYSTEM_OFFSCREEN, STATE_SYSTEM_SIZEABLE, STATE_SYSTEM_MOVEABLE, STATE_SYSTEM_SELFVOICING, STATE_SYSTEM_FOCUSABLE, STATE_SYSTEM_SELECTABLE, STATE_SYSTEM_LINKED, STATE_SYSTEM_TRAVERSED, STATE_SYSTEM_MULTISELECTABLE, STATE_SYSTEM_EXTSELECTABLE, STATE_SYSTEM_ALERT_LOW, STATE_SYSTEM_ALERT_MEDIUM, STATE_SYSTEM_ALERT_HIGH, STATE_SYSTEM_PROTECTED, STATE_SYSTEM_VALID

ROLE constants

ROLE_SYSTEM_TITLEBAR, ROLE_SYSTEM_MENUBAR, ROLE_SYSTEM_SCROLLBAR, ROLE_SYSTEM_GRIP, ROLE_SYSTEM_SOUND, ROLE_SYSTEM_CURSOR, ROLE_SYSTEM_CARET, ROLE_SYSTEM_ALERT, ROLE_SYSTEM_WINDOW, ROLE_SYSTEM_CLIENT, ROLE_SYSTEM_MENUPOPUP, ROLE_SYSTEM_MENUITEM, ROLE_SYSTEM_TOOLTIP, ROLE_SYSTEM_APPLICATION, ROLE_SYSTEM_DOCUMENT, ROLE_SYSTEM_PANE, ROLE_SYSTEM_CHART, ROLE_SYSTEM_DIALOG, ROLE_SYSTEM_BORDER, ROLE_SYSTEM_GROUPING, ROLE_SYSTEM_SEPARATOR, ROLE_SYSTEM_TOOLBAR, ROLE_SYSTEM_STATUSBAR, ROLE_SYSTEM_TABLE, ROLE_SYSTEM_COLUMNHEADER, ROLE_SYSTEM_ROWHEADER, ROLE_SYSTEM_COLUMN, ROLE_SYSTEM_ROW, ROLE_SYSTEM_CELL, ROLE_SYSTEM_LINK, ROLE_SYSTEM_HELPBALLOON, ROLE_SYSTEM_CHARACTER, ROLE_SYSTEM_LIST, ROLE_SYSTEM_LISTITEM, ROLE_SYSTEM_OUTLINE, ROLE_SYSTEM_OUTLINEITEM, ROLE_SYSTEM_PAGETAB, ROLE_SYSTEM_PROPERTYPAGE, ROLE_SYSTEM_INDICATOR, ROLE_SYSTEM_GRAPHIC, ROLE_SYSTEM_STATICTEXT, ROLE_SYSTEM_TEXT, ROLE_SYSTEM_PUSHBUTTON, ROLE_SYSTEM_CHECKBUTTON, ROLE_SYSTEM_RADIOBUTTON, ROLE_SYSTEM_COMBOBOX, ROLE_SYSTEM_DROPLIST, ROLE_SYSTEM_PROGRESSBAR, ROLE_SYSTEM_DIAL, ROLE_SYSTEM_HOTKEYFIELD, ROLE_SYSTEM_SLIDER, ROLE_SYSTEM_SPINBUTTON, ROLE_SYSTEM_DIAGRAM, ROLE_SYSTEM_ANIMATION, ROLE_SYSTEM_EQUATION, ROLE_SYSTEM_BUTTONDROPDOWN, ROLE_SYSTEM_BUTTONMENU, ROLE_SYSTEM_BUTTONDROPDOWNGRID, ROLE_SYSTEM_WHITESPACE, ROLE_SYSTEM_PAGETABLIST, ROLE_SYSTEM_CLOCK

SELFLAG constants

SELFLAG_NONE, SELFLAG_TAKEFOCUS, SELFLAG_TAKESELECTION, SELFLAG_EXTENDSELECTION, SELFLAG_ADDSELECTION, SELFLAG_REMOVESELECTION, SELFLAG_VALID

NAVDIR_MIN, NAVDIR_UP, NAVDIR_DOWN, NAVDIR_LEFT, NAVDIR_RIGHT, NAVDIR_NEXT, NAVDIR_PREVIOUS, NAVDIR_FIRSTCHILD, NAVDIR_LASTCHILD, NAVDIR_MAX

COPYRIGHT

Copyright 2001, Phill Wolf. All rights reserved, including, without limitation, motion-picture rights.

You may use Win32::ActAcc under the terms of the Artistic License, as specified in the README file of the Perl distribution.

AUTHOR

Phill Wolf, pbwolf@cpan.org

SEE ALSO

  • Active Accessibility documentation. As of this writing, it is available on http://msdn.microsoft.com on the "Libraries" page:

    Platform SDK
     User Interface Services
      Accessibility
       Microsoft Active Accessibility
  • Win32::OLE

  • Win32::GuiTest

  • Net::AIM

  • "The automation imperative" section of: Black-box Testing by Boris Beizer. Wiley, 1995.

14 POD Errors

The following errors were encountered while parsing the POD:

Around line 144:

=back doesn't take any parameters, but you said =back 4

Around line 148:

=back doesn't take any parameters, but you said =back 4

Around line 247:

=back doesn't take any parameters, but you said =back 4

Around line 597:

=back doesn't take any parameters, but you said =back 4

Around line 624:

=back doesn't take any parameters, but you said =back 4

Around line 830:

=back doesn't take any parameters, but you said =back 4

Around line 881:

=back doesn't take any parameters, but you said =back 4

Around line 927:

=back doesn't take any parameters, but you said =back 4

Around line 1343:

=back doesn't take any parameters, but you said =back 4

Around line 1377:

=back doesn't take any parameters, but you said =back 4

Around line 1656:

=back doesn't take any parameters, but you said =back 4

Around line 1685:

=back doesn't take any parameters, but you said =back 4

Around line 1709:

=back doesn't take any parameters, but you said =back 4

Around line 1807:

=back doesn't take any parameters, but you said =back 4