프로그래밍 기술/소프트웨어 접근성, UI 자동화

[S/W 접근성] 윈도우 옵저버 실습

언제나휴일 2016. 4. 26. 08:12
반응형

윈도우 옵저버 실습


 이번에는 컴퓨터 시스템에 윈도우가 열고 닫는 것을 관찰하는 윈도우 옵저버를 만들어 봅시다.


윈도우 옵저버 실행 화면

[그림] 윈도우 옵저버 실행 화면

 

 Form1에는 ListBox 컨트롤(Name: lbox_event)을 추가하였고 Dock속성을 Fill로 주었습니다.

 윈도우 닫힘 자동화 이벤트에서는 닫힌 윈도우의 자동화 요소를 전달하지 않습니다. 이를 처리하기 위해 자동화 요소를 래핑한 클래스를 만들어서 내부에서 이를 처리할게요.

public class EAE

{

 윈도우 닫힘 이벤트를 멤버로 추가합니다.

    public event AutomationEventHandler OnClose;

 자동화 요소 이름 가져오기 속성을 제공합시다.

    public string Name

    {

        get;

        private set;

    }

 생성자에서는 자동화 요소 개체를 입력 인자로 받습니다.

public EAE( AutomationElement ae)

{

 윈도우 창 닫힘 이벤트 핸들러를 생성합니다.

    AutomationEventHandler eh_close = new AutomationEventHandler(OnWindowClose);

 생성한 이벤트 핸들러를 등록합니다. 이벤트는 WindowPattern WindowClosedEvent 속성을 사용하며 TreeScope는 자기 자신인 Element로 전달합니다.

    Automation.AddAutomationEventHandler(

            WindowPattern.WindowClosedEvent, ae, TreeScope.Element, eh_close);

 자동화 요소 개체의 현재 이름을 Name 속성에 설정합니다.

    Name = ae.Current.Name;

}

 창 닫힘 이벤트 핸들러를 작성합시다.

private void OnWindowClose(object src, AutomationEventArgs e)

{

 만약 OnClose 이벤트 핸들러가 null이 아니면 이벤트 인자를 전달해 줍니다. 이 때 첫번째 인자로 Name 속성을 전달하기로 할게요.

    if (OnClose != null)

    {

        OnClose(Name, e);

    }

}

 

 

 Form1 Laod 이벤트 핸드러를 등록하세요.

private void Form1_Load(object sender, EventArgs e)

{

 AutomationElement 클래스이 정적 메서드로 RootElement를 참조합니다.

    AutomationElement ae = AutomationElement.RootElement;

 윈도우 창이 열릴 때 자동화 이벤트가 발생하면 처리할 자동화 이벤트 개체를 하나 생성합니다.

    AutomationEventHandler eh_open = new AutomationEventHandler(OnWindowOpen);

 그리고 이벤트 핸들러를 등록합니다. 이벤트 핸들러를 등록할 때 이벤트 종류는 WindowPattern 클래스의 정적 속성 WindowOpenedEvent이며 범위는 Subtree를 전달합니다.

    Automation.AddAutomationEventHandler(

        WindowPattern.WindowOpenedEvent, ae, TreeScope.Subtree, eh_open);

}

 OnWindowOpen 이벤트 핸들러를 구현합시다.

private void OnWindowOpen(object src, AutomationEventArgs e)

{

 이벤트 핸들러의 첫번째 인자를 자동화 요소로 참조합니다.

    AutomationElement se = src as AutomationElement;

 윈도우 닫힘 이벤트 처리를 위해 래핑한 개체를 생성합니다.

    EAE eae = new EAE(se);

 래핑한 개체의 OnClose 이벤트 핸들러를 등록하세요.

    eae.OnClose += new AutomationEventHandler(OnWindowClose);

 리스트 상자에 메시지를 추가합니다. 이 부분은 별도의 메서드로 작성할게요.

    AddMsg(se.Current.Name+"열림");

}

 

OnWindowClose 이벤트 핸드러를 등록합시다.

private void OnWindowClose(object src, AutomationEventArgs e)

{

 리스트 상자에 메시지를 추가하는 AddMsg 메서드를 호출합니다.

    string name = src.ToString();

    AddMsg(name+"닫힘");

}

 리스트 상자에 메시지를 추가하는 부분에서 크로스 스레드 문제가 발생합니다. 리스트 상자를 생성한 스레드와 윈도우 창이 닫힘을 전달하는 스레드가 달라서 발생하는 문제입니다. 이를 해결하기 위해 AddMsg와 시놉시스가 같은 대리자를 정의합니다.

delegate void AddMsgDele(string msg);

 AddMsg 메서드를 정의합시다.

private void AddMsg(string msg)

{

 만약 lbox_event 개체의 InvokeRequired 속성이 참이면 현재 스레드는 크로스 스레드입니다.

    if (lbox_event.InvokeRequired)

    {

        lbox_event.Invoke(new AddMsgDele(AddMsg), new object[] { msg });

    }

    else{    lbox_event.Items.Add(msg);    }

}


*Visual Studio 2010에서 작성한 프로젝트입니다.

윈도우 옵저버.zip


using System;

using System.Windows.Forms;

using System.Windows.Automation;

namespace 윈도우_옵저버

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

        private void Form1_Load(object sender, EventArgs e)

        {

            AutomationElement ae = AutomationElement.RootElement;

            AutomationEventHandler eh_open = new AutomationEventHandler(OnWindowOpen);

            Automation.AddAutomationEventHandler(

                WindowPattern.WindowOpenedEvent, ae, TreeScope.Subtree, eh_open);           

        }

        private void OnWindowOpen(object src, AutomationEventArgs e)

        {

            AutomationElement se;

            se = src as AutomationElement;

            EAE eae = new EAE(se);

            eae.OnClose += new AutomationEventHandler(OnWindowClose);

            AddMsg(se.Current.Name+"열림");

        }

        private void OnWindowClose(object src, AutomationEventArgs e)

        {

            string name = src.ToString();

            AddMsg(name+"닫힘");

        }

        delegate void AddMsgDele(string msg);

        private void AddMsg(string msg)

        {

            if (lbox_event.InvokeRequired)

            {

                lbox_event.Invoke(new AddMsgDele(AddMsg), new object[] { msg });

            }

            else

            {

                lbox_event.Items.Add(msg);

            }

        }

    }

    public class EAE

    {

        public event AutomationEventHandler OnClose;

        public string Name

        {

            get;

            private set;

        }

        public EAE( AutomationElement ae)

        {           

            AutomationEventHandler eh_close = new AutomationEventHandler(OnWindowClose);

            Automation.AddAutomationEventHandler(

                WindowPattern.WindowClosedEvent, ae, TreeScope.Element, eh_close);

            Name = ae.Current.Name;

        }

        private void OnWindowClose(object src, AutomationEventArgs e)

        {

            if (OnClose != null)

            {

                OnClose(Name, e);

            }

        }

    }

}

[소스] Form1.cs


반응형