마. 원격 제어 대상 호스트

이번에는 원격 제어 대상 호스트를 구현합시다.

먼저 키보드와 마우스 이벤트를 래핑한 클래스를 정의합니다. 이 부분은 앞에서 만들었던 것과 대부분 비슷하므로 설명하지 않겠습니다.

using System;
using System.Runtime.InteropServices;
using System.Drawing;
namespace 원격제어기
{
    [Flags]
    public enum MouseFlag
    {
        ME_MOVE = 1,  ME_LEFTDOWN = 2,    ME_LEFTUP = 4,
        ME_RIGHTDOWN = 8,   ME_RIGHTUP = 0x10,   ME_MIDDLEDOWN = 0x20,
        ME_MIDDLEUP = 0x40,  ME_WHEEL = 0x800,    ME_ABSOULUTE = 8000
    }
    /// <summary>
    /// 키보드 이벤트 열거형
    /// </summary>
    [Flags]
    public enum KeyFlag
    {
        /// <summary>
        /// 키 누름
        /// </summary>
        KE_DOWN = 0,
        /// <summary>
        /// 확장 키
        /// </summary>
        KE__EXTENDEDKEY = 1,
        /// <summary>
        /// 키 뗌
        /// </summary>
        KE_UP = 2
    }
    /// <summary>
    /// Native(Win32 API) 기술 래핑 클래스(정적 클래스)
    /// </summary>
    public static class WrapNative
    {
        [DllImport("user32.dll")]
        static extern void mouse_event(int flag, int dx, int dy, int buttons, int extra);
        [DllImport("user32.dll")]
        static extern bool GetCursorPos(ref Point point);
        [DllImport("user32.dll")]
        static extern int SetCursorPos(int x, int y);
        [DllImport("User32.dll")]
        static extern void keybd_event(byte vk, byte scan, int flags, int extra);
        /// <summary>
        /// 키 누름(DOWN) 이벤트를 발생시키는 메서드
        /// </summary>
        /// <param name="keycode">키</param>
        public static void KeyDown(int keycode)
        {
            keybd_event((byte)keycode, 0, (int)KeyFlag.KE_DOWN, 0);
        }
        /// <summary>
        /// 키 뗌(UP) 이벤트를 발생시키는 메서드
        /// </summary>
        /// <param name="keycode">키</param>
        public static void KeyUp(int keycode)
        {
            keybd_event((byte)keycode, 0, (int)KeyFlag.KE_UP, 0);
        }
        /// <summary>
        /// 마우스 좌표를 바꾸는 메서드
        /// </summary>
        /// <param name="x">바꿀 X 좌표</param>
        /// <param name="y">바꿀 Y 좌표</param>
        public static void Move(int x, int y)
        {
            SetCursorPos(x, y);
        }
        /// <summary>
        /// 마우스 좌표를 바꾸는 메서드
        /// </summary>
        /// <param name="pt">바꿀 포인트</param>
        public static void Move(Point pt)
        {
            Move(pt.X, pt.Y);
        }
        /// <summary>
        /// 프로그램 방식으로 마우스 왼쪽 버튼 누름 이벤트 발생시키는 메서드
        /// </summary>
        public static void LeftDown()
        {
            mouse_event((int)MouseFlag.ME_LEFTDOWN, 0, 0, 0, 0);
        }
        /// <summary>
        /// 프로그램 방식으로 마우스 왼쪽 버튼 뗌 이벤트 발생시키는 메서드
        /// </summary>
        public static void LeftUp()
        {
            mouse_event((int)MouseFlag.ME_LEFTUP, 0, 0, 0, 0);
        }
    }
}

[소스 9.12] WrapNative.cs

원격 제어 대상 호스트도 단일체 패턴을 적용합시다. 단일체를 기억하는 정적 멤버 필드와 이를 가져오기 할 수 있는 속성을 제공합니다.

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()
    {

자동화 요소의 루트 요소를 참조하여 사각 영역을 구합니다.

        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의 원격 제어 요청이 왔을 때 처리할 이벤트 핸들러를 등록합니다.

        SetupServer.RecvedRCInfo += new RecvRCInfoEventHandler(SetupServer_RecvedRCInfo);

그리고 SetupServer를 가동합니다. 여기에서는 포트 번호를 20002으로 하드 코딩할게요.

        SetupServer.Start(MyIP, 20002);
    }

원격 제어 요청 이벤트는 By Pass 합니다.

    void SetupServer_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
    {
        if (RecvedRCInfo != null){    RecvedRCInfo(this, e);    }
    }

자신의 IP를 가져오기 속성을 제공합시다.

    public string MyIP
    {
        get{    return Controller.Singleton.MyIP;    }
    }

키보드와 마우스 이벤트를 수신하는 서버를 가동하는 메서드를 제공합시다.

    public void RecvEventStart()
    {

포트 번호는 20010으로 하드 코딩할게요.

        res = new RecvEventServer(MyIP, 20010);

res 개체의 메시지를 수신하였을 때 처리할 이벤트 핸들러를 등록합니다.

        res.RecvedKMEvent += new RecvKMEEventHandler(res_RecvKMEEventHandler);
    }
    void res_RecvKMEEventHandler(object sender, RecvKMEEventArgs e)
    {

자신에게 등록한 이벤트 핸들러가 있으면 By Pass 합니다.

        if (RecvedKMEvent != null){    RecvedKMEvent(this, e);    }

이벤트 종류에 따라 래핑한 API 개체의 메서드를 호출하여 프로그램 방식으로 이벤트를 발생하게 합니다.

        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()
    {

SetupServer를 닫고 키보드와 마우스 이벤트 메시지를 수신 서버도 닫습니다.

        SetupServer.Close();
        if (res != null){    res.Close();    res = null;  }
    }
}
using System.Windows.Automation;
using System.Drawing;
namespace 원격제어기
{
    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 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, 20002);
        }
        void SetupServer_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
        {
            if (RecvedRCInfo != null)
            {
                RecvedRCInfo(this, e);
            }
        }
        public string MyIP
        {
            get{    return Controller.Singleton.MyIP;    }
        }
        public void RecvEventStart()
        {
            res = new RecvEventServer(MyIP, 20010);
            res.RecvedKMEvent += new RecvKMEEventHandler(res_RecvKMEEventHandler);
        }
        void res_RecvKMEEventHandler(object sender, RecvKMEEventArgs e)
        {
            if (RecvedKMEvent != null){    RecvedKMEvent(this, e);    }
            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()
        {
            SetupServer.Close();
            if (res != null){    res.Close();    res = null;    }
        }
    }
}

[소스 9.12] Remote.cs