원격 제어 프로그램 16. 원격 제어 호스트

이제 원격 제어 호스트를 표현할 Remote 클래스를 구현해 봅시다.

public class Remote
{

원격 제어 호스트 개체도 프로그램에 유일해야 하므로 단일체 패턴을 적용할게요. 먼저 클래스 내에 정적 멤버 필드로 단일 개체를 선언하세요.

    static Remote singleton;//단일 개체

다른 형식에서 단일 개체를 가져오기 할 수 있게 속성을 제공하세요.

    public static Remote Singleton
    {
        get
        {
            return singleton;
        }
    }

단일 개체는 정적 생성자에서 생성하세요.

    static Remote()
    {
        singleton = new Remote(); //단일 개체 생성
    }

폼에서는 원격 제어 요청이 왔을 때 요청자의 정보를 화면에 표시해 주어야 합니다. 이를 위해 원격 제어 요청 메시지 수신 이벤트를 선언하세요.

    //원격 제어 요청 메시지 수신 이벤트
    public event RecvRCInfoEventHandler RecvedRCInfo = null;

폼이나 다른 클래스에서 원격 제어 컨트롤로에서 보낸 키보드와 마우스 이벤트를 수신하였을 때의 이벤트 정보를 구독할 수 있게 이벤트를 선언하세요.

    //키보드, 마우스 메시지 수신 이벤트
    public event RecvKMEEventHandler RecvedKMEvent = null;

원격 제어 호스트에는 컨트롤러에서 보내는 키보드와 마우스 메시지를 수신할 서버가 필요합니다.

    RecvEventServer res = null;//키보드, 마우스 메시지 수신 서버

원격 제어 호스트는 주기적으로 데스크톱 화면 영역을 캡쳐하여 이미지를 전송해야 합니다. 여기에서는 데스크톱 화면 영역을 가져오기 할 수 있게 속성을 제공합시다.

    public Rectangle Rect
    {
        get;
        private set;
    }

생성자의 가시성을 private으로 접근 지정하여 단일 개체외에 다른 개체를 생성할 수 없게 하세요.

    private Remote()
    {

UI 자동화 기술에서는 최상위 자동화 요소를 구하는 정적 속성을 제공하고 있습니다. 다른 방법도 있지만 여기에서는 이를 이용하여 데스크 톱의 사각 영역을 구하기로 할게요. 이 방법은 일반적인 방법은 아닙니다. 제가 소프트웨어 접근성에 관심을 갖고 있어서 이를 사용한 것일 뿐입니다.

        //최상위 자동화 요소 구하기
        AutomationElement ae = AutomationElement.RootElement;
        //사각 영역 구하기
        System.Windows.Rect rt = ae.Current.BoundingRectangle;
        //Rectangle 형식으로 변환
        Rect = new Rectangle((int)rt.Left, (int)rt.Top, (int)rt.Width, (int)rt.Height);

UI 자동화 기술을 사용하기 위해서는 [그림 9]처럼 UI 자동화 기술에 관한 .NET 어셈블리를 참조 추가해야 합니다. UIAutomationClient.dll, UIAutomationClientSideProviders.dll, UIAutomationTypes.dll을 참조 추가하세요. 그리고 Rect 형식은 WindowsBase.dll을 참조 추가해야 사용할 수 있습니다.

(개발 도구의 버전에 따라 창의 모습이 다를 수 있습니다.)

[그림 9] 참조 추가 창
[그림 9] 참조 추가 창

원격 제어 요청이 왔을 때 이를 알기 위해 원격 제어 요청 수신 이벤트 핸들러를 등록하세요.

        //원격제어 요청 수신 이벤트 메시지 핸들러 등록
        SetupServer.RecvedRCInfo += new RecvRCInfoEventHandler(SetupServer_RecvedRCInfo);

원격 제어 요청을 받을 수 있게 서버를 가동하세요.

        SetupServer.Start(MyIP, NetworkInfo.SetupPort);//원격 제어 요청 서버 가동
    }

원격 제어 요청이 왔을 때 처리할 이벤트 핸들러를 구현합시다.

    void SetupServer_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
    {

원격 제어 요청 수신 구독자가 있다면 수신한 이벤트를 By Pass 하세요.

        if (RecvedRCInfo != null)//원격 제어 요청 수신 구독자가 있을 때
        {
            RecvedRCInfo(this, e);//원격 제어 요청 수신 이벤트 발생(By Pass)
        }
    }

로컬 IP 문자열을 속성으로 제공하여 사용하기 편하게 합시다.

    public string MyIP
    {
        get
        {
            return NetworkInfo.DefaultIP;
        }
    }

메시지 수신 서버를 가동하는 메서드를 구현합시다.

    public void RecvEventStart()
    {

메시지 수신 서버를 가동하세요.

        res = new RecvEventServer(MyIP, NetworkInfo.EventPort);//메시지 수신 서버 가동

그리고 키보드, 마우스 이벤트 수신 이벤트 핸들러를 등록하세요.

        //키보드, 마우스 이벤트 수신 이벤트 핸들러 등록
        res.RecvedKMEvent += new RecvKMEEventHandler(res_RecvKMEEventHandler);
    }

키보드와 마우스 메시지를 수신하였을 때 처리하는 이벤트 핸들러를 구현합시다.

    void res_RecvKMEEventHandler(object sender, RecvKMEEventArgs e)
    {

만약 수신 이벤트 핸들러가 있으면 이벤트를 By Pass 하세요.

        if (RecvedKMEvent != null)//메시지 수신 이벤트 핸들러가 있을 때
        {
            RecvedKMEvent(this, e);//이벤트 발생(By Pass)
        }

그리고 수신한 이벤트에 따라 프로그램 방식으로 이벤트를 발생하세요.

        //수신한 이벤트에 따라 프로그램 방식으로 이벤트 발생
        switch (e.MT)
        {
            case MsgType.MT_KDOWN: WrapNative.KeyDown(e.Key); break;
            case MsgType.MT_KEYUP: WrapNative.KeyUp(e.Key); break;
            case MsgType.MT_M_LEFTDOWN: WrapNative.LeftDown(); break;
            case MsgType.MT_M_LEFTUP: WrapNative.LeftUp(); break;
            case MsgType.MT_M_MOVE: WrapNative.Move(e.Now); break;
        }
    }

원격 제어 호스트를 멈추는 메서드를 제공합시다.

    public void Stop()
    {

Setup 서버와 메시지 수신 서버를 닫습니다.

        SetupServer.Close();//Setup 서버 닫기
        if (res != null)
        {
            res.Close();//메시지 수신 서버 닫기
            res = null;
        }
    }
}

다음은 이번 실습에서 작성한 소스 코드입니다.

//Remote.cs
using System.Windows.Automation;
using System.Drawing;

namespace 원격제어기
{
    /// <summary>
    /// 원격 제어 호스트 클래스
    /// </summary>
    public class Remote
    {
        static Remote singleton;//단일개체
        /// <summary>
        /// 단일 개체 - 가져오기
        /// </summary>
        public static Remote Singleton
        {
            get
            {
                return singleton;
            }
        }
        static Remote()
        {
            singleton = new Remote(); //단일 개체 생성
        }

        //원격제어 요청 메시지 수신 이벤트
        public event RecvRCInfoEventHandler RecvedRCInfo = null;

        //키보드, 마우스 메시지 수신 이벤트
        public event RecvKMEEventHandler RecvedKMEvent = null;

        RecvEventServer res = null;//키보드, 마우스 메시지 수신 서버

        /// <summary>
        /// 데스크톱 사각 영역 - 가져오기
        /// </summary>
        public Rectangle Rect
        {
            get;
            private set;
        }

        private Remote()
        {
            //최상위 자동화 요소 구하기
            AutomationElement ae = AutomationElement.RootElement;
            //사각 영역 구하기
            System.Windows.Rect rt = ae.Current.BoundingRectangle;
            // 형식 변환
            Rect = new Rectangle((int)rt.Left, 
                                 (int)rt.Top, 
                                 (int)rt.Width, 
                                 (int)rt.Height);

            //원격제어 요청 수신 이벤트 메시지 핸들러 등록
            SetupServer.RecvedRCInfo += 
                        new RecvRCInfoEventHandler(SetupServer_RecvedRCInfo);
            SetupServer.Start(MyIP, NetworkInfo.SetupPort);//원격제어 요청 서버 가동
        }

        void SetupServer_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
        {
            if (RecvedRCInfo != null)//원격 제어 요청 수신 구독자가 있을 때
            {
                RecvedRCInfo(this, e);//원격 제어 요청 수신 이벤트 발생(By Pass)

            }
        }

        /// <summary>
        /// 로컬 IP 문자열 - 가져오기
        /// </summary>
        public string MyIP
        {
            get
            {
                return NetworkInfo.DefaultIP;

            }
        }

        /// <summary>
        /// 메시지 수신 서버 가동
        /// </summary>
        public void RecvEventStart()
        {
            //메시지 수신 서버 가동
            res = new RecvEventServer(MyIP, NetworkInfo.EventPort);

            //키보드, 마우스 이벤트 수신 이벤트 핸들러 등록
            res.RecvedKMEvent += new RecvKMEEventHandler(res_RecvKMEEventHandler);
        }

        void res_RecvKMEEventHandler(object sender, RecvKMEEventArgs e)
        {
            if (RecvedKMEvent != null)//메시지 수신 이벤트 핸들러가 있을 때
            {
                RecvedKMEvent(this, e);//이벤트 발생(By Pass)
            }

            //수신한 이벤트에 따라 프로그램 방식으로 이벤트 발생
            switch (e.MT)
            {
                case MsgType.MT_KDOWN: WrapNative.KeyDown(e.Key); break;
                case MsgType.MT_KEYUP: WrapNative.KeyUp(e.Key); break;
                case MsgType.MT_M_LEFTDOWN: WrapNative.LeftDown(); break;
                case MsgType.MT_M_LEFTUP: WrapNative.LeftUp(); break;
                case MsgType.MT_M_MOVE: WrapNative.Move(e.Now); break;
            }
        }


        /// <summary>
        /// 원격 제어 호스트 닫기
        /// </summary>
        public void Stop()
        {
            SetupServer.Close();//Setup 서버 닫기
            if (res != null)
            {
                res.Close();//메시지 수신 서버 닫기
                res = null;
            }
        }
    }
}