Server-Side UI 자동화 공급자 구현
이 항목에서는 C++로 작성된 사용자 지정 컨트롤에 대해 서버 쪽 Microsoft UI 자동화 공급자를 구현하는 방법을 설명합니다. 여기에는 다음 단원이 포함되어 있습니다.
서버 쪽 공급자를 구현하는 방법을 보여 주는 코드 예제는 UI 자동화 공급자에 대한 방법 항목을 참조하세요.
공급자 트리 구조
UIA 클라이언트에 액세스해야 하는 각 UI 요소에 대해 UIA 공급자를 구현해야 합니다.
예를 들어 각 요소는 IRawElementProviderFragment 를 구현해야 하고 애플리케이션의 루트 요소는 IRawElementProviderFragmentRoot를 구현해야 합니다. 또한 각 공급자 요소는 에 연결해야 합니다.
- 부모(parent)
- 이전 공급자 요소
- 다음 공급자 요소
- 첫 번째 공급자 자식
- 마지막 공급자 자식
공급자 인터페이스
다음 COM(구성 요소 개체 모델) 인터페이스는 사용자 지정 컨트롤에 대한 기능을 제공합니다. 기본 기능을 제공하려면 모든 UI 자동화 공급자가 적어도 IRawElementProviderSimple 인터페이스를 구현해야 합니다. IRawElementProviderFragment 및 IRawElementProviderFragmentRoot 인터페이스는 선택 사항이지만 추가 기능을 제공하기 위해 복잡한 컨트롤의 요소에 대해 구현해야 합니다.
인터페이스 | 설명 |
---|---|
IRawElementProviderSimple | 컨트롤 패턴 및 속성에 대한 지원을 포함하여 창에서 호스트되는 컨트롤에 대한 기본 기능을 제공합니다. |
IRawElementProviderFragment | 조각 탐색, 포커스 설정, 요소의 경계 사각형 반환 등 복잡한 컨트롤의 요소에 대한 기능을 추가합니다. |
IRawElementProviderFragmentRoot | 지정된 좌표에 자식 요소 배치, 전체 컨트롤에 대한 포커스 상태 설정을 포함하여 복잡한 제어에 있는 루트 요소에 대한 기능을 추가합니다. |
참고
관리 코드에 대한 UI 자동화 API에서 이러한 인터페이스는 상속 계층 구조를 형성합니다. 인터페이스가 완전히 분리된 C++에서는 그렇지 않습니다.
다음 인터페이스는 추가 기능을 제공하지만 구현은 선택 사항입니다.
인터페이스 | Description |
---|---|
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 상태 변경 시 이를 클라이언트 애플리케이션에 알리는 이벤트를 발생시켜야 합니다. 이벤트를 발생 하려면 다음 함수를 사용 합니다.
함수 | Description |
---|---|
UiaRaiseAutomationEvent | 컨트롤 패턴에 의해 트리거되는 이벤트를 포함하여 다양한 이벤트를 발생시킵니다. |
UiaRaiseAutomationPropertyChangedEvent | UI 자동화 속성이 변경되면 이벤트를 발생시킵니다. |
UiaRaiseStructureChangedEvent | 예를 들어 요소를 제거하거나 추가하여 UI 자동화 트리의 구조가 변경되면 이벤트를 발생합니다. |
이벤트의 목적은 UI에서 발생한 작업을 클라이언트에 알리는 것입니다. 공급자는 변경이 사용자 입력에 의해 트리거되었는지 또는 UI 자동화 사용하는 클라이언트 애플리케이션에 의해 트리거되었는지 여부에 관계없이 이벤트를 발생시켜야 합니다. 예를 들어 직접 사용자 입력 또는 IUIAutomationInvokePattern::Invoke를 호출하는 클라이언트 애플리케이션을 통해 컨트롤이 호출될 때마다 UIA_Invoke_InvokedEventId 의해 식별되는 이벤트가 발생해야 합니다.
성능을 최적화하기 위해 공급자는 선택적으로 이벤트를 발생시키거나 이벤트 수신을 등록한 클라이언트 애플리케이션이 없는 경우에는 이벤트를 전혀 발생시키지 않을 수 있습니다. 최적화에 사용되는 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::Navigate 를 구현합니다.
UI 자동화에서 팝업 창을 발견하면 탐색이 기본값에서 재정의된다는 것을 인식하며 데스크톱의 자식 항목으로 발견된 경우에는 팝업 창으로 건너뜁니다. 대신 노드는 조각을 통해서만 연결할 수 있습니다.
컨트롤이 클래스의 창을 호스트할 수 있는 경우에는 새 부모 할당에 적합하지 않습니다. 예를 들어, rebar 컨트롤은 해당 밴드에서 모든 유형의 창을 호스트할 수 있습니다. 이러한 경우를 처리하기 위해 UI 자동화 다음 섹션에 설명된 대로 대체 형태의 창 재배치를 지원합니다.
공급자 위치 변경
UI 자동화 조각에는 각각 창에 포함된 두 개 이상의 요소가 포함될 수 있습니다. 각 창에는 창을 포함하는 창의 자식으로 간주하는 자체 기본 공급자가 있으므로 기본적으로 UI 자동화 트리는 조각의 창을 부모 창의 자식으로 표시합니다. 대부분의 경우 이것은 바람직한 동작이지만, 때로는 UI의 논리 구조와 일치하지 않기 때문에 혼란을 일으킬 수 있습니다.
그 좋은 예가 rebar 컨트롤입니다. rebar 컨트롤에는 밴드가 포함되며, 각 컨트롤에는 도구 모음, 편집 상자 또는 콤보 상자와 같은 창 기반 컨트롤이 포함될 수 있습니다. 철근 창의 기본 창 공급자는 밴드 제어 창을 자식으로 표시하고, 철근 공급자는 밴드를 자식으로 봅니다. 창 공급자와 철근 공급자가 함께 작업하고 자식을 결합하기 때문에 밴드와 창 기반 컨트롤은 모두 rebar 컨트롤의 자식으로 표시됩니다. 그러나 논리적으로는 밴드만 rebar 컨트롤의 자식으로 표시되어야 하며 각 밴드 공급자는 포함된 컨트롤에 대한 기본 창 공급자와 결합되어야 합니다.
이를 위해 rebar 컨트롤에 대한 조각 루트 공급자는 밴드를 나타내는 자식 집합을 노출합니다. 각 밴드에는 속성 및 컨트롤 패턴을 노출할 수 있는 단일 공급자가 있습니다. IRawElementProviderSimple::HostRawElementProvider 구현에서 밴드 공급자는 컨트롤 창에 대한 기본 창 공급자를 반환합니다. 이 공급자는 UiaHostProviderFromHwnd를 호출하고 컨트롤의 창 핸들(HWND)을 전달하여 가져옵니다. 마지막으로, rebar에 대한 조각 루트 공급자는 IRawElementProviderHwndOverride 인터페이스를 구현하고 IRawElementProviderHwndOverride::GetOverrideProviderForHwnd의 구현에서 지정된 창에 포함된 컨트롤에 대한 적절한 대역 공급자를 반환합니다.
공급자 연결 끊기
애플리케이션은 일반적으로 필요에 따라 컨트롤을 만들고 나중에 삭제합니다. 컨트롤을 삭제한 후 UiaDisconnectProvider를 호출하여 컨트롤과 연결된 UI 자동화 공급자 리소스를 해제해야 합니다.
마찬가지로 애플리케이션은 UiaDisconnectAllProviders 함수를 사용하여 종료하기 전에 애플리케이션의 모든 공급자가 보유한 모든 UI 자동화 리소스를 해제해야 합니다.
관련 항목