I am trying out accessing the information in (Windows only) Firefox / Thunderbird using the IAccessible2 API: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2 However I'm failing at almost the first step. I can obtain the IAccessible interface for either application, and then the IServiceProvider interface. But when I call QueryService to obtain the IAccessible2 interface, as described here: http://accessibility.linuxfoundation.org/a11yspecs/ia2/docs/html/_generalinfo.html#_dicoveringInterfaces it always returns E_INVALIDARG.
AccProbe successfully returns IA2 information for both applications. The MS documentation for QueryService doesn't list E_INVALIDARG as a possible return value. However browsing for the mozilla source it appears that it returns this if the first parameter (service ID) is unexpected, and otherwise calls through to QueryInterface (which would return E_NOINTERFACE for a bad interface ID). So... that implies the first parameter to QueryService is wrong; but I've tried pretty much every value I think mozilla expects with no difference.
I started off in c# and then tried again in c++ in case I was losing something in InterOp. Same result in both languages. Here's my current c++ test code:
HWND hw = GetForegroundWindow();
IAccessible *pIA;
HRESULT hr = AccessibleObjectFromWindow(hw, OBJID_WINDOW, IID_IAccessible, (void**)&pIA);
if (!SUCCEEDED(hr))
return -1;
// SNIP - calls pIA->get_accName to check correct window is being accessed. This works OK.
const IID IID_IAccessible2 = {0xE89F726E, 0xC4F4, 0x4c19, 0xbb, 0x19, 0xb6, 0x47, 0xd7, 0xfa, 0x84, 0x78};
::IServiceProvider *pService = NULL;
hr = pIA->QueryInterface(IID_IServiceProvider, (void **)&pService);
if(SUCCEEDED(hr))
{
IAccessible2 *pIA2 = NULL;
hr = pService->QueryService(IID_IAccessible2, IID_IAccessible2, (void**)&pIA2);
if (SUCCEEDED(hr) && pIA2)
{
// Always fails with E_INVALIDARG
pIA2->Release();
}
pService->Release();
}
This is all on Win7 - both 32-bit and 64-bit used. Firefox 3.6.24 and Thunderbird 8.0. Visual Studio 2005
Am I doing something wrong?
HRESULT hr = AccessibleObjectFromWindow(hw, OBJID_WINDOW, IID_IAccessible, (void**)&pIA);
if (!SUCCEEDED(hr))
return -1;
I think the problem is here - replace OBJID_WINDOW with OBJID_CLIENT, and it seems to work. (I don't have the typelib registered, but I can QS for IID_IAccessible and the IUnknown interface, and it seems to work. Also, be sure to CoInitialize() also.)
IAccessible has an interesting hierarchy: every HWND has both a 'window' part and a 'client' part. This is partially due to how Win32 works internally; a Win32 HWND can have items like titlebar, menu, scrollbars, and so on, that all share the same HWND - along with the content area aka client area where the actual control content lives. To allow these items to have their own representation, the designers of MSAA picked a two-level structure, where OBJID_WINDOW represents the entire window, and it has children that then represent things like scrollbars, menubar, titlebar, and so on - if they are present.
The part of a window that implements the accessiblity, however, is generally the client part, so you usually need to ask for OBJID_CLIENT to get the 'real' IAccessible.