从客户端使用 IAccessibleEx

本主题说明客户端如何访问服务器的 IAccessibleEx 实现,并使用它来获取 UI 元素的UI 自动化属性和控制模式。

本节中的过程和示例假定 IAccessible 客户端已进程内,以及现有的 Microsoft Active Accessibility 服务器。 他们还假设客户端已使用 AccessibilitObjectFromEvent、AccessibleObjectFromPointAccessibleObjectFromWindow 等辅助功能框架函数之一获取 IAccessible 对象。

从 IAccessible 接口获取 IAccessibleEx 接口

具有可访问对象的 IAccessible 接口的客户端可以通过以下步骤获取相应的 IAccessibleEx 接口:

处理子 ID

必须为具有除 CHILDID_SELF 以外的子 ID 的服务器准备客户端。 从 IAccessible 获取 IAccessibleEx 接口后,如果子 ID 未CHILDID_SELF (指示父对象) ,客户端必须调用 IAccessibleEx::GetObjectForChild

以下示例演示如何获取特定 IAccessible 对象和子 ID 的 IAccessibleEx

   
HRESULT GetIAccessibleExFromIAccessible(IAccessible * pAcc, long idChild, 
                                           IAccessibleEx ** ppaex)
{
    *ppaex = NULL;

    // First, get IServiceProvider from the IAccessible.
    IServiceProvider * pSp = NULL;
    HRESULT hr = pAcc->QueryInterface(IID_IServiceProvider, (void **) & pSp);
    if(FAILED(hr))
        return hr;
    if(pSp == NULL)
        return E_NOINTERFACE;

    // Next, get the IAccessibleEx for the parent object.
    IAccessibleEx * paex = NULL;
    hr = pSp->QueryService(__uuidof(IAccessibleEx), __uuidof(IAccessibleEx),
                                                                 (void **)&paex);
    pSp->Release();
    if(FAILED(hr))
        return hr;
    if(paex == NULL)
        return E_NOINTERFACE;

    // If this is for CHILDID_SELF, we're done. Otherwise, we have a child ID and 
    // can request the object for child.
    if(idChild == CHILDID_SELF)
    {
        *ppaex = paex;
        return S_OK;
    }
    else
    {
        // Get the IAccessibleEx for the specified child.
        IAccessibleEx * paexChild = NULL;
        hr = paex->GetObjectForChild(idChild, &paexChild);
        paex->Release();
        if(FAILED(hr))
            return hr;
        if(paexChild == NULL)
            return E_NOINTERFACE;
        *ppaex = paexChild;
        return S_OK;
    }
}

获取 IRawElementProviderSimple 接口

如果客户端具有 IAccessibleEx 接口,可以使用 QueryInterface 访问 IRawElementProviderSimple 接口,如以下示例所示。

HRESULT GetIRawElementProviderFromIAccessible(IAccessible * pAcc, long idChild,
                                                 IRawElementProviderSimple ** ppEl)
{
    * ppEl = NULL;

    // First, get the IAccessibleEx for the IAccessible and child ID pair.
    IAccessibleEx * paex;
    HRESULT hr = GetIAccessibleExFromIAccessible( pAcc, idChild, &paex );
    if(FAILED(hr))
        return hr;

    // Next, use QueryInterface.
    hr = paex->QueryInterface(__uuidof(IRawElementProviderSimple), (void **)ppEl);
    paex->Release();
    return hr;
}

检索控件模式

如果客户端有权访问 IRawElementProviderSimple 接口,则可以检索由提供程序实现的控制模式接口,然后可以在这些接口上调用方法。 以下示例演示如何执行此操作。

// Helper function to get a pattern interface from an IAccessible and child ID 
// pair. Gets the IAccessibleEx, then calls GetPatternObject and QueryInterface.
HRESULT GetPatternFromIAccessible(IAccessible * pAcc, long idChild,
                                     PATTERNID patternId, REFIID iid, void ** ppv)
{
    // First, get the IAccesibleEx for this IAccessible and child ID pair.
    IRawElementProviderSimple * pel;
    HRESULT hr = GetIRawElementProviderSimpleFromIAccessible(pAcc, idChild, &pel);
    if(FAILED(hr))
        return hr;
    if(pel == NULL)
        return E_NOINTERFACE;

    // Now get the pattern object.
    IUnknown * pPatternObject = NULL;
    hr = pel->GetPatternProvider(patternId, &pPatternObject);
    pel->Release();
    if(FAILED(hr))
        return hr;
    if(pPatternObject == NULL)
        return E_NOINTERFACE;

    // Finally, use QueryInterface to get the correct interface type.
    hr = pPatternObject->QueryInterface(iid, ppv);
    pPatternObject->Release();
    if(*ppv == NULL)
        return E_NOINTERFACE;
    return hr;
}

HRESULT CallInvokePatternMethod(IAccessible * pAcc, long idChild)
{
    IInvokeProvider * pPattern;
    HRESULT hr = GetPatternFromIAccessible(pAcc, varChild,
                                  UIA_InvokePatternId, __uuidof(IInvokeProvider),
                                  (void **)&pPattern);
    if(FAILED(hr))
        return hr;

    hr = pPattern->Invoke();
    pPattern->Release();
    return hr;
}

检索属性值

如果客户端有权访问 IRawElementProviderSimple,则可以检索属性值。 以下示例演示如何获取 AutomationId 和 LabeledBy Microsoft UI 自动化 属性的值。

#include <initguid.h>
#include <uiautomationcoreapi.h> // Includes the UI Automation property GUID definitions.
#include <uiautomationcoreids.h> // Includes definitions of pattern/property IDs.

// Assume we already have a IRawElementProviderSimple * pEl.

VARIANT varValue;

// Get AutomationId property:
varValue.vt = VT_EMPTY;
HRESULT hr = pEl->GetPropertyValue(UIA_AutomationIdPropertyId, &varValue);
if(SUCCEEDED(hr))
{
    if(varValue.vt == VT_BSTR)
    {
        // AutomationId is varValue.bstrVal.
    }
    VariantClear(&varValue);
}


// Get LabeledBy property:
varValue.vt = VT_EMPTY;
hr = pEl->GetPropertyValue(UIA_LabeledByPropertyId, &varValue);
if(SUCCEEDED(hr))
{
    if(varValue.vt == VT_UNKNOWN || varValue.punkVal != NULL)
    {
        // Use QueryInterface to get IRawElementProviderSimple.
        IRawElementProviderSimple * pElLabel = NULL;
        hr = varValue.punkVal->QueryInterface(__uuidof(IRawElementProviderSimple),
                                              (void**)& pElLabel);
        if (SUCCEEDED(hr))
        {
            if(pElLabel != NULL)
            {
            // Use the pElLabel pointer here.
            pElLabel ->Release();
            }
        }
    }
    VariantClear(&varValue);
}

前面的示例适用于与控件模式不关联的属性。 若要访问控制模式属性,客户端必须获取和使用控制模式接口。

从 IRawElementProviderSimple 接口检索 IAccessible 接口

如果客户端获取 UI 元素的 IRawElementProviderSimple 接口,客户端可以使用该接口获取元素的相应 IAccessible 接口。 如果客户端需要访问元素的 Microsoft Active Accessibility 属性,则这非常有用。

例如,客户端可以通过调用具有 UIA_LabeledByPropertyId) 的 IRawElementProviderSimple::GetPropertyValue 或调用所选元素的 IRawElementProviderSimple 接口,或调用 ISelectionProvider::GetSelection 来 (检索所选元素的 IRawElementProviderSimple 接口数组,从而获取 IRawElementProviderSimple 接口的 (属性值,例如,客户端可以获取 IRawElementProviderSimple 接口) 。 获取 IRawElementProviderSimple 接口后,客户端可以通过以下步骤获取相应的 IAccessible

以下代码片段演示如何从以前获取的 IRawElementProviderSimple 接口获取 IAccessible 接口。

// IRawElementProviderSimple * pVal - an element returned by a property or method
// from another IRawElementProviderSimple.

IAccessible * pAcc = NULL;
long idChild;

// First, try to use QueryInterface to get the IAccessibleEx interface.
IAccessibleEx * pAccEx;
HRESULT hr = pVal->QueryInterface(__uuidof(IAccessibleEx), (void**)&pAccEx);
if (SUCCEEDED(hr)
{
    if (!pAccEx)
    {
        // If QueryInterface fails, and the IRawElementProviderSimple was 
              // obtained as a property or return value from another 
              // IRawElementProviderSimple, pass it to the 
              // IAccessibleEx::ConvertReturnedValue method of the
        // originating element.

        pAccExOrig->ConvertReturnedElement(pVal, &pAccEx);
    }

    if (pAccEx)
    {
        // Call GetIAccessiblePair to get an IAccessible interface and 
              // child ID.
        pAccEx->GetIAccessiblePair(&pAcc, &idChild);
    }

    // Finally, use the IAccessible interface and child ID.
    if (pAcc)
    {
        // Use IAccessible methods to get further information about this UI
              // element, or pass it to existing code that works in terms of 
              // IAccessible.
        ...
    }
}