The following information is applicable to two or more interfaces.
Error Handling
Memory Management
Special Consideration when using Arrays
Zero and One Based Indexes
Enums
Special Offsets for use in the IAccessibleText and IAccessibleEditableText Methods
Discovery of Interfaces
Changing between Accessible Interfaces
Access to Information about the Application
Child IDs
VARIANTs
IAccessibleHyperlink as subclass of IAccessibleAction
HRESULT values are defined by the Microsoft Win32 API. For more information, refer to
Interpreting HRESULT Values in MSDN.
Note that the S_FALSE return value is considered a non-error value and the SUCCEEDED macro will return TRUE. S_FALSE is used when there is no failure but there was nothing valid to return, e.g. in IAccessible2::attributes when there are no attributes. When S_FALSE is returned [out] pointer types should be NULL and [out] longs should generally be 0, but sometimes -1 is used such as IAccessible2::indexInParent, IAccessibleText::caretOffset, and IAccessibleHypertext::hyperlinkIndex.
Note that for BSTR [out] variables common COM practice is that the server does the SysAllocString and the client does the SysFreeString. Also note that when NULL is returned there is no need for the client to call SysFreeString. Please refer to the documentation for each method for more details regarding error handling.
The following memory management issues should be considered:
- Although [out] BSTR variables are declared by the client, their space is allocated by the server. They need to be freed with SysFreeString by the client at end of life; the same is true when BSTRs are used in structs or arrays which are passed to the server.
- If there is no valid [out] BSTR to return, the server should return S_FALSE and assign NULL to the output, e.g. *theOutBSTR = NULL;.
- COM interfaces need to be referenced with AddRef when used and dereferenced with Release at end of life.
- Single [out] longs, HWNDs, booleans, and structs are declared by the caller and passed by reference. The marshaller does all the memory management.
The following articles may be helpful for understanding memory management issues:
There are several methods which return arrays. It is considered a best practice for the client to allocate and free the arrays. This can be done for
IAccessible2::relations and
IAccessibleRelation::targets. However, due to the coding of the IDL for the remaining methods which return arrays, the server must allocate the array and the client must free the array when no longer needed. These methods are
IAccessible2::extendedStates,
IAccessible2::localizedExtendedStates,
IAccessibleAction::keyBinding,
IAccessibleTable::selectedChildren,
IAccessibleTable::selectedColumns, and
IAccessibleTable::selectedRows. For those methods, the server must allocate both the top level array and any storage associated with it, e.g. for BSTRs. The client must use CoTaskMemFree to free the array and any BSTRs must be freed with SysFreeString.
Also, the IDL for those methods includes an extraneous [in] parameter for the caller to specify the max size of the array. This parameter will be ignored by the COM server.
Unless otherwise specified all offsets and indexes are 0 based.
Note that enums start at 0.
IAccessibleText and
IAccessibleEditableText can use one or more of the following special offset values. They are defined in the
IA2TextSpecialOffsets enum.
In general AT (Assistive Technology) should try
IAccessible2 interfaces, followed by using the MSAA interfaces. (In cases where the an application is known to have custom interfaces which provide information not supplied by
IAccessible2 or MSAA, then those custom interfaces can be used.) The AT can then, by default, support unknown IAccessible2/MSAA applications, without the application developers having to request AT vendors for support on an individual application by application basis.
When you have a reference to an IAccessible and require a reference to an IAccessible2 use QueryService as follows:
IServiceProvider *pService = NULL;
hr = pAcc->QueryInterface(IID_IServiceProvider, (void **)&pService);
if(SUCCEEDED(hr)) {
IAccessible2 *pIA2 = NULL;
hr = pService->QueryService(IID_IAccessible, IID_IAccessible2, (void**)&pIA2);
if (SUCCEEDED(hr) && pIA2) {
}
}
Note that developers must always implement MSAA's IAccessible and, if needed, some of the interfaces in the set of
IAccessible2 interfaces. Although the
IAccessible2 IDL is currently coded such that
IAccessible2 is a subclass of MSAA's IAccessible, none of MSAA's IAccessible methods are overridden or extended. In order to allow future removal of the inheritance, Assistive Technologies (ATs) should not rely on that inheritance.
QueryService must be used to switch from a reference to an MSAA IAccessible interface to another interface. This has been documented by Microsoft and the pertinent facts have been extracted below:
- Why use QueryService instead of just using QueryInterface to get IAccessibleEx directly? The reason is that since MSAA 2.0, clients don't talk to a server's IAccessible interface directly; instead they talk to an intermediate MSAA-provided wrapper that calls through to the original IAccessible. This wrapper provides services such as implementing IDispatch, supplying information from MSAA 2.0's Dynamic Annotation service, and scaling locations when running on Windows Vista with DPI scaling enabled. QueryService is the supported way to expose additional interfaces from an existing IAccessible and was originally used by MSHTML to expose IHTMLElement objects corresponding to IAccessibles. QueryService is often more convenient for servers to implement than QueryInterface because it does not have the same requirements for preserving object identity or symmetry/transitivity as QueryInterface, so QueryService allows servers to easily implement the interface on the same object or a separate object. The latter is often hard to do with QueryInterface unless the original object supports aggregation.
Two related references in MSDN are:
Based on this information from Microsoft, QueryService must be used to switch back and forth between a reference to an MSAA IAccessible interface and any of the
IAccessible2 interfaces.
Regarding switching between any of the IAccessible2 interfaces, ATs typically use QueryInterface. This is because it is typical for applications implementing IAccessible2 to implement all IAccessible2 interfaces on a single object. However it is possible that an application could use more than one object to implement the full set of IAccessible2 interfaces and as a result QueryService would have to be used. If your AT is using the IAccessible2 interfaces and you want to allow for access to the widest range of existing and future applications you should use QueryService rather than QueryInterface. If the interface being switched to is implemented on the same object there shouldn't be a noticeable difference in performance and if the interface is implemented on another object the application can transfer the request to the secondary object saving exception handling code in the AT.
Servers implementing
IAccessible2 should provide access to the
IAccessibleApplication interface via QueryService from any object so that ATs can easily determine specific information about the application such as its name or version.
The
IAccessible2 interfaces do not support child IDs, i.e. simple child elements. Full accessible objects must be created for each object that supports
IAccessible2. Therefore MSAA's get_accChild should never return a child ID (other than CHILDID_SELF) for an object that implements any of the
IAccessible2 interfaces.
Microsoft's UI Automation specification has the same limitation and this was resolved in the UI Automation Express specification by adding IAccessibleEx::GetObjectForChild and IAccessibleEx::GetIAccessiblePair. These methods allow mapping back and forth between an IAccessibleEx and an {IAccessible, Child ID} pair. A future version of IAccessible2 may include similar methods to map back and forth between an IAccessible2 and an {IAccessible, Child ID} pair.
Some methods return a VARIANT. Implementers need to make sure that the return type is specified, i.e. VT_I4, VT_IDISPATCH, etc. The methods that return VARIANTs are
IAccessibleHyperlink::anchor,
IAccessibleHyperlink::anchorTarget,
IAccessibleValue::currentValue,
IAccessibleValue::maximumValue,
IAccessibleValue::minimumValue.
In this version of the IDL,
IAccessibleHyperlink is a subclass of
IAccessibleAction. However, there is no practical need for that inheritance and in some cases, such as an image map of smart tags, it doesn't make sense because such an image map doesn't have actionable objects; it's the secondary smart tags that are actionable. As a result, implementations should not rely on the inheritance as it may be removed in a later version of the IDL.