如何使用UI 自动化使无窗口 ActiveX 控件易于访问

介绍如何使用 Microsoft UI 自动化 API 来确保辅助技术 (AT) 客户端应用程序可以访问无窗口 Microsoft ActiveX 控件。

需要了解的事项

技术

先决条件

  • C/C++
  • Microsoft Win32 和组件对象模型 (COM) 编程
  • 无窗口 ActiveX 控件
  • UI 自动化提供程序

Instructions

步骤 1:实现UI 自动化提供程序接口。

若要使应用程序可访问,必须为无窗口 ActiveX 控件实现UI 自动化提供程序接口,包括 IRawElementProviderSimpleIRawElementProviderFragmentIRawElementProviderFragmentRootIRawElementProviderAdviseEvents。 应像实现基于窗口的控件一样实现这些接口,但以下步骤中所述除外。 有关实现 UIA 提供程序接口的详细信息,请参阅 UI 自动化 提供程序程序员指南

步骤 2:实现 IServiceProvider 接口。

当客户端需要有关无窗口控件的辅助功能信息时,控件容器会调用控件的 IServiceProvider::QueryService 方法来检索控件的 IRawElementProviderSimple 接口指针。

以下示例演示如何实现 QueryService 方法。

STDMETHODIMP CMyAccessibleUIAControl::QueryService(REFGUID guidService,
        REFIID riid, void **ppvObject)
{  
    if (ppvObject == NULL)
    {
        return E_INVALIDARG;
    }

    *ppvObject = NULL;  
    HRESULT hr = E_FAIL; 
 
    if (guidService == __uuidof(IRawElementProviderSimple))
    {  
        hr = QueryInterface(riid, ppvObject);  
    }  
    return hr;  
}

步骤 3:实现 IRawElementProviderFragment::Navigate 方法。

当调用无窗口控件的 IRawElementProviderFragment::Navigate 方法导航到无窗口控件的根提供程序的父级或同级时, Navigate 方法应委托给控件容器的 IRawElementProviderWindowlessSite::GetAdjacentFragment 方法。

以下示例演示如何实现 Navigate 方法。

STDMETHODIMP CMyAccessibleUIAControl::Navigate(NavigateDirection direction,
     IRawElementProviderFragment **ppRetVal) 
{   
    if (ppRetVal == NULL)
    {
        return E_INVALIDARG;
    }

    *ppRetVal = NULL;  
    HRESULT hr = E_FAIL;
    IRawElementProviderWindowlessSite *pWindowlessSite = NULL;  
    
    if (direction == NavigateDirection_Parent)  
    {  
        // Query the control container's windowless site 
        // for the parent.
         if (SUCCEEDED(m_pClientSite->QueryInterface(
                IID_PPV_ARGS(&pWindowlessSite))))  
        {  
            hr =  pWindowlessSite->GetAdjacentFragment(direction, ppRetVal);  
        }  
    }  

    else if (direction == NavigateDirection_FirstChild)  
    {  
        // GetFragmentForChild is an application-defined function that 
        // retrieves the first or last child fragment.
        hr =  GetFragmentForChild(FIRST, ppRetVal);  
    }  

    else if (direction == NavigateDirection_LastChild)  
    {  
        hr = GetFragmentForChild(LAST, ppRetVal);  
    }  

    SafeRelease(&pWindowlessSite);
    return S_OK;   
}

步骤 4:实现 IRawElementProviderFragment::GetRuntimeId 方法。

当无窗口控件收到对其 IRawElementProviderFragment::GetRuntimeId 方法的调用时,该控件必须执行以下操作:

  1. 通过调用控件站点的 IRawElementProviderWindowlessSite::GetRuntimeIdPrefix 方法检索运行时 ID 前缀。
  2. 通过将整数追加到运行时 ID 前缀,为控件创建唯一的运行时 ID。
  3. 将运行时 ID 返回给调用方。

以下示例演示如何实现 GetRuntimeId 方法。

STDMETHODIMP CMyAccessibleUIAControl::GetRuntimeId(SAFEARRAY **ppRetVal)  
{   
    if (ppRetVal == NULL)
    {
        return E_INVALIDARG;
    }

    *ppRetVal = NULL;  
    HRESULT hr = E_FAIL;
    IRawElementProviderWindowlessSite *pWindowlessSite = NULL;  

    if (SUCCEEDED(m_pClientSite->QueryInterface(IID_PPV_ARGS(&pWindowlessSite))))  
    {  
        // Create a safe array to hold runtime ID.
        SAFEARRAY *psa = SafeArrayCreateVector(VT_I4, 1, 3);  
        if (psa == NULL)
        {
            hr = E_OUTOFMEMORY;
        }

        // Retrieve the runtime ID prefix from the control container. The prefix
        // consists of UiaAppendRuntimeId followed by the windowless site ID.
        if (SUCCEEDED(hr))
        {    
            hr = pWindowlessSite->GetRuntimeIdPrefix(&psa);  
        } 

        if (SUCCEEDED(hr))
        {
        // Append this fragment's ID to the retrieved runtime ID prefix.
            long i = 2;
            hr = SafeArrayPutElement(psa, &i, (void*)&m_Id);        
        }

        if (SUCCEEDED(hr))
        {
            *ppRetVal = psa;  
        }
    }

    SafeRelease(&pWindowlessSite);
    return hr;  
}

使用 MSAA 使无窗口 ActiveX 控件易于访问

无窗口 ActiveX 控件辅助功能