가상 커서와 원격 제어 화면과 상대 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(); } } } }
이상으로 원격 제어 프로그램에 관한 소개를 마칠게요. 여러분께서는 비동기 소켓 통신으로 버그를 개선하고 다른 기술과 융합하여 보다 나은 프로그램을 작성할 수 있기를 기대합니다.