Using Direct Annotation in C++
Sometimes you will encounter a situation in which the existing accessibility support for an user interface element is almost good enough and you need to just make small changes to the existing support. For example, perhaps you just need to override the Name or Role properties. In these cases, fully implementing the IAccessible interface as described in Implementing a Microsoft Active Accessibility (MSAA) Server Using the Windows API and C++ may seem like a great deal of effort to achieve these improvements.
Fortunately, starting with Windows 7 there is an easier alternative, the Direct Annotation feature of the Dynamic Annotation API. The Dynamic Annotation API is extension to Microsoft Active Accessibility and UI Automation that makes it possible to customize existing IAccessible support without using subclassing or wrapping techniques. Direct Annotation is one of several features provided by the Dynamic Annotation API; it is the simplest of the features provided.
Direct Annotation makes it possible to customize the following MSAA properties.
- Default Action (PROPID_ACC_DEFAULTACTION)
- Description (PROPID_ACC_DESCRIPTION)
- Help (PROPID_ACC_HELP)
- Name (PROPID_ACC_NAME)
- Role (PROPID_ACC_ROLE)
- State (PROPID_ACC_STATE)
- Value (PROPID_ACC_VALUE)
Since Name, Role, Value, and State are included in the list of properties supported by the Direct Annotation feature, this feature is sufficient to meet WCAG Success Criterion 4.1.2: Name, Role, Value.
Direct Annotation also makes it possible to override or add a Microsoft UI Automation property to a control by specifying a UI Automation Property Identifier instead.
Implementation Details
The first step to using the Direct Annotation feature of the Dynamic Annotation API is to create an IAccPropServices object using either the CoCreateInstance function or CoCreateInstanceEx function. Then you can use either the IAccPropServices::SetHwndProp method or the IAccPropServices::SetHwndPropStr method.
The following sample from Using Direct Annotation demonstrates the technique.
::SetAccessibleProperties()
HRESULT CMyTextControl{
// COM is assumed to be initialized...
* pAccPropServices = NULL;
IAccPropServices= CoCreateInstance(CLSID_AccPropServices, nullptr, CLSCTX_SERVER,
HRESULT hr , reinterpret_cast<void**>(&pAccPropServices));
IID_IAccPropServicesif (SUCCEEDED(hr))
{
// Annotating the Role of this object to be STATICTEXT
;
VARIANT var.vt = VT_I4;
var.lVal = ROLE_SYSTEM_STATICTEXT;
var= pAccPropServices->SetHwndProp(_hwnd, OBJID_CLIENT, CHILDID_SELF, PROPID_ACC_ROLE, var);
hr ->Release();
pAccPropServices}
return hr;
}
More complete examples can be found in the following locations.
WndWithCustomizedAccessibleInfo.cpp and WndWithCustomizedAccessibleInfo.h
Implements the
CWndWithCustomizedAccessibleInfo
class, a ATL/MFC class that uses Direct Annotation to customize the accessible info of a window.DirectAnnotation.cpp and DirectAnnotation.h
Implements the
DirectAnnotation
class, a Windows API class that uses Direct Annotation to customize the accessible info of a window.
Note that using the IAccPropServices::SetHwndProp method or the IAccPropServices::SetHwndPropStr method will not send WinEvents. It is your responsibility to send appropriate events by calling the NotifyWinEvent function after the IAccPropServices::SetHwndProp method or the IAccPropServices::SetHwndPropStr method has been called.
For example, if you use IAccPropServices::SetHwndPropStr method to set the Name property of an element, send an EVENT_OBJECT_NAMECHANGE event for that object after SetHwndPropStr returns.
The CWndWithCustomizedAccessibleInfo
class and
DirectAnnotation
class both make the
necessary calls to the NotifyWinEvent
function for you.