원격 제어 프로그램 19. MainForm

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

string sip; //상대 IP
int sport;  //상대 port
RemoteCleintForm rcf = null;//원격 호스트 화면(제어 화면)
VirtualCursorForm vcf = null;//가상 커서

속성 창을 이용해서 Load 이벤트 핸들러를 등록하세요.

private void MainForm_Load(object sender, EventArgs e)
{

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

    vcf = new VirtualCursorForm(); //가상 커서 생성
    rcf = new RemoteCleintForm(); //원격 호스트 화면 폼 생성

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

    //원격 제어 요청 수신 이벤트 핸들러 등록
    Remote.Singleton.RecvedRCInfo += new RecvRCInfoEventHandler(Remote_RecvedRCInfo);
}

크로스 스레드 문제가 발생할 수 있으니 다음처럼 문제를 해결하세요. Windows Forms 응용에서는 컨트롤을 소유하고 있는 스레드가 아닌 다른 스레드에서 컨트롤의 상태를 변경하려고 할 때 크로스 스레드 문제가 발생합니다.

이를 확인하려면 컨트롤의 InvokeRequired 속성을 확인합니다. 만약 InvokeRequired 속성이 true라면 현재 수행하는 스레드는 컨트롤을 소유하지 않은 스레드라는 의미입니다. 이 때 컨트롤의 Invoke 메서드를 호출하면 .NET 프레임워크에서는 컨트롤을 소유한 스레드가 대행하게 해 줍니다.

다음은 수신한 상대 정보를 컨트롤을 통해 표시하는 부분으로 크로스 스레드 문제를 해결하는 코드로 작성하였습니다.

delegate void Remote_Dele(object sender, RecvRCInfoEventArgs e);
void Remote_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
{
    if (this.InvokeRequired)
    {
       object[] objs = new object[2]{sender,e};
       this.Invoke(new Remote_Dele(Remote_RecvedRCInfo), objs);
    }
    else
    {

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

        tbox_controller_ip.Text = e.IPAddressStr;// 요청 IP 주소를 표시
        sip = e.IPAddressStr; //요청 IP 주소 설정
        sport = e.Port; //요청 포트 설정

요청 수락할 수 있게 버튼을 활성화합니다.

        btn_ok.Enabled = true; //요청 수락 버튼 활성화
    }
}

속성 창을 이용하여 FormClosed 이벤트 핸들러를 등록하세요.

private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
{

원격 호스트와 컨트롤러를 멈춥니다.

    Remote.Singleton.Stop();//원격 호스트 멈춤
    Controller.Singleton.Stop();//원격 컨트롤러 멈춤

응용을 종료합니다.

    Application.Exit();
}

btn_setting 컨트롤의 Click 이벤트 핸들러를 등록하세요.

private void btn_setting_Click(object sender, EventArgs e)
{

원격 제어 요청한 IP 주소가 로컬 IP 주소와 같으면 메시지 창으로 통보하고 이벤트 핸들러를 끝냅니다.

    if (tbox_ip.Text == NetworkInfo.DefaultIP) //자신의 아이피와 같을 때
    {
        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.ClientSize = new Size(rect.Width - 40, rect.Height - 80);
    rcf.Show();//원격 제어 화면 폼 시각화
}

btn_ok 컨트롤의 Click 이벤트 핸들러를 등록하세요.

private void btn_ok_Click(object sender, EventArgs e)
{

MainForm을 숨깁니다.

    this.Hide(); //자신의 화면을 숨기기

원격 제어 이벤트 수신 서버를 가동하여 원격 제어 컨트롤러의 제어 메시지를 수신합니다.

    Remote.Singleton.RecvEventStart();//원격 제어 이벤트 수신 서버 가동

이미지 전송 타이머를 가동하여 주기적으로 데스크 톱 화면을 전송할 수 있게 합니다.

    timer_send_img.Start(); //이미지 전송 타이머 가동

가상 커서를 시각화하여 원격 제어 컨트롤러가 제어하기 쉽게 해 줍니다.

    vcf.Show(); //가상 커서 시각화
}

noti 컨트롤의 MouseDoubleClick 이벤트 핸들러를 등록하세요.

private void noti_MouseDoubleClick(object sender, MouseEventArgs e)
{

숨겼던 자신을 시각화합니다.

    this.Show(); //자신을 시각화
}

이미지 전송 타이머인 timer_send_img 컨트롤의 Tick 이벤트 핸들러를 등록하세요.

private void timer_send_img_Tick(object sender, EventArgs e)
{

사각 영역을 구하여 비트맵 개체를 생성하고 Graphics 개체를 생성합니다.

    Rectangle rect = Remote.Singleton.Rect; //사각 영역 구함
    Bitmap bitmap = new Bitmap(rect.Width, rect.Height); //비트맵 생성
    Graphics gp = Graphics.FromImage(bitmap);//비트맵에서 Graphics 개체 생성

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

Graphics 개체로 화면을 복사합니다.

    gp.CopyFromScreen(new Point(0, 0), new Point(0, 0), size2);//화면 복사
    gp.Dispose();
    try
    {

복사한 이미지를 비동기로 전송합니다.

        ImageClient ic = new ImageClient(sip, NetworkInfo.ImgPort);
        ic.SendImageAsync(bitmap, null);//이미지를 비동기로 전송
    }

예외가 발생하면 프로그램을 닫습니다.

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

이상으로 원격 제어 프로그램 만들기가 끝났습니다. 테스트를 해 보시고 보다 나은 프로그램으로 수정해 보세요.

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

//MainForm.cs
using System;
using System.Drawing;
using System.Windows.Forms;

namespace 원격제어기
{
    public partial class MainForm : Form
    {        
        string sip; //상대 IP
        int sport;  //상대 port
        RemoteCleintForm rcf = null;//원격 호스트 화면(제어 화면)
        VirtualCursorForm vcf = 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);
        }
        delegate void Remote_Dele(object sender, RecvRCInfoEventArgs e);
        void Remote_RecvedRCInfo(object sender, RecvRCInfoEventArgs e)
        {
            if (this.InvokeRequired)
            {
                object[] objs = new object[2]{sender,e};
                this.Invoke(new Remote_Dele(Remote_RecvedRCInfo), objs);
            }
            else
            {
                tbox_controller_ip.Text = e.IPAddressStr;// 요청 IP 주소를 표시
                sip = e.IPAddressStr; //요청 IP 주소 설정
                sport = e.Port; //요청 포트 설정
                btn_ok.Enabled = true; //요청 수락 버튼 활성화
            }
        }
        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 == NetworkInfo.DefaultIP) //자신의 아이피와 같을 때
            {
                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);//비트맵에서 Graphics 개체 생성

            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, NetworkInfo.ImgPort);
                ic.SendImageAsync(bitmap, null);//이미지를 비동기로 전송

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

이상으로 원격 제어 프로그램에 관한 소개를 마칠게요. 여러분께서는 비동기 소켓 통신으로 버그를 개선하고 다른 기술과 융합하여 보다 나은 프로그램을 작성할 수 있기를 기대합니다.