注册自定义属性、事件和控件模式

在使用自定义属性、事件或控件模式之前,提供程序和客户端都必须在运行时注册属性、事件或控件模式。 注册在应用程序进程中全局有效,在进程关闭或最后一个 Microsoft UI 自动化 元素对象 (IUIAutomationIRawElementProviderSimple) 在该进程中释放之前,注册仍然有效。

Registation 涉及将 GUID 传递给UI 自动化,以及有关自定义属性、事件或控件模式的详细信息。 尝试使用相同信息再次注册同一 GUID 会成功,但尝试再次注册同一 GUID 但具有不同信息 (例如,不同类型的自定义属性) 将失败。 将来,如果接受自定义规范并将其集成到UI 自动化核心中,UI 自动化将验证自定义注册信息并使用已注册的代码而不是“官方”框架实现,从而最大限度地减少应用程序兼容性问题。 不能删除已注册的属性、事件或控件模式。

本主题包含以下各节:

注册自定义属性和事件

注册自定义属性或事件使提供程序和客户端能够获取属性或事件的 ID,然后将这些 ID 传递给将 ID 作为参数的各种 API 方法。

注册属性或事件:

  1. 为自定义属性或事件定义 GUID。
  2. 使用有关属性或事件的信息填充 UIAutomationPropertyInfoUIAutomationEventInfo 结构,包括 GUID 和包含自定义属性或事件名称的不可本地化的字符串。 自定义属性还要求指定属性的数据类型,例如,无论属性包含整数还是字符串。 数据类型必须是 UIAutomationType 枚举指定的以下类型之一。 自定义属性不支持其他数据类型。
    • UIAutomationType_Bool
    • UIAutomationType_Double
    • UIAutomationType_Element
    • UIAutomationType_Int
    • UIAutomationType_Point
    • UIAutomationType_String
  3. 使用 CoCreateInstance 函数创建 CUIAutomationRegistrar 对象的实例,并检索指向对象的 IUIAutomationRegistrar 接口的指针。
  4. 调用 IUIAutomationRegistrar::RegisterPropertyRegisterEvent 方法,并传递 UIAutomationPropertyInfo 结构或 UIAutomationEventInfo 结构的地址。

IUIAutomationRegistrar::RegisterPropertyRegisterEvent 方法返回一个属性 ID 或事件 ID,应用程序可以将该 ID 或事件 ID 传递给采用此类标识符作为参数的任何UI 自动化方法。 例如,可以将注册的属性 ID 传递给 IUIAutomationElement::GetCurrentPropertyValue 方法或 IUIAutomation::CreatePropertyCondition 方法。

以下示例演示如何注册自定义属性。

// Declare a variable for holding the custom property ID.
PATTERNID g_MyCustomPropertyID;
// Define a GUID for the custom property.
GUID GUID_MyCustomProperty = { 0x82f383ff, 0x4b4d, 0x40d3, 
    { 0x8e, 0xd2, 0x90, 0xb5, 0x25, 0x8e, 0xaa, 0x19 } };

HRESULT RegisterProperty()
{
    // Fill the structure with the GUID, name, and data type.
    UIAutomationPropertyInfo MyCustomPropertyInfo = 
    {
        GUID_MyCustomProperty,
        L"MyCustomProp",
        UIAutomationType_String
    };

    // Create the registrar object and get the IUIAutomationRegistrar 
    // interface pointer. 
    IUIAutomationRegistrar * pUIARegistrar = NULL;
    CoCreateInstance(CLSID_CUIAutomationRegistrar, NULL, CLSCTX_INPROC_SERVER, 
            IID_IUIAutomationRegistrar, (void **)&pUIARegistrar);

    if (pUIARegistrar == NULL)
        return E_NOINTERFACE;

    // Register the property and retrieve the property ID. 
    HRESULT hr = pUIARegistrar->RegisterProperty(&MyCustomPropertyInfo, &g_MyCustomPropertyID);
    pUIARegistrar->Release();

    return hr;
}

IUIAutomationRegistrar::RegisterPropertyRegisterEvent 方法检索的属性和事件标识符仅在检索它们的应用程序的上下文中有效,并且仅在应用程序的生存期内有效。 当通过同一应用程序的不同运行时实例调用同一 GUID 时,注册方法可以为该 GUID 返回不同的整数值。

没有注销自定义属性或事件的方法。 而是在释放最后一个UI 自动化对象时隐式取消注册它们。

重要

如果代码是 Microsoft Active Accessibility (MSAA) 客户端,则必须在更改自定义属性的值时调用 NotifyWinEvent 函数。

 

实现自定义控件模式

自定义控件模式不包含在UI 自动化 API 中,但由第三方在运行时提供。 客户端和提供程序应用程序的开发人员必须协同工作来定义自定义控件模式,包括控件模式将支持的方法、属性和事件。 定义控件模式后,客户端和提供程序都必须实现支持的组件对象模型 (COM) 对象,以及代码以在运行时注册控件模式。 自定义控件模式需要实现两个 COM 对象:客户端包装和模式处理程序。

注意

以下主题中的示例演示了如何实现复制现有 Value 控件模式功能的自定义控件模式。 这些示例仅用于说明目的。 实际的自定义控件模式应提供标准UI 自动化控件模式中不可用的功能。

 

客户端包装和模式处理程序

客户端包装器实现客户端用于检索属性和调用自定义控件模式公开的方法的 API。 API 作为 COM 接口实现,该接口将所有属性请求和方法调用传递给 UI 自动化 核心,然后封送请求和对提供程序的调用。

注册自定义控件模式的代码必须提供UI 自动化可用于创建客户端包装对象的实例的类工厂。 成功注册自定义控件模式后,UI 自动化将返回一个 IUIAutomationPatternInstance 接口指针,客户端使用该指针将属性请求和方法调用转发到UI 自动化核心。

在提供程序端,UI 自动化核心从客户端获取属性请求和方法调用,并将其传递给模式处理程序对象。 然后,模式处理程序在提供程序接口上调用自定义控件模式的适当方法。

注册自定义控件模式的代码将创建模式处理程序对象,在注册控件模式时,为UI 自动化提供指向对象的 IUIAutomationPatternHandler 接口的指针。

下图显示了客户端属性请求或方法调用如何从客户端包装器流经UI 自动化核心组件到模式处理程序,然后流向提供程序接口。

显示从客户端包装器流到模式处理程序和提供程序的示意图

实现客户端包装和模式处理程序接口的对象必须是自由线程的。 此外,UI 自动化核心必须能够直接调用对象,而无需任何中间封送代码。

实现客户端包装器

客户端包装器是一个对象,它公开客户端用于请求属性和调用自定义控件模式支持的方法的 IXxxPattern 接口。 接口由每个受支持的属性的一对“getter”方法组成, (get_CurrentXxx和get_CachedXxx方法) ,以及每个受支持方法的“调用方”方法。 实例化对象时,对象构造函数会收到指向 IUIAutomationPatternInstance 接口的指针,该接口由UI 自动化核心实现。 IXxxPattern 接口的方法使用 IUIAutomationPatternInstance::GetPropertyCallMethod 方法将属性请求和方法调用转发到UI 自动化核心。

以下示例演示如何为支持单个属性的简单自定义控件模式实现客户端包装对象。 有关更复杂的示例,请参阅 自定义控件模式的示例实现

// Define the client interface.
interface __declspec(uuid("c78b266d-b2c0-4e9d-863b-e3f74a721d47"))
IMyCustomPattern : public IUnknown
{
    STDMETHOD (get_CurrentIsReadOnly)(BOOL * pIsReadOnly) = 0;
    STDMETHOD (get_CachedIsReadOnly)(BOOL * pIsReadOnly) = 0;
};

// Implement the client wrapper class.
class CMyValuePatternClientWrapper :
    public IMyCustomPattern
{
    IUIAutomationPatternInstance * _pInstance;
    
public:
    // Get IUIAutomationPatternInstance interface pointer from the
    // UI Automation core.
    CMyValuePatternClientWrapper(IUIAutomationPatternInstance * pInstance)
        : _pInstance(pInstance)
    {
        _pInstance->AddRef();
    }
    
    ~CMyValuePatternClientWrapper()
    {
        _pInstance->Release();
    }

    // Note: Put standard IUnknown implementation here.

    STDMETHODIMP get_CurrentIsReadOnly(BOOL * pIsReadOnly)
    {
        return _pInstance->GetProperty(0, FALSE, UIAutomationType_Bool, pIsReadOnly);
    }

    STDMETHODIMP get_CachedIsReadOnly(BOOL * pIsReadOnly)
    {
        return _pInstance->GetProperty(0, TRUE, UIAutomationType_Bool, pIsReadOnly);
    }
};

实现模式处理程序

模式处理程序是实现 IUIAutomationPatternHandler 接口的对象。 此接口有两种方法: IUIAutomationPatternHandler::CreateClientWrapperDispatchCreateClientWrapper 方法由 UI 自动化 核心调用,并接收指向 IUIAutomationPatternInstance 接口的指针。 CreateClientWrapper 通过实例化客户端包装器对象并将 IUIAutomationPatternInstance 接口指针传递给客户端包装器构造函数来做出响应。

调度方法由UI 自动化核心用于将属性请求和方法调用传递给自定义控件模式的提供程序接口。 参数包括指向提供程序接口的指针、要调用的属性 getter 或方法的从零开始的索引,以及包含要传递给提供程序的参数的 UIAutomationParameter 结构的数组。 模式处理程序的响应方式是检查索引参数以确定要调用的提供程序方法,然后调用该提供程序接口,传递 UIAutomationParameter 结构中包含的参数。

在注册控件模式之前,模式处理程序对象由注册自定义控件模式的同一代码实例化。 代码必须在注册时将模式处理程序对象的 IUIAutomationPatternHandler 接口指针传递到UI 自动化核心。

以下示例演示如何为支持单个属性的简单自定义控件模式实现模式处理程序对象。 有关更复杂的示例,请参阅 自定义控件模式的示例实现

// Define the provider interface.
interface __declspec(uuid("9f5266dd-f0ab-4562-8175-c383abb2569e"))
IMyValueProvider : public IUnknown
{
    STDMETHOD (get_IsReadOnly)(BOOL * pIsReadOnly) = 0;
};            

// Index used by IUIAutomationPatternHandler::Dispatch.
const int MyValue_GetIsReadOnly = 0; 
            
// Define the pattern handler class.        
class CMyValuePatternHandler : public IUIAutomationPatternHandler
{
public:
    
    // Put standard IUnknown implementation here.

    STDMETHODIMP CreateClientWrapper(
            IUIAutomationPatternInstance * pPatternInstance, 
            IUnknown ** pClientWrapper)
    {
        *pClientWrapper = new CMyValuePatternClientWrapper(pPatternInstance);
        if (*pClientWrapper == NULL)
            return E_INVALIDARG;
        return S_OK;
    }
    
    STDMETHODIMP Dispatch (IUnknown * pTarget, UINT index, 
            const struct UIAutomationParameter *pParams, UINT cParams)
    {
        switch(index)
        {
        case MyValue_GetIsReadOnly:
            return ((IMyValueProvider*)pTarget)->get_IsReadOnly((BOOL*)pParams[0].pData);
        }
        return E_INVALIDARG;
    }
};

注册自定义控件模式

自定义控件模式必须先由提供程序和客户端注册,然后才能使用它。 注册为UI 自动化核心提供有关控件模式的详细信息,并为提供程序或客户端提供控件模式 ID 以及控件模式支持的属性和事件的 ID。 在提供程序端,必须在关联的控件处理 WM_GETOBJECT 消息之前或同时注册自定义控件模式。

注册自定义控件模式时,提供程序或客户端会提供以下信息:

  • 自定义控件模式的 GUID。
  • 包含自定义控件模式名称的不可本地化字符串。
  • 支持自定义控件模式的提供程序接口的 GUID。
  • 支持自定义控件模式的客户端接口的 GUID。
  • 描述自定义控件模式支持的属性的 UIAutomationPropertyInfo 结构的数组。 对于每个属性,必须指定 GUID、属性名称和数据类型。
  • 描述自定义控件模式支持的方法的 UIAutomationMethodInfo 结构的数组。 对于每个方法,结构包括以下信息:方法名称、参数计数、参数数据类型列表和参数名称列表。
  • 描述自定义控件模式引发的事件的 UIAutomationEventInfo 结构的数组。 对于每个事件,必须指定 GUID 和事件名称。
  • 使自定义控件模式可供客户端使用的模式处理程序对象的 IUIAutomationPatternHandler 接口的地址。

若要注册自定义控件模式,提供程序或客户端代码必须执行以下步骤:

  1. 使用上述信息填充 UIAutomationPatternInfo 结构。
  2. 使用 CoCreateInstance 函数创建 CUIAutomationRegistrar 对象的实例,并检索指向该对象的 IUIAutomationRegistrar 接口的指针。
  3. 调用 IUIAutomationRegistrar::RegisterPattern 方法,传递 UIAutomationPatternInfo 结构的地址。

RegisterPattern 方法返回控件模式 ID,以及属性 ID 和事件 ID 的列表。 应用程序可以将这些 ID 传递给采用此类标识符作为参数的任何UI 自动化方法。 例如,可以将已注册的模式 ID 传递给 IUIAutomationElement::GetCurrentPattern 方法,以检索指向控件模式的提供程序接口的指针。

没有任何方法可以取消注册自定义控件模式。 而是在释放最后一个UI 自动化对象时隐式取消注册。

有关演示如何注册自定义控件模式的示例,请参阅以下部分。

自定义控件模式的示例实现

本部分包含的示例代码演示如何实现自定义控件模式的客户端包装和模式处理程序对象。 该示例实现基于 Value 控件模式的自定义控件模式。

// Step 1: Define the public provider and client interfaces using IDL. Interface 
// definitions are in C here to simplify the example.

// Define the provider interface.
interface __declspec(uuid("9f5266dd-f0ab-4562-8175-c383abb2569e"))
IMyValueProvider : public IUnknown
{
    STDMETHOD (get_Value)(BSTR * pValue) = 0;
    STDMETHOD (get_IsReadOnly)(BOOL * pIsReadOnly) = 0;
    STDMETHOD (SetValue)(LPCWSTR pNewValue) = 0;
    STDMETHOD (Reset)() = 0;
};

// Define the client interface.
interface __declspec(uuid("103b8323-b04a-4180-9140-8c1e437713a3"))
IUIAutomationMyValuePattern : public IUnknown
{
    STDMETHOD (get_CurrentValue)(BSTR * pValue) = 0;
    STDMETHOD (get_CachedValue)(BSTR * pValue) = 0;

    STDMETHOD (get_CurrentIsReadOnly)(BOOL * pIsReadOnly) = 0;
    STDMETHOD (get_CachedIsReadOnly)(BOOL * pIsReadOnly) = 0;

    STDMETHOD (SetValue)(LPCWSTR pNewValue) = 0;
    STDMETHOD (Reset)() = 0;
};

// Enumerate the properties and methods starting from 0, and placing the 
// properties first. 
enum
{
    MyValue_GetValue = 0,
    MyValue_GetIsReadOnly = 1,
    MyValue_SetValue = 2,
    MyValue_Reset = 3,
};

// Step 2: Implement the client wrapper class.
class CMyValuePatternClientWrapper :
    public IUIAutomationMyValuePattern
{
    IUIAutomationPatternInstance * _pInstance;

public:
    // Get IUIAutomationPatternInstance interface pointer.
    CMyValuePatternClientWrapper(IUIAutomationPatternInstance * pInstance)
    {
        _pInstance = pInstance;
        _pInstance->AddRef();
    }

    // Put standard IUnknown implementation here.

    STDMETHODIMP get_CurrentValue(BSTR * pValue)
    {
        return _pInstance->GetProperty(MyValue_GetValue, FALSE, 
                UIAutomationType_String, pValue);
    }

    STDMETHODIMP get_CachedValue(BSTR * pValue)
    {
        return _pInstance->GetProperty(MyValue_GetValue, TRUE, 
                UIAutomationType_String, pValue);
    }

    STDMETHODIMP get_CurrentIsReadOnly(BOOL * pIsReadOnly)
    {
        return _pInstance->GetProperty(MyValue_GetIsReadOnly, FALSE, 
                UIAutomationType_Bool, pIsReadOnly);
    }

    STDMETHODIMP get_CachedIsReadOnly(BOOL * pIsReadOnly)
    {
        return _pInstance->GetProperty(MyValue_GetIsReadOnly, TRUE, 
                UIAutomationType_Bool, pIsReadOnly);
    }

    STDMETHODIMP SetValue(LPCWSTR pValue)
    {
        UIAutomationParameter SetValueParams[] = 
                { UIAutomationType_String, &pValue };
        return _pInstance->CallMethod(MyValue_SetValue,  SetValueParams, 
                ARRAYSIZE(SetValueParams));
    }

    STDMETHODIMP Reset()
    {
        return _pInstance->CallMethod(MyValue_Reset, NULL, 0);
    }
};

// Step 3: Implement the pattern handler class.
class CMyValuePatternHandler : public IUIAutomationPatternHandler
{
public:

    // Put standard IUnknown implementation here.
    
    STDMETHODIMP CreateClientWrapper(
            IUIAutomationPatternInstance * pPatternInstance, 
            IUnknown ** pClientWrapper)
    {
        *pClientWrapper = new CMyValuePatternClientWrapper(pPatternInstance);
        if (*pClientWrapper == NULL)
            return E_INVALIDARG;
        return S_OK;
    }
    
    STDMETHODIMP Dispatch (IUnknown * pTarget, UINT index, 
            const struct UIAutomationParameter *pParams, 
            UINT cParams)
    {
        switch(index)
        {
        case MyValue_GetValue:
            return ((IMyValueProvider*)pTarget)->get_Value((BSTR*)pParams[0].pData);

        case MyValue_GetIsReadOnly:
            return ((IMyValueProvider*)pTarget)->get_IsReadOnly((BOOL*)pParams[0].pData);

        case MyValue_SetValue:
            return ((IMyValueProvider*)pTarget)->SetValue(*(LPCWSTR*)pParams[0].pData);

        case MyValue_Reset:
            return ((IMyValueProvider*)pTarget)->Reset();
        }
        return E_INVALIDARG;
    }
};

CMyValuePatternHandler g_MyValuePatternHandler;

// Step 4: Declare information about the properties and methods supported
// by the custom control pattern.

// Define GUIDs for the custom control pattern and each of its properties 
// and events.
static const GUID MyValue_Pattern_Guid = { 0xa49aa3c0, 0xe413, 0x4ecf, 
        { 0xa1, 0xc3, 0x37, 0x42, 0xa7, 0x86, 0x67, 0x3f } };
static const GUID MyValue_Value_Property_Guid = { 0xe58f3f67, 0x22c7, 0x44f0, 
        { 0x83, 0x55, 0xd8, 0x76, 0x14, 0xa1, 0x10, 0x81 } };
static const GUID MyValue_IsReadOnly_Property_Guid = { 0x480540f2, 0x9829, 0x4acd, 
        { 0xb8, 0xea, 0x6e, 0x2a, 0xdc, 0xe5, 0x3a, 0xfb } };
static const GUID MyValue_Reset_Event_Guid = { 0x5b80edd3, 0x67f, 0x4a70, 
        { 0xb0, 0x7, 0x4, 0x12, 0x85, 0x11, 0x1, 0x7a } };

// Declare information about the properties, in the same order as the
// previously defined "MyValue_" enumerated type.
UIAutomationPropertyInfo g_MyValueProperties[] = 
{
    // GUID, name, data type.
    { MyValue_Value_Property_Guid, L"MyValuePattern.Value", 
                                                    UIAutomationType_String },
    { MyValue_IsReadOnly_Property_Guid, L"MyValuePattern.IsReadOnly", 
                                                    UIAutomationType_Bool },
};

// Declare information about the event.
UIAutomationEventInfo g_MyValueEvents [] =
{
    { MyValue_Reset_Event_Guid,  L"MyValuePattern.Reset" },
};

// Declare the data type and name of the SetValue method parameter. 
UIAutomationType g_SetValueParamTypes[] = { UIAutomationType_String };
LPCWSTR g_SetValueParamNames[] = {L"pNewValue"};

// Declare information about the methods.
UIAutomationMethodInfo g_MyValueMethods[] =
{
    // Name, focus flag, count of in parameters, count of out parameters, types, parameter names.
    { L"MyValuePattern.SetValue", TRUE, 1, 0, g_SetValueParamTypes, g_SetValueParamNames },
    { L"MyValuePattern.Reset", TRUE, 0, 0, NULL, NULL },
};

// Declare the custom control pattern using the previously defined information.
UIAutomationPatternInfo g_ValuePatternInfo =
{
    MyValue_Pattern_Guid,
    L"MyValuePattern",
    __uuidof(IMyValueProvider),
    __uuidof(IUIAutomationMyValuePattern),
    ARRAYSIZE(g_MyValueProperties), g_MyValueProperties, // properties
    ARRAYSIZE(g_MyValueMethods), g_MyValueMethods,       // methods
    ARRAYSIZE(g_MyValueEvents), g_MyValueEvents,         // events 
    &g_MyValuePatternHandler
};

// Step 5: Register the custom control pattern and retrieve the control pattern and property 
// identifiers.

// Control pattern, property, and event IDs.
PATTERNID  g_MyValue_PatternID;
PROPERTYID g_MyValue_Value_PropertyID;
PROPERTYID g_MyValue_IsReadOnly_PropertyID;
EVENTID    g_MyValueReset_EventID;

// ID used by the client to determine whether the custom control pattern is available.
PROPERTYID g_IsMyValuePatternAvailable_PropertyID;

HRESULT RegisterPattern()
{
    // Create the registrar object and get the IUIAutomationRegistrar interface pointer. 
    IUIAutomationRegistrar * pUIARegistrar;
    CoCreateInstance(CLSID_CUIAutomationRegistrar, NULL, CLSCTX_INPROC_SERVER, 
            IID_IUIAutomationRegistrar, (void **)&pUIARegistrar);

    if (pUIARegistrar == NULL)
        return E_NOINTERFACE;

    PROPERTYID propIDs[2]; // Array for property IDs.

    // Register the control pattern.
    HRESULT hr = pUIARegistrar->RegisterPattern(
        &g_ValuePatternInfo,
        &g_MyValue_PatternID,
        &g_IsMyValuePatternAvailable_PropertyID,
        ARRAYSIZE(propIDs), 
        propIDs,
        1,
        &g_MyValueReset_EventID);
            
    if (hr == S_OK)
    {
        // Copy the property IDs.
        g_MyValue_Value_PropertyID = propIDs[0];
        g_MyValue_IsReadOnly_PropertyID = propIDs[1];
    }

    pUIARegistrar->Release();
    return hr;
}

概念性

设计自定义属性、事件和控件模式

UI 自动化属性概述

UI 自动化事件概述

UI 自动化控件模式概述