Server-Side UI 자동화 공급자 구현
이 항목에서는 C++로 작성된 사용자 지정 컨트롤에 대한 서버 쪽 Microsoft UI 자동화 공급자를 구현하는 방법에 대해 설명합니다. 여기에는 다음 섹션이 포함되어 있습니다.
서버 쪽 공급자를 구현하는 방법을 보여 주는 코드 예제는 UI 자동화 공급자 대한How-To 항목을 참조하세요.
공급자 트리 구조
UIA 클라이언트에 액세스할 수 있어야 하는 각 UI 요소에 대해 UIA 공급자를 구현해야 합니다.
예를 들어 각 요소는 IRawElementProviderFragment 구현해야 하지만 애플리케이션의 루트 요소는 IRawElementProviderFragmentRoot구현해야 합니다. 또한 각 공급자 요소는 하나의 항목에 연결되어야 합니다.
- 부모
- 이전 공급자 요소
- 다음 공급자 요소
- 첫 번째 공급자 자식
- 마지막 공급자 자식
공급자 인터페이스
다음 COM(구성 요소 개체 모델) 인터페이스는 사용자 지정 컨트롤에 대한 기능을 제공합니다. 기본 기능을 제공하려면 모든 UI 자동화 공급자가 최소한 IRawElementProviderSimple 인터페이스를 구현해야 합니다. IRawElementProviderFragment 및 IRawElementProviderFragmentRoot 인터페이스는 선택 사항이지만 추가 기능을 제공하기 위해 복잡한 컨트롤의 요소에 대해 구현해야 합니다.
인터페이스 | 묘사 |
---|---|
IRawElementProviderSimple | 컨트롤 패턴 및 속성에 대한 지원을 포함하여 창에서 호스트되는 컨트롤에 대한 기본 기능을 제공합니다. |
IRawElementProviderFragment | 조각 탐색, 포커스 설정 및 요소의 경계 사각형 반환을 포함하여 복잡한 컨트롤의 요소에 대한 기능을 추가합니다. |
IRawElementProviderFragmentRoot | 지정된 좌표에서 자식 요소를 찾고 전체 컨트롤의 포커스 상태를 설정하는 등 복잡한 컨트롤의 루트 요소에 대한 기능을 추가합니다. |
메모
관리 코드에 대한 UI 자동화 API에서 이러한 인터페이스는 상속 계층 구조를 형성합니다. 인터페이스가 완전히 분리된 C++에서는 그렇지 않습니다.
다음 인터페이스는 추가 기능을 제공하지만 구현은 선택 사항입니다.
인터페이스 | 묘사 |
---|---|
IRawElementProviderAdviseEvents | 공급자가 이벤트에 대한 요청을 추적할 수 있도록 합니다. |
IRawElementProviderHwndOverride | 조각의 UI 자동화 트리에서 창 기반 요소의 위치를 변경할 수 있습니다. |
UI 자동화 공급자에 필요한 기능
UI 자동화와 통신하려면 컨트롤이 다음 표에 설명된 기능의 주요 영역을 구현해야 합니다.
기능 | 이행 |
---|---|
공급자를 UI 자동화에 노출합니다. | 제어 창으로 전송된 WM_GETOBJECT 메시지에 대한 응답으로 IRawElementProviderSimple구현하는 개체를 반환합니다. 조각의 경우에는, 반드시 조각 루트에 대한 공급자여야 합니다. |
속성 값을 입력하십시오. | 값을 제공하거나 재정의하는 IRawElementProviderSimple::GetPropertyValue 구현합니다. |
클라이언트가 컨트롤과 상호 작용할 수 있도록 설정합니다. | IInvokeProvider같은 적절한 각 컨트롤 패턴을 지원하는 인터페이스를 구현합니다. IRawElementProviderSimple::GetPatternProvider구현에서 이러한 컨트롤 패턴 공급자를 반환합니다. |
이벤트를 발생시킵니다. | UiaRaiseAutomationEvent, IProxyProviderWinEventSink 의 메서드. |
조각에서 탐색 및 포커스를 활성화합니다. | 조각 내의 각 요소에 대해 IRawElementProviderFragment 구현합니다. 조각의 일부가 아닌 요소에는 필요하지 않습니다. |
프래그먼트에서 자식 요소의 포커스를 지정하고 검색할 수 있도록 합니다. | IRawElementProviderFragmentRoot구현합니다. 조각 루트가 아닌 요소에는 필요하지 않습니다. |
속성 값
사용자 지정 컨트롤에 대한 UI 자동화 공급자는 UI 자동화 및 클라이언트 애플리케이션에서 사용할 수 있는 특정 속성을 지원해야 합니다. 창에서 호스트되는 요소의 경우 UI 자동화는 기본 창 공급자에서 일부 속성을 검색할 수 있지만 사용자 지정 공급자에서 다른 속성을 가져와야 합니다.
일반적으로 창 기반 컨트롤에 대한 공급자는 PROPERTYID식별되는 다음 속성을 제공할 필요가 없습니다.
- UIA_BoundingRectanglePropertyId
- UIA_ClickablePointPropertyId
- UIA_ProcessIdPropertyId
- UIA_ClassNamePropertyId
- UIA_HasKeyboardFocusPropertyId
- UIA_IsEnabledPropertyId
- UIA_IsKeyboardFocusablePropertyId
- UIA_IsPasswordPropertyId
- UIA_NamePropertyId
- UIA_RuntimeIdPropertyId
창에서 호스트되는 단순 요소 또는 조각 루트의 RuntimeId 속성을 창에서 가져옵니다. 그러나 목록 상자의 목록 항목과 같이 루트 아래의 조각 요소는 자체 식별자를 제공해야 합니다. 자세한 내용은 IRawElementProviderFragment::GetRuntimeId참조하세요.
IsKeyboardFocusable 속성은 Windows Forms 컨트롤에서 호스트되는 공급자에 대해 반환되어야 합니다. 이 경우 기본 창 공급자가 올바른 값을 검색할 수 없습니다.
Name 속성은 일반적으로 호스트 공급자가 제공합니다.
공급자의 이벤트
UI 자동화 공급자는 클라이언트 애플리케이션에 UI 상태의 변경 내용을 알리는 이벤트를 발생시켜야 합니다. 다음 함수는 이벤트를 발생시키는 데 사용됩니다.
기능 | 설명 |
---|---|
UiaRaiseAutomationEvent | 컨트롤 패턴이 트리거하는 이벤트를 포함하여 다양한 이벤트를 발생시킵니다. |
UiaRaiseAutomationPropertyChangedEvent | UI 자동화 속성이 변경되면 이벤트를 발생합니다. |
UiaRaiseStructureChangedEvent | 예를 들어 요소를 제거하거나 추가하여 UI 자동화 트리의 구조가 변경될 때 이벤트를 발생합니다. |
이벤트의 목적은 UI에서 발생한 작업을 클라이언트에 알리는 것입니다. 공급자는 UI 자동화를 사용하여 사용자 입력 또는 클라이언트 애플리케이션에 의해 변경이 트리거되었는지 여부에 관계없이 이벤트를 발생시켜야 합니다. 예를 들어, UIA_Invoke_InvokedEventId로 식별된 이벤트는 사용자에 의한 직접 입력이나 클라이언트 애플리케이션이 IUIAutomationInvokePattern::Invoke 를 호출할 때마다 컨트롤이 호출되면 발생해야 합니다.
성능을 최적화하기 위해, 공급자는 이벤트를 선택적으로 발생시키거나, 수신할 클라이언트 애플리케이션이 등록되어 있지 않은 경우 이벤트를 전혀 발생시키지 않을 수 있습니다. 최적화에 사용되는 API 요소는 다음과 같습니다.
API 요소 | 묘사 |
---|---|
UiaClientsAreListening | 이 함수는 클라이언트 애플리케이션이 UI 자동화 이벤트를 구독했는지 여부를 확인합니다. |
IRawElementProviderAdviseEvents | 조각 루트에서 이 인터페이스를 구현하면 클라이언트가 조각의 이벤트에 대한 이벤트 처리기를 등록 및 등록 취소할 때 공급자에게 알 수 있습니다. |
메모
COM 프로그래밍에서 참조 계산 구현과 유사합니다. UI 자동화 공급자는 IUnknown 인터페이스의 IUnknown::AddRef 및 Release 메서드와 같은 IRawElementProviderAdviseEvents::AdviseEventAdded 및 AdviseEventRemoved 메서드를 처리하는 것이 중요합니다. AdviseEventAdded가 특정 이벤트나 속성에 대해 AdviseEventRemoved보다 더 많이 호출되는 한, 공급자는 몇몇 클라이언트가 여전히 수신 대기 중이기 때문에 해당 이벤트를 계속 발생시켜야 합니다. 또는 UI 자동화 공급자는 UiaClientsAreListening 함수를 사용하여 하나 이상의 클라이언트가 수신 대기 중인지 여부를 확인하고, 이 경우 모든 적절한 이벤트를 발생할 수 있습니다.
공급자 탐색
창에서 호스트되는 사용자 지정 단추와 같은 간단한 컨트롤에 대한 공급자는 UI 자동화 트리에서 탐색을 지원할 필요가 없습니다. 요소에 대한 탐색은 호스트 창의 기본 공급자에 의해 처리되며, IRawElementProviderSimple::HostRawElementProvider구현에 지정됩니다. 그러나 복잡한 사용자 지정 컨트롤에 대한 공급자를 구현하는 경우 조각의 루트 노드와 해당 하위 노드 간 및 형제 노드 간 탐색을 지원해야 합니다.
메모
루트가 아닌 조각의 요소는, 창에서 직접 호스트되지 않고 기본 공급자가 이들에 대한 탐색을 지원할 수 없기 때문에, HostRawElementProvider에서 NULL을 반환해야 합니다.
조각의 구조는 IRawElementProviderFragment::Navigate구현에 따라 결정됩니다. 각 조각에서 가능한 각 방향에 대해 이 메서드는 해당 방향으로 요소에 대한 공급자 개체를 반환합니다. 해당 방향에 요소가 없으면 메서드는 NULL 반환합니다.
조각 루트는 자식 요소에 대한 탐색만 지원합니다. 예를 들어 방향이 NavigateDirection_FirstChild일 때 목록의 첫 번째 항목을 반환하며, 방향이 NavigateDirection_LastChild일 때는 마지막 항목을 반환합니다. 조각 루트는 부모 또는 형제에 대한 탐색을 지원하지 않습니다. 호스트 창 공급자가 처리합니다.
루트가 아닌 조각의 요소는 부모, 형제, 자식에 대한 탐색을 지원해야 합니다.
새 부모 할당
팝업 창은 실제로 최상위 창이며 기본적으로 데스크톱의 자식으로 UI 자동화 트리에 표시됩니다. 그러나 대부분의 경우 팝업 창은 논리적으로 다른 컨트롤의 자식입니다. 예를 들어 콤보 상자의 드롭다운 목록은 논리적으로 콤보 상자의 자식입니다. 마찬가지로 메뉴 팝업 창은 논리적으로 메뉴의 자식입니다. UI 자동화는 연결된 컨트롤의 자식으로 표시되도록 팝업 창에 새 부모를 할당하도록 지원합니다.
팝업 창에 새 부모를 할당하려면 다음을 수행합니다.
- 팝업 창에 대한 공급자를 만듭니다. 이렇게 하려면 팝업 창의 클래스를 미리 알려야 합니다.
- 해당 팝업에 대해 평소와 같이 모든 속성 및 컨트롤 패턴을 구현합니다( 마치 자체적으로 컨트롤인 것처럼).
- IRawElementProviderSimple::HostRawElementProvider 속성을 구현하여 UiaHostProviderFromHwnd가져온 값을 반환합니다. 여기서 매개 변수는 팝업 창의 창 핸들입니다.
- 팝업 창과 해당 부모에 대해 IRawElementProviderFragment 의 탐색을 구현하여, 논리적 부모에서 논리적 자식으로, 그리고 형제 자식 간의 탐색이 제대로 처리되도록 합니다.
UI 자동화가 팝업 창을 발견하면 탐색이 기본값에서 재정의되고 있음을 인식합니다. 그리고 데스크톱의 자식으로 발견된 경우, 팝업 창을 무시하고 넘어갑니다. 대신 노드는 조각을 통해서만 연결할 수 있습니다.
컨트롤이 클래스의 창을 호스트할 수 있는 경우에는 새 부모 할당에 적합하지 않습니다. 예를 들어 rebar 컨트롤은 해당 밴드에서 모든 유형의 창을 호스트할 수 있습니다. 이러한 경우를 처리하기 위해 UI 자동화는 다음 섹션에 설명된 대로 다른 형태의 창 재배치를 지원합니다.
공급자 위치 변경
UI 자동화 조각에는 각각 창에 포함된 두 개 이상의 요소가 포함될 수 있습니다. 각 창에는 창을 포함하는 창의 자식으로 간주하는 자체 기본 공급자가 있으므로 기본적으로 UI 자동화 트리는 조각의 창을 부모 창의 자식으로 표시합니다. 대부분의 경우 이 동작은 바람직한 동작이지만 UI의 논리적 구조와 일치하지 않으므로 혼동이 발생할 수 있습니다.
이 예제의 좋은 예는 rebar 컨트롤입니다. Rebar 컨트롤에는 각각 도구 모음, 편집 상자 또는 콤보 상자와 같은 창 기반 컨트롤을 포함할 수 있는 밴드가 포함됩니다. 리바 UI의 기본 창 제공자는 밴드 컨트롤 창을 자식으로 보고, 리바 제공자는 밴드를 자식으로 봅니다. 창 공급자와 리바 공급자가 함께 작업하고 자식을 결합하기 때문에 밴드와 윈도우 기반 컨트롤이 모두 리바 컨트롤의 자식으로 나타납니다. 그러나 논리적으로는 밴드만 rebar 컨트롤의 자식으로 표시되어야 하며 각 밴드 공급자는 포함된 컨트롤에 대한 기본 창 공급자와 결합되어야 합니다.
이를 위해 rebar 컨트롤에 대한 조각 루트 공급자는 밴드를 나타내는 자식 집합을 노출합니다. 각 밴드에는 속성 및 컨트롤 패턴을 노출할 수 있는 단일 공급자가 있습니다. IRawElementProviderSimple::HostRawElementProvider구현에서 밴드 공급자는 컨트롤 창에 대한 기본 창 공급자를 반환합니다. 이 공급자는 UiaHostProviderFromHwnd호출하여 컨트롤의 창 핸들(HWND)을 전달하여 가져옵니다. 마지막으로, rebar에 대한 조각 루트 공급자는 IRawElementProviderHwndOverride 인터페이스를 구현하고, IRawElementProviderHwndOverride::GetOverrideProviderForHwnd의 구현에서 지정된 창에 포함된 컨트롤에 대한 적절한 밴드 공급자를 반환합니다.
공급자 연결 끊기
애플리케이션은 일반적으로 필요에 따라 컨트롤을 만들고 나중에 삭제합니다. 컨트롤을 삭제한 후 UiaDisconnectProvider호출하여 컨트롤과 연결된 UI 자동화 공급자 리소스를 해제해야 합니다.
마찬가지로 애플리케이션은 UiaDisconnectAllProviders 함수를 사용하여 종료하기 전에 애플리케이션의 모든 공급자가 보유한 모든 UI 자동화 리소스를 해제해야 합니다.
관련 항목