CAFE

WPF

[22기 조대준] UI 자동화를 이용한 응용프로그램 제어

작성자22기 조대준|작성시간11.10.18|조회수1,122 목록 댓글 0

 

 

 

 

 

 

 

 

 

 

 

 

UI 자동화를 이용한 응용프로그램 제어

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

선문비트 22기 조대준

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1.     UI 자동화 란?

Microsoft UI Automation WPF를 지원하는 모든 운영 체제에서 사용할 수 있는  Microsoft Windows의 새로운 내게 필요한 옵션 프레임 워크이다.

UI Automation를 통해 데스크톱에 있는 대부분의 user interface(UI) 요소에 프로그래밍 방식으로 액세스할 수 있으므로 화면 판독기 등의 보조 기술 제품에서 최종 사용자에게 UI에 대한 정보를 제공할 수 있으며 표준 입력 이외의 방법을 통해 UI를 조작할 수 있다. 또한, UI Automation를 통해 자동화된 테스트 스크립트에서 UI와 상호 작용할 수 있다.

 

2.     UI 자동화 모델

UI Automation UI의 모든 요소를 AutomationElement로 클라이언트 응용 프로그램에 노출한다. 요소는 데스크톱이 루트 요소인 트리 구조에 포함된다. 클라이언트에서 트리의 Raw 뷰를 컨트롤뷰 또는 콘텐츠 뷰로 필터링할 수 있다.

AutomationElement 개체는 개체가 나타내는 UI 요소의 공통 속성을 노출한다. 이러한 속성 중 하나는 컨트롤의 기본 모양과 기능을 단추나 확인란과 같은 인식 가능한 하나의 엔터티로 정의하는 컨트롤 형식이다.

또한 요소는 해당 컨트롤 형식에 고유한 속성을 제공하는 컨트롤 패턴을 노출한다. 뿐만 아니라 컨트롤 패턴은 클라이언트가 요소에 대한 추가 정보를 수집하여 입력을 제공하는데 사용할 수 있는 메서드를 노출한다.

 

자세한 내용은 UI 자동 컨트롤 형식을 참조.

 

3.     UI 자동화 컨트롤 패턴

컨트롤 패턴을 사용하면 컨트롤 형식이나 컨트롤 모양에 독립적으로 컨트롤의 기능을 분류하고 노출할 수 있다.

UI Automation는 컨트롤 패턴을 사용하여 많이 사용되는 컨트롤 동작을 나타낸다. 예를 들어 버튼과 같이 호출 가능한 컨트롤에는 Invoke 컨트롤 패턴을 사용하고, 리스트 박스, 리스트 뷰 또는 콤보 상자와 같이 스크롤 막대가 있는 컨트롤에는 Scroll 컨트롤 패턴을 사용한다. 각 컨트롤 패턴은 서로 다른 기능을 나타내므로 이들을 결합하여 특정 컨트롤이 지원하는 전체 기능 집합을 정의할 수 있다.

          

4.     컨트롤 패턴 클래스 인터페이스

다음 표에서는 UI Automation 컨트롤 패턴을 설명한다. 이 표에서는 컨트롤 패턴에 액세스하기 위해 UI 자동화 클라이언트에 사용되는 클래스 및 이를 구현하기 위해 UI 자동화 공급자에 사용되는 인터페이스도 보여 준다.

컨트롤 패턴 클래스

공급자 인터페이스

설명

DockPattern

IDockProvider

도킹 컨테이너에 도킹할 수 있는 컨트롤에 사용된다. : 도구 모음 또는 도구 팔레트

ExpandCollapsePattern

IExpandCollapseProvider

확장하거나 축소할 수 있는 컨트롤에 사용된다.

 : 파일 메뉴와 같은 응용 프로그램의 메뉴 항목

GridPattern

IGridProvider

특정 셀로 이동 및 크기 조정 같은 표 기능을 지원하는 컨트롤에 사용된다.

: Windows 탐색기의 큰 아이콘 보기 또는 Microsoft Word의 머리글 없는 간단한 표

GridItemPattern

IGridItemProvider

표 안에 셀이 있는 컨트롤에 사용된다. 개별 셀이 GridItem 패턴을 지원해야 한다.

 : Microsoft Windows Explorer 자세히 보기의 각 셀

InvokePattern

IInvokeProvider

호출할 수 있는 컨트롤에 사용된다. : 단추

MultipleViewPattern

IMultipleViewProvider

같은 정보, 데이터 또는 자식 집합의 여러 표현 간에 전환할 수 있는 컨트롤에 사용된다.

: 데이터를 축소판 이미지, 바둑판식, 아이콘, 목록 또는 자세히 보기로 표현할 수 있는 목록 뷰 컨트롤

RangeVal‎uePattern

IRangeVal‎ueProvider

컨트롤에 적용할 수 있는 값의 범위가 있는 컨트롤에 사용된다. : 1900부터 2010까지의 연도를 포함하거나 1부터 12까지의 달을 표현하는 회전자 컨트롤

ScrollPattern

IScrollProvider

스크롤할 수 있는 컨트롤에 사용된다.

: 컨트롤의 표시 가능한 영역에 나타낼 수 있는 것보다 정보가 많을 경우 활성화되는 스크롤 막대가 포함된 컨트롤

ScrollItemPattern

IScrollItemProvider

스크롤되는 목록에 개별 항목이 있는 컨트롤에 사용된다.

 : 콤보 상자 컨트롤처럼 스크롤 목록에 개별 항목이 있는 목록 컨트롤

SelectionPattern

ISelectionProvider

선택 컨테이너 컨트롤에 사용된다.

: 목록 상자와 콤보 상자

SelectionItemPattern

ISelectionItemProvider

목록 상자 및 콤보 상자와 같은 선택 컨테이너 컨트롤에 있는 개별 항목에 사용된다.

TablePattern

ITableProvider

표와 머리말 정보가 있는 컨트롤에 사용된다.

: Microsoft Excel 워크시트

TableItemPattern

ITableItemProvider

테이블의 항목에 사용된다.

TextPattern

ITextProvider

텍스트 정보를 노출하는 문서 및 편집 컨트롤에 사용된다.

TogglePattern

IToggleProvider

상태를 전환할 수 있는 컨트롤에 사용된다.

: 확인란 및 선택 가능한 메뉴 항목

TransformPattern

ITransformProvider

크기를 조정하고, 이동하고, 회전할 수 있는 컨트롤에 사용된다. Transform 컨트롤 패턴은 일반적으로 디자이너, , 그래픽 편집기 및 그리기 응용 프로그램에 사용된다.

ValuePattern

IValueProvider

값 범위를 지원하지 않는 컨트롤에서 클라이언트가 값을 가져오거나 설정할 수 있습니다.

: 날짜 시간 선택 컨트롤

WindowPattern

IWindowProvider

Microsoft Windows 운영 체제에 대한 기본 개념인 창에 관련된 정보를 노출합니다.

: 최상위 응용 프로그램 창(Microsoft Word, Microsoft Windows Explorer ), multiple-document interface (MDI) 자식 창 및 대화 상자

 

5.     예제 프로그램

간단하게 UI Automation을 사용하여 데스크 탑에 있는 다른 응용프로그래밍의 컨트롤들을 제어 해보는 프로그램이다.

버튼 이라던지 호출 가능한 컨트롤들과 텍스트 박스등 값 형태를 가지고 있는 컨트롤들을 나눠 콤보 박스에 추가하고 콤보박스의 해당 아이템들을 선택하여 대상 응용프로그램의 컨트롤들을 사용할 수 있는 프로그램이다.

 

 

초기 화면은 이러하다. 프로그램 파일 선택을 눌러 대상 프로그램의 exe 파일을 불러와 실행 시킨다.

 

 

 

따로 더미 응용프로그램을 만들어 불러온다. 5개의 버튼요소와 4개의 텍스트 박스 요소를 얻어 온다.

 

 

 

 

 

왼쪽 콤보박스의 경우 아이템을 클릭시 더미 응용프로그램의 해당 컨트롤의 이벤트가 실행되고 오른쪽 콤보박스의 경우 아이템 클릭후 텍스트박스에 값을 넣고 변경을 누르게 되면 더미 프로그램의 해당되는 텍스트박스에 값이 바뀌게 된다.

          

           처음으로 참조 추가를 해야 한다.

 

( xaml 코드는 특별히 없으니 기술하지 않음 )

Main.xaml.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

using Microsoft.Win32;

using System.Data;

 

namespace ControlProcess

{

    /// <summary>

    /// Window1.xaml 대한 상호 작용 논리

    /// </summary>

    public partial class Window1 : Window

    {

        ComboBoxItem item2 = null;

        public Window1()

        {

            InitializeComponent();

        }

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            AEButtonList.Items.Clear();

            OpenFileDialog ofd = new OpenFileDialog();

            ofd.FileName = "실행파일";

            ofd.DefaultExt = ".exe";

            ofd.Filter = "실행파일 (*.exe)|*.exe";

 

            Nullable<bool> re = ofd.ShowDialog();

            if (re == true)

            {

                AECollection ae = new AECollection(ofd.FileName);

                DataTable dt = ae.DataTable;

               

                DataTable dt2 = ae.DataTable2;

                ComboBoxItem item;

 

                foreach (DataRow dr in dt.Rows)

                {

                    item = new ComboBoxItem();

                    item.Content = dr["name"];

                    item.Tag = ae;

                    item.ToolTip = dr["aeid"];

                    AEButtonList.Items.Add(item);

                }

                foreach (DataRow dr in dt2.Rows)

                {

                    item = new ComboBoxItem();

                    item.Content = dr["name"];

                    item.Tag = ae;

                    item.ToolTip = dr["aeid"];

                    AETextList.Items.Add(item);

                }

                Result.Content = string.Format("{0}개의 실행 가능한 자동화 요소 발견", dt.Rows.Count);

                Result2.Content = string.Format("{0}개의 실행 가능한 자동화 요소 발견", dt2.Rows.Count);

            }

        }

 

        private void AEList_SelectChanged(object sender, Selecti[안내]태그제한으로등록되지않습니다-xxonChangedEventArgs e)

        {

            ComboBoxItem item = AEButtonList.SelectedItem as ComboBoxItem;

            if (item == null)

            {

                return;

            }

 

            AECollection ae = item.Tag as AECollection;

 

            ae.InvokeAE(item.ToolTip.ToString());

 

    }

 

        private void button1_Click(object sender, RoutedEventArgs e)

        {

            AECollection ae = item2.Tag as AECollection;

 

            ae.SetValue(item2.ToolTip.ToString(),textBox1.Text);

        }

 

        private void AETextList_Selecti[안내]태그제한으로등록되지않습니다-xxonChanged(object sender, Selecti[안내]태그제한으로등록되지않습니다-xxonChangedEventArgs e)

        {

             item2 = AETextList.SelectedItem as ComboBoxItem;

            if (item2 == null)

            {

                return;

            }

 

 

        }

    }

}

 

프로그램 구조는 생각 간단 하다. 열고자 하는 프로그램(exe파일) 을 열어 AECollection 개체를 통해 DataTable을 받아와 콤보 박스에 추가 한다. 콤보박스에 추가시 아이템의 Tag 값에 AECollection 을 넣어줘 콤보 박스의 아이템 클릭 시 해당 아이템의 Tag 값을 얻어와 버튼일 경우 Invoke TextBox일 경우 SetValue 메서드를 호출 해준다.

        

 

        

AECollection.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

using System.Threading;

using System.Windows.Automation;

using System.Data;

 

 

namespace ControlProcess

{

    public class AECollection

    {

 

        AutomationElementCollection collection;

        AutomationElementCollection textboxcollection;

        DataTable dataTable;

        DataTable dataTable2;

        public DataTable DataTable2

        {

            get

            {

                if (dataTable2 == null)

                {

                    dataTable2 = MakeDataTable2();

                }

                return dataTable2;

            }

        }

 

        public DataTable DataTable

        {

            get

            {

                if (dataTable == null)

                {

                    dataTable = MakeDataTable();

                }

                return dataTable;

            }

        }

         private DataTable MakeDataTable2()

        {

            dataTable2 = new DataTable("AETable");

 

            dataTable2.Columns.Add(new DataColumn("name", typeof(string)));

            dataTable2.Columns.Add(new DataColumn("ae", typeof(AutomationElement)));

            dataTable2.Columns.Add(new DataColumn("aeVal‎uePattern", typeof(object)));

 

            DataColumn[] pk = new DataColumn[1];

            pk[0] = new DataColumn("aeid", typeof(string));

            dataTable2.Columns.Add(pk[0]);

            dataTable2.PrimaryKey = pk;

 

            DataRow dr;

            object obj;

 

            foreach (AutomationElement ae in textboxcollection)

            {

                dr = dataTable2.NewRow();

 

                dr["name"] = ae.Current.AutomationId;

                dr["aeid"] = ae.Current.GetHashCode();

                if (ae.TryGetCurrentPattern( ValuePattern.Pattern, out obj))

                {

                    dr["aeVal‎uePattern"] = obj;

                    dr["ae"] = ae;

                    dataTable2.Rows.Add(dr);

                }

 

            }

 

            return dataTable2;

 

        }

        private DataTable MakeDataTable()

        {

            dataTable = new DataTable("AETable");

 

            dataTable.Columns.Add(new DataColumn("name", typeof(string)));

            dataTable.Columns.Add(new DataColumn("ae", typeof(AutomationElement)));

            dataTable.Columns.Add(new DataColumn("aeInvokePattern", typeof(object)));

 

            DataColumn[] pk = new DataColumn[1];

            pk[0] = new DataColumn("aeid", typeof(string));

            dataTable.Columns.Add(pk[0]);

            dataTable.PrimaryKey = pk;

 

            DataRow dr;

            object obj;

 

            foreach (AutomationElement ae in collection)

            {

                dr = dataTable.NewRow();

 

                dr["name"] = ae.Current.AutomationId;

                dr["aeid"] = ae.Current.GetHashCode();

                if (ae.TryGetCurrentPattern(InvokePattern.Pattern, out obj))

                {

                    dr["aeInvokePattern"] = obj;

                    dr["ae"] = ae;

                    dataTable.Rows.Add(dr);

 

                }

 

            }

 

            return dataTable;

 

        }

        public AECollection(string proname)

        {

            Process process = Process.Start(proname);

            Thread.Sleep(3000);

 

            IntPtr MainHWnd = process.MainWindowHandle;

 

            AutomationElement ae = AutomationElement.FromHandle(MainHWnd);

            Condition conditon = new AndCondition(

                new PropertyCondition(AutomationElement.IsInvokePatternAvailableProperty, true),

                new PropertyCondition(AutomationElement.IsEnabledProperty, true));

            collection = ae.FindAll(TreeScope.Subtree, conditon);

 

            AutomationElement ae2 = AutomationElement.FromHandle(MainHWnd);

            Condition condition = new AndCondition(new PropertyCondition(AutomationElement.IsTextPatternAvailableProperty, true),

                new PropertyCondition(AutomationElement.IsValuePatternAvailableProperty, true),

                new PropertyCondition(AutomationElement.IsEnabledProperty, true));

 

            textboxcollection = ae2.FindAll(TreeScope.Subtree, condition);

        }

 

        public bool InvokeAE(string aeid)

        {

            DataRow dr = dataTable.Rows.Find(aeid);

 

            if (dr == null)

            {

                return false;

            }

 

            object obj = dr["aeInvokePattern"];

            InvokePattern invokePattern = obj as InvokePattern;

 

            invokePattern.Invoke();

            return true;

        }

 

 

 

 

        internal bool SetValue(string aeid , string textboxtext)

        {

            DataRow dr = dataTable2.Rows.Find(aeid);

 

            if (dr == null)

            {

                return false;

            }

 

            object obj = dr["aeVal‎uePattern"];

            ValuePattern vp = obj as ValuePattern;

 

            vp.SetValue(textboxtext);

            return true;

        }

    }

}

 

초기 생성자에서 파일(Main에서 OpenFileDialog 로 열기한 exe파일) 이름을 받아와 프로그램을 실행 시키고 해당 프로세스의 핸들 값을 받아온다.

AutomationElement.IsInvokePatternAvailableProperty,

AutomationElement.IsEnabledProperty,

이러한 패턴을 가지고 있는 컨트롤을 뽑아내서 AutomationElementCollection 개체에 보관을 하고 마찬가지로 텍스트 박스를 뽑아 내기 위해서는

AutomationElement.IsTextPatternAvailableProperty,

AutomationElement.IsValuePatternAvailableProperty,
AutomationElement.IsEnabledProperty

이러한 패턴을 가지고 있는 컨트롤들을 뽑아내서 컬렉션에 보관한다. 보관후 DataTable(싱글톤으로 되어 있다.) 추가하고 TryGetCurrentPattern 이용해 해당 패턴을 검색후 참일경우 DataTable 추가된다.

 

다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
맨위로

카페 검색

카페 검색어 입력폼