The Server sample application (Server.exe) was written purposely to be inaccessible. It does, however, expose the Active Accessibility interface to allow a client to operate it. It takes left mouse clicks to select an object from the dialog box's choices of icons. There are no menu choices. If the #define KEYBOARD_SUPPORT is used, then the left and right arrows are enabled, along with the SPACEBAR.
Note: A server application developer should have what parent-child reciprocity : While Parent A's child is Child X (obtained by ::accNavigate NAVDIR_FIRSTCHILD or NAVDIR_LASTCHILD, or ::accChild), and while ::accParent on Child X is Parent B, Parent A is expected to equal Parent B.
Server application developers must remember to eliminate sending redundant messages to client applications, because this is a major performance issue If a control is constantly being updated, a EVENT_OBJECT_VALUECHANGE or EVENT_OBJECT_STATECHANGE should only be sent if the value or state is different that the previous value. For example, if text field repaints every second, the EVENT_OBJECT_VALUECHANGE should only be sent when the actual contents change, not each second.
In addition to Server's executable file, the SDK includes the sample's source code, header files, and Microsoft® Visual C++® project workspace files. This tutorial sequentially discusses the tasks that Server performs and, in doing so, presents the basic information you need to develop your own Active Accessibility server application.
The tutorial contains the following steps.
Additionally, the following topic contains background information.
Server is a simple application that provides information about accessible objects and returns the information as an LRESULT. Server uses Active Accessibility technology. This sample uses only the mouse to select accessible objects.
Before you can start using Active Accessibility's features, you must initialize the Component Object Model (COM) library by calling the OleInitialize(NULL) OLE function. Server calls this function before it can receive WM_GETOBJECT messages.
// Attempt to initialize OLE. OleInitialize(NULL);
After initializing the COM library, the server will idle until it has a WM_GETOBJECT message to process. The object, if found, is returned in the LRESULT of the message; otherwise NULL is returned.
MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_GETOBJECT: if (fShuttingDown) return(NULL); return(GetObjectPointer(hwnd, wParam, lParam)); }
When your server no longer needs COM services, use the OleUninitialize(); function to close the COM library. Closing the COM library is required by COM standards; failing to do so will result in memory leaks.
// Attempt to close OLE. OleUninitialize();
The Name property of an OUTLINEITEM is the string displayed, or the name by which an item would be referred. (Or, we could use Value for this...an open issue.)
In an OUTLINE object, indentation levels are represented by making the indented item's children of the item one level higher. Thus, enumerating the children of an OUTLINE object yields only the first-level OUTLINEITEMs. Enumerating the children of a first-level object reveals the second-level objects grouped underneath it. Note that you cannot determine an item's indent level directly, you must walk up the chain of parents until you reach an OUTLINE object.
In some cases an outline might skip one or more levels. In that case an invisible OUTLINEITEM object must be created to represent each level that has no items.
Alternatively, we could state that the accValue (or accName...to be decided) of an outlineitem object is its indentation level. That would remove the need for invisible placeholder objects when indentation levels are skipped. Note, however, that because levels can be skipped, a client cannot assume that the indentation levels alone convey the parent-child relationships. That is, you cannot assume that a third-level item is actually the child of the second-level item that precedes it.
Also note that an OUTLINEITEM can contain objects that are not additional OUTLINEITEMs. For example, the standard Windows tree view control displays a check box (displaying a plus or minus symbol) to the left of each item that has child items. When this is exposed via OLE Accessibility, the checkbox would be a child of the outline item. Any child of an OUTLINEITEM that is not itself an OUTLINEITEM is assumed to be a component of the item itself. (Alternatively, we could recommend that each compound item be a child of a GROUPING object. In that case, children of OUTLINEITEMs would be limited to other OUTLINEITEMs and GROUPINGs. This arrangement makes icons, check boxes and other decorations peers to the outline item rather its children. There seem to be pros and cons to both approaches.)
If a client wishes to enumerate the contents of an outline in top-to-bottom order, it must recurse through the contents of each child in turn. There is no single list of all the items.
LIST objects are a degenerate case of OUTLINE objects. In essence a LIST is an OUTLINE with only one level, and all LISTITEM objects are children of the LIST object. Clients can be much more efficient when dealing with a LIST, because they can enumerate the contents using accChild or accNavigate with NAVDIR_NEXT.
There is no need for recursive code. However, as with OUTLINEITEMs, LISTITEM objects can have children such as check boxes and icons that are actually a part of a complex item's visual presentation.
Clients should also keep in mind that LIST and OUTLINE objects are not limited to conventional, one-dimensional displays with the root at the top. Applications have complete freedom of visual presentation. In fact, an OUTINE object can be displayed much like a flow chart, with items scattered seemingly without pattern. Therefore clients should always rely on the accLocation property of each item rather than assuming a constant direction or visual style.
When an object has several regions of text that actually present a single text-stream, such as text broken into multiple columns, it should expose this relationship to accessibility aids through the use of NAVDIR_NEXT and NAVDIR_PREVIOUS ordering. In addition, it should set the STATE_TEXT_CONTINUED attribute on all but the first region, and STATE_TEXT_CONTINUES attribute on all but the final region.
The STATE_SYSTEM_ANIMATED flag is used to mark an object whose appearance is changing rapidly or constantly. Blind access utilities can use this flag to avoid notifying the user repeatedly for what is really a single series of visual changes.
A special case of this is marquee text; that is, text that is progressively disclosed as it scrolls across a region. Such objects should be given the attribute of STATE_SYSTEM_ANIMATED. In most cases the object's Value property string should reflect the entire text, even the portion that is not currently visible. If the Value string is changed too frequently, a blind access utility might not be able to convey it usefully to the user.
Example: A window contains a rectangular region in that shows the word "Cool!" moving around in a figure-eight pattern. (This can be implemented using AVI files or through other means.) This object's Role property would be GRAPHIC, its Value property would be the string displayed, and its LOCATION would be the bounding rectangle, and it would have the STATE_SYSTEM_ANIMATED attribute flag set. (STATECHANGE events would also be generated when the object started or ceased animating.) Its Description property should also be something like "The word 'Cool!' is moving about the screen in a figure-eight pattern."
Child, Focus, and Selection can all return pointers to objects. This is done using a variant structure, and specifying the variant type as VT_DISPATCH.
Tables are potentially very complicated, but are also an extremely important part of accessibility. They are everywhere, and today they are often inaccessible. We expect to define a new object class in the future to handle complex tables, but the current Active Accessibility architecture is sufficient to handle the vast majority of cases.
A table should be represented as an object with role TABLE. It has children with CELL objects, whose name identifies their locations (such as "A1" or "1996"). As noted elsewhere, appropriate names are a very useful part of this process.
For objects that have screen locations, NAVDIR_NEXT and NAVDIR_PREVIOUS should traverse them in an order that the user will consider 'reasonable'. In most cases that means in a left-to-right, top-to-bottom ordering (in English-speaking countries), but in other cases it could be based on front-to-back or proximity or even alphabetical ordering, depending on the nature of the container.
For objects that do not have defined screen locations, the order is entirely up the application and clients should make no assumptions about this ordering. It is acceptable for non-visible objects, such as GROUPING objects or objects that are only temporarily hidden, to be interspersed with visible objects.
Next and Previous should always visit all visible child belonging to the parent object. Each child should be visited only once. A voice input or macro utility should be able to implement generic next and previous commands that obey those restrictions and so are "well behaved" from the user's perspective. Certainly a Next command that got you into an infinite loop while only visiting two objects in the container would not meet the user's expectations.
By extension, Next and Previous should not loop around. They should fail when attempting to move before the first object or after the last object.
The order used by Navigate is not necessarily the indexing order used with Child.
Order is important: applications should always perform the stages of an alert in the following order:
The same thing is repeated if and when the object is changed to no longer be in the alert state. For example, when the battery indicator icon changes to yellow, its STATE_SYTEM_ALERT_MEDIUM flag is set, and the EVENT_SYSTEM_ALERTLEVELCHANGE message has been sent referencing this icon object. When the indicator changes back to green, its STATE_SYSTEM_ALERT_MEDIUM flag is cleared (so that none of the alert flags are set), and another EVENT_SYSTEM_ALERTLEVELCHANGE message is sent. A client receives this event, checks the icon object's state flags, finds that none are set, and realizes that this notification indicated that the alert level on this object has completed.
It is important to avoid race conditions where a client tries to talk to an object before it is fully initialized or after it is beginning to close down. Active Accessibility containers should not respond to the WM_GETOBJECT message until fully able to respond to requests from the clients. For example, the system will send EVENT_OBJECT_CREATE on the creation of a new window, but before the WM_CREATE message is sent to the window. Since many applications use WM_CREATE to start their initialization process, it would not be wise for the window to respond to the WM_GETOBJECT message until it has processed the WM_CREATE message. Conversely, clients should be aware that the AccessibleObjectFromWindow, AccessibleObjectFromPoint functions might fail if called in response to an EVENT_OBJECT_CREATE. The recommendation is to watch for SHOW and HIDE events rather than CREATE and DESTROY. Objects should be sure to use SHOW and HIDE to bracket their active lifetime.
© 1997 Microsoft Corporation. All rights reserved. Legal Notices.