[windowsAPI] 1주차, 마우스이벤트로 사각형 도형 출력시키기

교내 전공과목인 윈도우즈 API수업을 정리합니다

 

 

 

 

1. 작성된 코드를 기반으로 WindowsAPI 프로젝트 시작하기

# 프로젝트 생성: 실습실에서 쓰는 vs2012버전 기준

(1) 프로젝트 > win32 > 솔루션디렉토리 Uncheck > 응용프로그램 빈 프로젝트 체크

(2) 솔루션 탐색기 > 소스파일 > .cpp 파일에 하단의 소스 삽입

 

# 프로젝트 생성: 개인용으로 쓰는 vs2019버전 기준

(1) 새 프로젝트 만들기 > Windows 데스크톱 마법사

(2) 프로젝트명, 솔루션 이름 작성

(3) 새 프로젝트 구성 > 애플리케이션 종류: 데스크톱 애플리케이션 > 추가옵션: 빈 프로젝트 > 확인

(4) 솔루션 탐색기 > 소스파일 > .cpp 파일에 하단의 소스 삽입

 

 

# 기반이 되는 windowsAPI(ApiStart.cpp) 소스코드

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;
LPCTSTR lpszClass = TEXT("Class");

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
	, LPSTR lpszCmdParam, int nCmdShow)
{
	HWND hWnd;
	MSG Message;
	WNDCLASS WndClass;
	g_hInst = hInstance;

	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	WndClass.hInstance = hInstance;
	WndClass.lpfnWndProc = WndProc;
	WndClass.lpszClassName = lpszClass;
	WndClass.lpszMenuName = NULL;
	WndClass.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&WndClass);

	hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, (HMENU)NULL, hInstance, NULL);
	ShowWindow(hWnd, nCmdShow);

	while (GetMessage(&Message, NULL, 0, 0)) {
		TranslateMessage(&Message);
		DispatchMessage(&Message);
	}
	return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;

	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

해당 소스는 다음과 같은 사항이 작성되어있다

  • 끄기, 최소화, 최대화 이벤트가 먹혀있음
  • 이 소스를 기반으로 "아래한글"프로그램처럼 복합적인 윈도우상자를 다룰 수 있도록 하는 것이 목표임
  • 키보드 이벤트, 마우스 이벤트, 메뉴패널 만들기 등이 예정

 

 

2. 마우스 좌클릭시 사각형 그리기

(1) 마우스 좌클릭 이벤트 작성

CALLBACK WndProc 함수 내부 코드를 다음과 같이 바꿔 작성한다

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;

	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_LBUTTONDOWN:
		/* 마우스 좌클릭 이벤트 작성 */
		hdc = GetDC(hWnd);
		Rectangle(hdc, 10, 10, 60, 60);  // (10,10) 위치에 60x60사이즈 출력
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));

(2) 실행결과

좌클릭시 도형이 출력됨

 

3. 마우스가 좌클릭된 위치에서 도형이 출력되도록 만들자

- 교제 94p(마우스 클릭 이벤트 핸들러 다루기)를 참고

 

(1) 소스 작성

전역변수로 int형의 좌표 저장공간과 갯수 저장 변수를 선언하였다

좌클릭이 될 때마다 iCount를 통해 사각형 갯수가 증가되며, 해당 좌표를 p[0], p[1]공간에 삽입하게 한다

/* 전역으로 저장공간을 선언 */
int p[2][200]; // 도형의 좌표를 저장하는 배열
int iCount; // 사각형의 갯수를 저장하는 변수
int x,y; // 현재 좌표
HBRUSH hBrush;

/* CALLBACK WndProc함수 > case WM_LBUTTONDOWN: 내부에 다음 코드 작성 */
case WM_LBUTTONDOWN:
  /* 마우스 좌클릭 이벤트 작성 */
  hdc = GetDC(hWnd);
  x = LOWORD(lParam); //매크로(Mecro), 캐스팅을 통해서도 가능(x = (WORD)(lParam) )
  y = HIWORD(lParam); 
  p[0][iCount] = x;
  p[1][iCount] = y;
  iCount++;
  hBrush = CreateSolidBrush(RGB(0, 0, 255));
  SelectObject(hdc, hBrush);
  Rectangle(hdc, x - 10, y - 10, x + 60, y + 60); // 마우스로부터 (10,10) 떨어진 위치에 60x60 사이즈 출력
  return 0;

 

(2) 실행결과

정상출력되는 것 같으나 화면 크기 조절 혹은 내리고 올렸을 때, 출력결과가 초기화되는 문제가 발생한다

 

 

4. 출력결과 초기화 문제를 해결하기

# 화면 조절시 초기화 문제 해결

(1) 소스 작성

저장된 p[0], p[1]의 공간(각각 x, y좌표)의 정보를 반복문을 통해 출력시킴

다시 그리는 역할을 하는 것이 WM_PAINT함수의 역할이다

/* CALLBACK WndProc함수 > case WM_PAINT: 내부에 다음 내용을 작성 */
case WM_PAINT:
	hdc = BeginPaint(hWnd, &ps);
    for (int i = 0; i < iCount; i++) {
    	hBrush = CreateSolidBrush(RGB(0, 0, 255));
        SelectObject(hdc, hBrush);
        Rectangle(hdc, p[0][i] - 10, p[1][i] - 10, p[0][i] + 60, p[1][i] + 60);
    }
    EndPaint(hWnd, &ps);
    return 0;

 

(2) 실행결과

창을 내렸다가 올린 경우에도 그렸던 그림이 정상적으로 출력된다

 

 

5. 배열이 아니라 구조체를 통해 출력하도록 수정하기(숙제)

(1) 소스코드

구조체 tagP 안에 x, y변수를 생성해준다. 이 구조체를 배열로 선언한다. 나머지 알고리즘은 전과 동일하다.

/* 전역 변수로 구조체 생성과 선언을 함 */
typedef struct tagP {
	int x;
	int y;
} pxy;

pxy p[1000]; // 구조체 배열 선언
int iCount;
int x, y; // 현재 좌표


/* CALLBACk WndProc 함수 */
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	HBRUSH hBrush;

	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_LBUTTONDOWN:
		/* 마우스 좌클릭 이벤트 작성 */
		hdc = GetDC(hWnd);
		x = LOWORD(lParam); //매크로(Mecro), 캐스팅을 통해서도 가능(x = (WORD)(lParam) )
		y = HIWORD(lParam); 
		/* p[0][iCount] = x;
		p[1][iCount] = y; */
		p[iCount].x = x;
		p[iCount].y = y;
		iCount++;
		hBrush = CreateSolidBrush(RGB(0, 0, 255));
		SelectObject(hdc, hBrush);
		Rectangle(hdc, x - 10, y - 10, x + 60, y + 60); // 마우스로부터 (10,10) 떨어진 위치에 60x60 사이즈 출력
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iCount; i++) {
			hBrush = CreateSolidBrush(RGB(0, 0, 255));
			SelectObject(hdc, hBrush);
			Rectangle(hdc, p[i].x - 10, p[i].y - 10, p[i].x + 60, p[i].y + 60);
		}
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}