이번에는 가상 키보드에 필요한 것은 아니지만 가상 마우스를 만든다고 하면 프로그램 방식으로 마우스 이벤트를 발생하는 방법을 알아야 합니다. 프로그램 방식으로 키보드 이벤트를 발생하는 부분과 비슷하므로 같이 다루기로 할게요.
Windows 32 API에서는 프로그램 방식으로 마우스 이벤트를 발생시키는 mouse_event 함수를 제공하고 있습니다.
다음은 프로그램 방식으로 키보드 이벤트를 발생시키는 mouse_event 함수 원형입니다.
WINUSERAPI VOID WINAPI mouse_event( __in DWORD dwFlags, __in DWORD dx, __in DWORD dy, __in DWORD dwData, __in ULONG_PTR dwExtraInfo);
여기에서는 키보드의 방향 키를 누르면 프로그램 방식으로 마우스 이벤트를 발생하여 마우스 좌표를 이동하는 예광탄을 만들어 보기로 합시다. 그리고 Home, End, PageUp, PageDown 키를 누르면 왼쪽, 오른쪽, 위, 아래로 10을 이동하게 할 것입니다. 또한 Space를 누르면 왼쪽 마우스 버튼 누름, Tap을 누르면 왼쪽 마우스 뗌, 엔터 키를 누르면 왼쪽 마우스 클릭 이벤트를 발생하게 할 것입니다.
먼저 컨트롤을 배치하세요.
번호 | 컨트롤 타입 | 컨트롤 명 | 설명 |
1 | ListBox | lbox_key | 제어 방법 도움말 항목 (Enabled 속성을 False로 설정) |
2 | ListBox | lbox_event | 발생한 이벤트 및 좌표 항목(Enabled 속성을 False로 설정) |
[표 8.2] Form1의 컨트롤 배치
먼저 Windows API를 래핑할 MouseController 클래스를 추가하세요.
마우스 이벤트 종류를 열거형으로 정의합시다. 그리고 조합을 사용할 수 있게 Flags 특성을 지정합니다.
[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 }
static class MouseController {
mouse_event를 상호 운영할 수 있게 선언합니다.
[DllImport("user32.dll")] static extern void mouse_event(int flag, int dx, int dy, int buttons, int extra);
현재 마우스 좌표를 얻어오는 GetCursorPos도 상호 운영할 수 있게 선언합니다.
[DllImport("user32.dll")] static extern bool GetCursorPos(ref Point point);
원하는 좌표로 마우스 좌표를 설정하는 SetCursorPos도 상호 운영할 수 있게 선언합니다.
[DllImport("user32.dll")] static extern int SetCursorPos(int x, int y);
현재 마우스 좌표를 얻어오는 메서드를 제공합시다.
public static Point GetPos() { Point pt = new Point(); 상호 운영하기 위해 선언한 GetCursorPos 메서드를 이용합니다. GetCursorPos 메서드의 첫번째 인자는 현재 마우스 좌표 값을 기억할 변수를 참조 형식의 인자로 전달합니다. GetCursorPos(ref pt); 얻어온 좌표를 반환합니다. return pt; }
마우스 좌표를 현재 위치에서 상대적인 거리로 이동하는 메서드를 정의합시다.
public static void MoveCXY(int cx, int cy) { 먼저 현재 좌표를 얻어옵니다. Point pt = GetPos(); 입력 인자로 전달받은 값을 반영합니다. pt.X += cx; pt.Y += cy; 변경한 좌표로 마우스 좌표를 설정합니다. SetCursorPos(pt.X, pt.Y); }
원하는 x,y 위치로 마우스 좌표를 이동하는 메서드도 제공합시다.
public static void Move(int x, int y) { SetCursorPos(x, y); }
Point 형식을 입력 인자로 받아 마우스 좌표를 이동하는 메서드도 제공합시다.
public static void Move(Point pt) { 여기에서는 앞에서 만든 Move 메서드를 이용합니다. Point 형식을 입력 인자로 받아 마우스 좌표를 이동하는 메서드를 제공하는 것은 사용하는 개발자의 편의성을 위해 선택의 폭을 넓히는 목적외에는 다른 의도는 없습니다. Move(pt.X, pt.Y); }
마우스 왼쪽 버튼 누름 메서드를 제공합시다.
public static void LeftDown() { mouse_event 메서드를 이용하여 프로그램 방식으로 마우스 왼쪽 버튼 누름 이벤트를 발생시킵니다. mouse_event((int)MouseFlag.ME_LEFTDOWN, 0, 0, 0, 0); }
같은 방식으로 마우스 왼쪽 버튼 뗌, 클릭, 더블 클릭 메서드를 구현합니다.
public static void LeftUp() { mouse_event((int)MouseFlag.ME_LEFTUP, 0, 0, 0, 0); } public static void Click() { mouse_event((int)MouseFlag.ME_LEFTDOWN, 0, 0, 0, 0); mouse_event((int)MouseFlag.ME_LEFTUP, 0, 0, 0, 0); } public static void DoubleClick() { Click(); Click(); } }
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 } static class MouseController { [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); public static Point GetPos() { Point pt = new Point(); GetCursorPos(ref pt); return pt; } public static void MoveCXY(int cx, int cy) { Point pt = GetPos(); pt.X += cx; pt.Y += cy; SetCursorPos(pt.X, pt.Y); } public static void Move(int x, int y) { SetCursorPos(x, y); } public static void Move(Point pt) { Move(pt.X, pt.Y); } public static void LeftDown() { mouse_event((int)MouseFlag.ME_LEFTDOWN, 0, 0, 0, 0); } public static void LeftUp() { mouse_event((int)MouseFlag.ME_LEFTUP, 0, 0, 0, 0); } public static void Click() { mouse_event((int)MouseFlag.ME_LEFTDOWN, 0, 0, 0, 0); mouse_event((int)MouseFlag.ME_LEFTUP, 0, 0, 0, 0); } public static void DoubleClick() { Click(); Click(); } } }
[소스 8.3] MouseController.cs
이제 Form1 을 작성합시다. 먼저 Form1의 Load 이벤트 핸들러를 등록하세요.
private void Form1_Load(object sender, EventArgs e) { 어느 키를 누르면 어떻게 동작하는지 도움 정보를 lbox_key 항목에 추가합니다. 이 프로그램에서는 상, 하, 좌, 우 방향 키와 PageUp, PageDown, Home, End, Space, Tap, Enter 키를 누르면 프로그램 방식으로 마우스를 이동하거나 왼쪽 마우스 버튼을 누리기, 뗌, 클릭 이벤트를 발생하게 할 것입니다. lbox_key.Items.Add("← 왼쪽으로 1 이동"); lbox_key.Items.Add("→ 오른쪽으로 1 이동"); lbox_key.Items.Add("↑ 위로 1 이동"); lbox_key.Items.Add("↓ 아래로 1 이동"); lbox_key.Items.Add("PageUp 위로 10 이동"); lbox_key.Items.Add("Home 왼쪽으로 10 이동"); lbox_key.Items.Add("PageDown 아래로 10 이동"); lbox_key.Items.Add("End 오른쪽으로 10 이동"); lbox_key.Items.Add("Space 마우스 왼쪽 버튼 누름"); lbox_key.Items.Add("Enter 마우스 왼쪽 버튼 클릭"); lbox_key.Items.Add("Tap 마우스 왼쪽 버튼 뗌"); 현재 마우스 좌표를 얻어와서 lbox_event 항목에 추가합니다. Point pt = MouseController.GetPos(); lbox_event.Items.Add(string.Format("마우스 좌표:{0}",pt)); }
Form1의 KeyDown 이벤트 핸드러를 등록하세요.
private void Form1_KeyDown(object sender, KeyEventArgs e) { 누른 키에 따라 발생할 마우스 이벤트의 종류는 다릅니다. 누른 키에 따라 MouseController의 메서드를 호출하고 lbox_event의 항목에 이벤트 정보를 추가합니다. switch (e.KeyCode) { case Keys.Left: MouseController.MoveCXY(-1, 0); lbox_event.Items.Add("왼쪽으로 1 이동"); break; ...중략... 제어에 사용하지 않는 키를 누르면 이벤트 핸들러를 종료합니다. default: return; }
프로그램 방식으로 마우스 이벤트를 발생시켰을 때마다 마우스 좌표를 얻어와서 lbox_event 항목에 추가합니다.
Point pt = MouseController.GetPos(); lbox_event.Items.Add(string.Format("마우스 좌표:{0}",pt)); lbox_event에 새로 발생한 이벤트가 화면에 시각화할 수 있게 선택 항목을 마지막 추가한 요소로 지정합니다. 이를 통해 마지막에 추가한 항목은 언제나 화면에 나타납니다. lbox_event.SelectedIndex = lbox_event.Items.Count - 1; }
using System; using System.Drawing; using System.Windows.Forms; namespace 마우스_이벤트_예광탄 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { lbox_key.Items.Add("← 왼쪽으로 1 이동"); lbox_key.Items.Add("→ 오른쪽으로 1 이동"); lbox_key.Items.Add("↑ 위로 1 이동"); lbox_key.Items.Add("↓ 아래로 1 이동"); lbox_key.Items.Add("PageUp 위로 10 이동"); lbox_key.Items.Add("Home 왼쪽으로 10 이동"); lbox_key.Items.Add("PageDown 아래로 10 이동"); lbox_key.Items.Add("End 오른쪽으로 10 이동"); lbox_key.Items.Add("Space 마우스 왼쪽 버튼 누름"); lbox_key.Items.Add("Enter 마우스 왼쪽 버튼 클릭"); lbox_key.Items.Add("Tap 마우스 왼쪽 버튼 뗌"); Point pt = MouseController.GetPos(); lbox_event.Items.Add(string.Format("마우스 좌표:{0}", pt)); } private void Form1_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Left: MouseController.MoveCXY(-1, 0); lbox_event.Items.Add("왼쪽으로 1 이동"); break; case Keys.Right: MouseController.MoveCXY(1, 0); lbox_event.Items.Add("오른쪽으로 1 이동"); break; case Keys.Up: MouseController.MoveCXY(0, -1); lbox_event.Items.Add("위로 1 이동"); break; case Keys.Down: MouseController.MoveCXY(0, 1); lbox_event.Items.Add("아래로 1 이동"); break; case Keys.Home: MouseController.MoveCXY(-10, 0); lbox_event.Items.Add("왼쪽으로 10 이동"); break; case Keys.End: MouseController.MoveCXY(10, 0); lbox_event.Items.Add("오른쪽으로 10 이동"); break; case Keys.PageUp: MouseController.MoveCXY(0, -10); lbox_event.Items.Add("위로 10 이동"); break; case Keys.PageDown: MouseController.MoveCXY(0, 10); lbox_event.Items.Add("아래로 10 이동"); break; case Keys.Space: MouseController.LeftDown(); lbox_event.Items.Add("마우스 왼쪽 버튼 누름"); break; case Keys.Enter: MouseController.Click(); lbox_event.Items.Add("마우스 왼쪽 버튼 클릭"); break; case Keys.Tab: MouseController.LeftUp(); lbox_event.Items.Add("마우스 왼쪽 버튼 뗌"); break; default: return; } Point pt = MouseController.GetPos(); lbox_event.Items.Add(string.Format("마우스 좌표:{0}",pt)); lbox_event.SelectedIndex = lbox_event.Items.Count - 1; } } }
[소스 8.4] Form1.cs