바. 폼

먼저 가상 커서를 정의합시다. 원격 제어를 하는 곳에서는 상대 호스트의 마우스 위치를 화면에 표시해야 합니다. 이 부분을 도구 스타일의 작은 폼으로 정의합시다. 프로젝트에 폼을 하나 추가합니다. 여기에서는 VirtualCursorForm이라고 명명할게요.

Form의 AutoSizeMode를 GrowAndShrink로 설정하세요. BackColor는 Red, FormBorderSytle은 None, TopMost를 True로 지정합니다.

폼의 Load 이벤트 핸들러를 등록하세요.

private void VirtualCursorForm_Load(object sender, EventArgs e)
{

Remote 클래스 단일체의 키보드와 마우스 이벤트 수신 이벤트 핸들러를 등록하세요.

    Remote.Singleton.RecvedKMEvent +=
                new RecvKMEEventHandler(Singleton_RecvedKMEEvent);
}
void Singleton_RecvedKMEEvent(object sender, RecvKMEEventArgs e)
{

만약 수신한 메시지가 마우스 이동 이벤트일 때 위치를 변경합니다.

    if (e.MT == MsgType.MT_M_MOVE)
    {
        Location = new Point(e.Now.X + 3, e.Now.Y + 3);
    }
}
using System;
using System.Drawing;
using System.Windows.Forms;

namespace 원격제어기
{
    public partial class VirtualCursorForm : Form
    {
        public VirtualCursorForm()
        {            
            InitializeComponent();
        }

        private void VirtualCursorForm_Load(object sender, EventArgs e)
        {
            Remote.Singleton.RecvedKMEvent += 
                 new RecvKMEEventHandler(Singleton_RecvedKMEEvent);
        }

        void Singleton_RecvedKMEEvent(object sender, RecvKMEEventArgs e)
        {
            if (e.MT == MsgType.MT_M_MOVE)
            {
                Location = new Point(e.Now.X + 3, e.Now.Y + 3);
            }
        }
    }
}

[소스 9.13] VirtualCursorForm.cs

이번에는 원격 제어할 때 대상 호스트의 화면을 표시하는 RemoteCleintForm 폼을 추가하세요.

자식 컨트롤로 PictureBox 컨트롤을 추가하고 이름을 pbox_remote으로 정합시다. PictureBox 컨트롤의 Dock 속성을 Fill로 설정하고 SizeMode 속성르 StretchImage로 설정하세요.

멤버 필드로 이미지를 수신하였는지 확인하는 멤버와 이미지 크기를 기억할 멤버를 선언하세요.

bool check;
Size csize;

이미지 전송 클라이언트 개체를 가져오기하는 속성을 정의하여 코드를 간결하게 합시다.

SendEventClient EventSC
{
    get{    return Controller.Singleton.SendEventClient;    }
}

폼의 Load 이벤트 핸들러를 등록합니다.

private void RemoteCleintForm_Load(object sender, EventArgs e)
{

원격 제어기 단일체의 이미지 수신 이벤트 핸들러를 등록합니다.

    Controller.Singleton.RecvedImage +=
              new RecvImageEventHandler(Singleton_RecvImageEventHandler);
}
void Singleton_RecvImageEventHandler(object sender, RecvImageEventArgs e)
{
    if (check == false)
    {

check가 false일 때 키보드, 마우스 이벤트를 송신하는 클라이언트를 가동하고 초기 설정합니다.

        Controller.Singleton.StartEventClient();
        check = true;
        csize = e.Image.Size;
    }

그리고 이벤트로 전달받은 이미지로 PictureBox 컨트롤의 Image 속성을 설정합니다.

    pbox_remote.Image = e.Image;
}

폼의 KeyDown 이벤트 핸들러를 등록합니다.

private void RemoteCleintForm_KeyDown(object sender, KeyEventArgs e)
{

check가 참일 때 누른 키를 이벤트 전송 클라이언트에게 보냅니다.

    if (check == true){    EventSC.SendKeyDown(e.KeyValue);    }
}

폼의 KeyUp 이벤트를 등록합니다.

private void RemoteCleintForm_KeyUp(object sender, KeyEventArgs e)
{

마찬가지로 check가 참일 때 뗀 키를 이벤트 전송 클라이언트에게 보냅니다.

    if (check == true){    EventSC.SendKeyUp(e.KeyValue);    }
}

폼의 MouseMove 이벤트도 등록합니다.

private void RemoteCleintForm_MouseMove(object sender, MouseEventArgs e)
{

check가 참을 때 마우스 좌표를 인자를 변환하여 이벤트 전송 클라이언트에게 보냅니다.

    if (check == true)
    {
        Point pt = ConvertPoint(e.X, e.Y);    EventSC.SendMouseMove(pt.X, pt.Y);
    }
}
private Point ConvertPoint(int x, int y)
{

마우스의 좌표를 대상 호스트의 좌표로 변환하여 반환합니다.

    int nx = csize.Width * x / pbox_remote.Width;
    int ny = csize.Height * y / pbox_remote.Height;
    return new Point(nx, ny);
}

폼의 MouseDown 이벤트 핸들러도 등록합니다.

private void RemoteCleintForm_MouseDown(object sender, MouseEventArgs e)
{

현재 마우스 좌표를 얻어와서 이벤트 전송 클라이언트에게 보냅니다.

    if (check == true)
    {
        Text = e.Location.ToString();
        EventSC.SendMouseDown(e.Button);
    }
}

폼의 MouseUp 이벤트 핸들러도 등록합니다.

private void RemoteCleintForm_MouseUp(object sender, MouseEventArgs e)
{

버튼 정보를 이벤트 전송 클라이언트에게 보냅니다.

    if (check == true){    EventSC.SendMouseUp(e.Button);    }
}
using System;
using System.Drawing;
using System.Windows.Forms;

namespace 원격제어기
{
    public partial class RemoteCleintForm : Form
    {
        bool check;
        Size csize;
        SendEventClient EventSC
        {
            get
            {
                return Controller.Singleton.SendEventClient;
            }
        }
        public RemoteCleintForm()
        {            
            InitializeComponent();
        }
        private void RemoteCleintForm_Load(object sender, EventArgs e)
        {
            Controller.Singleton.RecvedImage += 
                new RecvImageEventHandler(Singleton_RecvImageEventHandler);
        }

        void Singleton_RecvImageEventHandler(object sender, RecvImageEventArgs e)
        {
            if (check == false)
            {
                Controller.Singleton.StartEventClient();
                check = true;
                csize = e.Image.Size;
            }
            pbox_remote.Image = e.Image;
        }
        private void RemoteCleintForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (check == true)
            {
                EventSC.SendKeyDown(e.KeyValue);
            }
        }
        private void RemoteCleintForm_KeyUp(object sender, KeyEventArgs e)
        {
            if (check == true)
            {
                EventSC.SendKeyUp(e.KeyValue);
            }
        }
        private void RemoteCleintForm_MouseMove(object sender, MouseEventArgs e)
        {
            if (check == true)
            {
                Point pt = ConvertPoint(e.X, e.Y);
                EventSC.SendMouseMove(pt.X, pt.Y);
            }
        }
        private Point ConvertPoint(int x, int y)
        {
            int nx = csize.Width * x / pbox_remote.Width;
            int ny = csize.Height * y / pbox_remote.Height;
            return new Point(nx, ny);
        }
        private void RemoteCleintForm_MouseDown(object sender, MouseEventArgs e)
        {
            if (check == true)
            {
                Text = e.Location.ToString();
                EventSC.SendMouseDown(e.Button);
            }
        }
        private void RemoteCleintForm_MouseUp(object sender, MouseEventArgs e)
        {
            if (check == true)
            {
                EventSC.SendMouseUp(e.Button);
            }
        }
    }
}

[소스 9.14] RemoteClientForm.cs

마지막으로 MainForm을 구현합시다. 프로젝트 생성 시에 만들어진 Form1의 이름을 MainForm으로 변경하세요.

[그림 9.02] MainForm 자식 컨트롤 배치
[그림 9.02] MainForm 자식 컨트롤 배치
번호컨트롤 타입컨트롤 명설명
1TextBoxtbox_ip원격 제어 대상 호스트 IP 주소 입력
2Buttonbtn_setting원격 제어 대상 호스트에게 요청
3TextBoxtbox_controller_ip원격 제어기 호스트 IP 주소
4Buttonbtn_ok원격 제어 허용
5Timertimer_send_img이미지 수신 타이머
6NotifyIconnoti원격 제어를 당할 때 Notify 아이콘

[표 9.2] Form1의 컨트롤 배치

가상 커서와 원격 제어 화면과 상대 IP와 포트를 멤버를 선언합니다.

VirtualCursorForm vcf = null;
RemoteCleintForm rcf = null;
string sip;
int sport;

폼의 Load 이벤트 핸들러를 등록하세요.

private void MainForm_Load(object sender, EventArgs e)
{

가상 커서 폼과 원격 제어 호스트 화면을 표시할 폼을 생성합니다.

    vcf = new VirtualCursorForm();
    rcf = new RemoteCleintForm();

원격 제어 요청을 수신하였을 때의 처리 이벤트 핸들러를 등록합니다.

    Remote.Singleton.RecvedRCInfo += new RecvRCInfoEventHandler(Remote_RecvedRCInfo);
}
void Remote_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
{

상대 IP 주소를 텍스트 박스에 표시하고 IP와 포트 정보를 멤버에 설정합니다.

    tbox_controller_ip.Text = e.IPAddressStr;
    sip = e.IPAddressStr;
    sport = e.Port;
    btn_ok.Enabled = true;
}

폼의 FormClosed 이벤트 핸들러를 등록하여 해제화 작업을 작성합니다.

private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
{
    Remote.Singleton.Stop();
    Controller.Singleton.Stop();
    Application.Exit();
}

설정하기 버튼 클릭 이벤트 핸들러를 등록하세요.

private void btn_setting_Click(object sender, EventArgs e)
{
    if (tbox_ip.Text == Remote.Singleton.MyIP)
    {

입력한 IP가 자신의 IP일 때는 제어할 수 없음을 알려줍니다.

        MessageBox.Show("같은 호스트를 원격 제어할 수 없습니다.");
        tbox_ip.Text = string.Empty;    return;
    }

입력한 IP를 얻어와서 원격 제어기를 가동합니다.

    string host_ip = tbox_ip.Text;
    Rectangle rect = Remote.Singleton.Rect;
    Controller.Singleton.Start(host_ip);

화면 크기에 맞게 원격 제어 호스트의 화면을 띄우줄 rcf 폼의 크기를 설정하여 시각화합니다.

    rcf.ClientSize = new Size(rect.Width - 40, rect.Height - 80);
    rcf.Show();
}

원격 제어 허용 버튼의 클릭 이벤트 핸들러를 등록합니다.

private void btn_ok_Click(object sender, EventArgs e)
{

MainForm은 화면에서 숨깁니다.

    this.Hide();

키보드와 마우스 이벤트를 수신하는 Remote 클래스의 단일체를 가동합니다.

    Remote.Singleton.RecvEventStart();

이미지 수신 타이머도 가동하고 가상 커서도 시각화합니다.

    timer_send_img.Start();    vcf.Show();
}

Notify 아이콘의 더블 클릭 이벤트 핸들러를 등록합니다. 여기에서는 화면에서 숨겼던 자신을 시각화합니다.

private void noti_MouseDoubleClick(object sender, MouseEventArgs e)
{
    this.Show();
}

이미지 수신 타이머 이벤트 핸들러를 등록합니다.

private void timer_send_img_Tick(object sender, EventArgs e)
{

먼저 사각 영역을 얻어옵니다.

    Rectangle rect = Remote.Singleton.Rect;

얻어온 사각 영역 크기의 비트맵 개체를 생성합니다.

    Bitmap bitmap = new Bitmap(rect.Width, rect.Height);

이미지로부터 Graphics 개체를 구합니다.

    Graphics gp = Graphics.FromImage(bitmap);

사각 영역의 크기로 Size 개체를 생성합니다.

    Size size2 = new Size(rect.Width, rect.Height);

화면의 전체 이미지를 복사합니다.

    gp.CopyFromScreen(new Point(0, 0), new Point(0, 0), size2);

Graphics 개체는 더 이상 할 것이 없으므로 해제합니다.

    gp.Dispose();
    try
    {

이미지 전송 클라이언트 개체를 생성합니다. 이미지 수신 서버는 포트번호를 20004로 하드 코딩한 상태입니다.

        ImageClient ic = new ImageClient(sip, 20004);

이미지 전속 클라이언트 개체의 비동기 방식으로 이미지를 전송하는 SendImageAsync 메서드를 호출합니다.

        ic.SendImageAsync(bitmap, null);
    }

예외가 발생하면 이미지 전송 타이머를 멈추고 서버에 연결이 실패하였음을 알려줍니다.

    catch
    {
        timer_send_img.Stop();
        MessageBox.Show("서버에 연결 실패");
        this.Close();
    }
}

원격 제어 프로그램 만들기를 마칠게요. 이를 응용하는 것은 여러분의 몫으로 남깁니다.

using System;
using System.Drawing;
using System.Windows.Forms;
namespace 원격제어기
{
    public partial class MainForm : Form
    {
        VirtualCursorForm vcf = null;
        string sip;
        int sport;
        RemoteCleintForm rcf = null;
        public MainForm()
        {
            InitializeComponent();
        }
        private void MainForm_Load(object sender, EventArgs e)
        {
            vcf = new VirtualCursorForm();
            rcf = new RemoteCleintForm();
            Remote.Singleton.RecvedRCInfo += 
                         new RecvRCInfoEventHandler(Remote_RecvedRCInfo);
        }
        void Remote_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
        {
            tbox_controller_ip.Text = e.IPAddressStr;
            sip = e.IPAddressStr;
            sport = e.Port;
            btn_ok.Enabled = true;
        }
        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            Application.Exit();
            Remote.Singleton.Stop();
            Controller.Singleton.Stop();
        }
        private void btn_setting_Click(object sender, EventArgs e)
        {
            if (tbox_ip.Text == Remote.Singleton.MyIP)
            {
                MessageBox.Show("같은 호스트를 원격 제어할 수 없습니다.");
                tbox_ip.Text = string.Empty;
                return;
            }
            string host_ip = tbox_ip.Text;
            Rectangle rect = Remote.Singleton.Rect;
            Controller.Singleton.Start(host_ip);
            rcf.ClientSize = new Size(rect.Width - 40, rect.Height - 80);
            rcf.Show();
        }
        private void btn_ok_Click(object sender, EventArgs e)
        {
            this.Hide();
            Remote.Singleton.RecvEventStart();
            timer_send_img.Start();
            vcf.Show();
        }
        private void noti_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.Show();
        }        
        private void timer_send_img_Tick(object sender, EventArgs e)
        {
            Rectangle rect = Remote.Singleton.Rect;
            Bitmap bitmap = new Bitmap(rect.Width, rect.Height);
            Graphics gp = Graphics.FromImage(bitmap);

            Size size2 = new Size(rect.Width, rect.Height);
            gp.CopyFromScreen(new Point(0, 0), new Point(0, 0), size2);

            gp.Dispose();
            try
            {
                ImageClient ic = new ImageClient(sip, 20004);
                ic.SendImageAsync(bitmap, null);

            }
            catch
            {
                timer_send_img.Stop();
                MessageBox.Show("서버에 연결 실패");
                this.Close();
            }
        }
    }
}

[소스 9.15] MainForm.cs