이번에는 원격 제어 대상 호스트를 구현합시다.
먼저 키보드와 마우스 이벤트를 래핑한 클래스를 정의합니다. 이 부분은 앞에서 만들었던 것과 대부분 비슷하므로 설명하지 않겠습니다.
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