반응형

빨간팬, 백스페이스로 선 하나 지우기


typedef struct _line {
	POINT p[500];
	int iCount;
	COLORREF pColor;
} line;

line lines[1000];

//int iTempCount;
int iLines;

COLORREF iCurrentPenColor = RGB(255, 0, 0);

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	static int x;
	static int y;
	//static BOOL bNowDraw=FALSE;
	HPEN hPen;

	switch (iMessage) {
	case WM_COMMAND:
		switch (LOWORD(wParam)){
			case IDM_RED:
				iCurrentPenColor = RGB(255, 0, 0); 
			
				break;
			case IDM_GREEN:
				iCurrentPenColor = RGB(0, 255, 0);
			
				break;
			case IDM_BLUE:
				iCurrentPenColor = RGB(0, 0, 255);
			
				break;
		}
	case WM_LBUTTONDOWN:
		x=LOWORD(lParam);
		y=HIWORD(lParam);
		lines[iLines].pColor = iCurrentPenColor;
		lines[iLines].p[lines[iLines].iCount].x = x;
		lines[iLines].p[lines[iLines].iCount].y = y;
		return 0;
	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {
			hdc=GetDC(hWnd);
			hPen = CreatePen(PS_SOLID, 3, iCurrentPenColor);
			hPen=(HPEN)SelectObject(hdc, hPen);
			MoveToEx(hdc,x,y,NULL);
			x=LOWORD(lParam);
			y=HIWORD(lParam);
			lines[iLines].iCount++;
			lines[iLines].p[lines[iLines].iCount].x = x;
			lines[iLines].p[lines[iLines].iCount].y = y;
			LineTo(hdc,x,y);
			DeleteObject(SelectObject(hdc, hPen));
			ReleaseDC(hWnd,hdc);
		}
		return 0;
	case WM_LBUTTONUP:
		iLines++;
		return 0;
		// 내가 생성한 GDI Object는 사용후반드시 삭제한다, 단 선택해제후
	case WM_KEYDOWN:
		switch (wParam) {
		case VK_BACK:
			if (iLines > 0)
			{
				iLines--;
			}
			InvalidateRect(hWnd, NULL, TRUE);
			lines[iLines].iCount = 0;
			break;
		}

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iLines; i++)
		{
			hPen = CreatePen(PS_SOLID, 3, lines[i].pColor);
			hPen = (HPEN)SelectObject(hdc, hPen);  //선택한다.
			MoveToEx(hdc, lines[i].p[0].x, 
					lines[i].p[0].y, NULL);
			for (int j = 1; j <= lines[i].iCount; j++)
			{
				
				LineTo(hdc, lines[i].p[j].x, lines[i].p[j].y);
			}
			 //선택해제...
			DeleteObject(SelectObject(hdc, hPen));  //그리고 삭제한다...
		}
		return 0;

	case WM_LBUTTONDBLCLK:
		InvalidateRect(hWnd, NULL, TRUE);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

펜 스타일(색, 크기) 지정

API Default 펜 스타일

윈도우의 Default 펜의 값은 색=검정, 크기=1 pixel이다.

색과 크기를 바꾸기 위해서는 그리기 도구 통(DC)에 다른 펜을 만들어서 줘야 한다.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	HPEN hPen, oldPen; //HPEN hPen, oldPen선언
	static int x;
	static int y;
}

윈도우 프로시져를 호출하면

1. hPen과 oldPen을  선언해준다.

case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {
			hdc=GetDC(hWnd);
			hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));//1
			oldPen = (HPEN)SelectObject(hdc, hPen); //2
			MoveToEx(hdc,x,y,NULL);
			x=LOWORD(lParam);
			y=HIWORD(lParam);
			lines[iLines].p[lines[iLines].iCount].x = x;
			lines[iLines].p[lines[iLines].iCount].y = y;
			lines[iLines].iCount++;
			//iTempCount++;
			LineTo(hdc,x,y);
			SelectObject(hdc, oldPen); //3.선택해제
			DeleteObject(hPen);// 4. 삭제
			ReleaseDC(hWnd,hdc);
		}
		return 0;

왼쪽마우스를 누른 상태로 마우스를 움직이면

1. hPen은 실선 / 3pixel / 빨간색의 펜으로 설정한다.

2. hdc그리기 도구통에 hPen을 넣고 oldPen에는 디폴트 값을 리턴한다.

3. hdc그리기 도구통에 oldPen에 있는 디폴트 값을 넣는다.

4. hPen을 삭제한다.

이렇게 펜을 반납하지 않으면 오버플로가 나서 결국 WM_PAINT하다 선이 사라지거나 그리는 도중에 사라지기 때문에 꼭 사용한 펜은 반납하자

 

백스페이스바로 선하나 지우기

	case WM_KEYDOWN:
		switch (wParam) {
		case VK_BACK:
			if (iLines > 0)
			{
				iLines--;
			}	
			InvalidateRect(hWnd, NULL, TRUE);
			lines[iLines].iCount = 0;
			break;
		}
		return 0;

백스페이스 입력시 (iLines=3이다)

1. iLine이 0보다 크다면 선의 갯수를 하나 줄이고 // iLines=2이다

2. WM_PAINT가 아까전보다 1선 작은 그림을 그린다. //선을 2개 그린다

3. 점 갯수를 0으로 초기화시킨다 

 -왼쪽버튼을 눌렸을때 3번째 선에 iCount 0부터 왼쪽버튼을 땔때까지 좌표를 저장한다 이미 저장되 있던 좌표는 뒤로 밀려난다. (지우기전 점개수가 20이고 새로찍은 점갯수가 10이면 새로찍은 좌표는 p[0~9]까지 저장되고 전에 점은 p[10~30]까지 저장된다.)

- 새로 찍은 점의 갯수는 10이라 10까지의 선만을 불러온다.

  

메뉴 창에서 색 바꾸기

리소스 추가

새항목 추가 - 리소스 - 리소스 파일을 추가한다.

리소스 추가

 

메뉴
속성창에서 ID를 꼭 입력해줘야 한다.

메뉴 리소스 받기

	switch (iMessage) {
	case WM_COMMAND:
		switch (LOWORD(wParam)){
			case IDM_RED:
				iCurrentPenColor = RGB(255, 0, 0); 
				break;
			case IDM_GREEN:
				iCurrentPenColor = RGB(0, 255, 0);
				break;
			case IDM_BLUE:
				iCurrentPenColor = RGB(0, 0, 255);
				break;
		}

WM_COMMAND는 리소스를 받는 이벤트이다

여기서 방금 입력한 아이디 값을 받아오면 거기에 맞는 iCurrentPenColor의 색상 값을 변경한다.

여기서 또 문제가 생기는데 WM_PAINT에서 빨간색으로만 그리도록 해놨기 때문에 선의 색을 저장하는 배열을 만들어서 선의 색을 저장해줘야한다.

 

다른 색상의 선 저장

typedef struct _line {
	POINT p[500];
	int iCount;
	COLORREF pColor; // 1
} line;

line lines[1000];
int iLines;
COLORREF iCurrentPenColor = RGB(255, 0, 0); //2

선의 색을 저장하는 자료구조

1. 선마다의 색을 저장해줄 구조체 변수이다

2. 색을 변수로 지정해 메뉴에서 다른 색상 값을 선택하면 색상 값이 바뀐게 해준다.

	case WM_LBUTTONDOWN:
		x=LOWORD(lParam);
		y=HIWORD(lParam);
		lines[iLines].pColor = iCurrentPenColor; //1
		lines[iLines].p[lines[iLines].iCount].x = x;
		lines[iLines].p[lines[iLines].iCount].y = y;
		return 0;

왼쪽 버튼클릭시

1. 현재 선의 색상을 저장해준다.

case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {
			hdc=GetDC(hWnd);
			hPen = CreatePen(PS_SOLID, 3, iCurrentPenColor);//1
			hPen=(HPEN)SelectObject(hdc, hPen);
			MoveToEx(hdc,x,y,NULL);
			x=LOWORD(lParam);
			y=HIWORD(lParam);
			lines[iLines].iCount++;
			lines[iLines].p[lines[iLines].iCount].x = x;
			lines[iLines].p[lines[iLines].iCount].y = y;
			LineTo(hdc,x,y);
			DeleteObject(SelectObject(hdc, hPen));
			ReleaseDC(hWnd,hdc);
		}

마우스가 움직일때

1. 내가 선택한 색상의 선을 그린다.

case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iLines; i++)
		{
			hPen = CreatePen(PS_SOLID, 3, lines[i].pColor); //1
			hPen = (HPEN)SelectObject(hdc, hPen);  //선택한다.
			MoveToEx(hdc, lines[i].p[0].x, 
					lines[i].p[0].y, NULL);
			for (int j = 1; j <= lines[i].iCount; j++)
			{
				
				LineTo(hdc, lines[i].p[j].x, lines[i].p[j].y);
			}
			 //선택해제...
			DeleteObject(SelectObject(hdc, hPen));  //그리고 삭제한다...
		}
		return 0;

무효영역에 들어왔을때

1. 사용할 팬의 색상을 지금 그리는 선의 color값을 가져온다.

반응형
반응형

KeyDown을 통해 spacebar를 누르면 A와 #을 토글하는 프로그램을 만들어 보자.

방향키 입력시 A출력 프로그램

2주차에 만든 키보드 입력을 통해 A를 계속 그려가는 프로그램에서 space bar를 눌렀을 때 A에서 #을 그리도록 프로그램을 만들어 보자

BOOL A = true; //space 누를 시 값을 변환 해줄 BOOL 값
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{	
	HDC hdc;
	PAINTSTRUCT ps;
	
	static int x = 100;
	static int y = 100;
	TCHAR* str = TEXT("A");
	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_KEYDOWN://사용자가 키보드를 누르면
		switch (wParam) { //키보드 입력값이 어떤값인지 wParam으로 전달
		case VK_LEFT:
			x -= 8;
			break;
		case VK_RIGHT:
			x += 8;
			break;
		case VK_DOWN:
			y += 8;
			break;
		case VK_UP:
			y -= 8;
			break;
		case VK_SPACE:
			if (A) //A가 true일 때 
				A = false;
			else //A가 false일때
				A = true;
			break;
		}

		InvalidateRect(hWnd, NULL, FALSE); //무효영역
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		if (A) // A가 true면 A를 그린다
		{
			 str = TEXT("A");
		}
		else //A가 false이면 #을 그린다
		{
			str = TEXT("#");
		}
		TextOut(hdc, x, y, str, lstrlen(str));
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);

		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

스페이스 입력시 불의 T / F 값을 변환해준다.

WM_PAINT에서 A=T면 'A'를 그리고 A=F면 '#'을 그린다

 

static 변수

선언된 함수 내부에서만 접근 할 수 있다.(지역변수)

딱 1회 초기화 되고 프로그램 종료 시까지 메모리에 남아있는다.(전역변수)

위에서 본 지역변수와 전역변수의 특징을 가지고 있는게 static변수이다.

 

유니코드(16비트)와 멀티바이트(8비트)

멀티바이트에서  "가ab"       메모리 값
유니코드에서 "가ab" 메모리 값

두 가지 문자체계의 차이점이 보이는가?

유니코드에서는 문자를 표현하는데 2byte(16bit)를 사용한다.

메모리에서 '00ac'는 한글 "가" / '61 00'은 "a"  / '62 00'은 "B"를 나타낸다.

멀티바이트코드에서는 문자 표현에 1byte (8bit)를 사용한다. 싱글바이트(ex. "a")와 멀티바이트(ex. "가")를 바꿔가면서 처리한다.

메모리에서 'b0'은 "ㄱ" /  'a1'은 "ㅏ" / "61"은 "a" / 62 "b"를 나타낸다.

 

멀티바이트코드는 내부에서 유니코드로 움직안다.

다시말해 멀티바이트코드로 명령하더라도 유니코드로 바꿔서 처리하게 된다.

그렇다면 멀티바이트코드를 사용하면 유니코드로 바꾸는 작업을 더 하기 때문에 효율이 떨어질텐데 왜 아직 쓰는걸까?

과거부터 사용하고 있는 미사일시스템이나 나사의 인공위성 등이 아직도 이 멀티바이트코드를 사용하고 있기 때문에

프로그래머들은 멀티바이트 환경이나 유니코드 환경 두 가지에서 다 돌아갈 수 있도록 Neutrul Code를 프로그램을 만든다.

Neutral Code

이러한 Neutral Code로 프로그램을 짜면 어느 환경에서도 돌아가는 프로그램을 만들 수 있다. 

 

InvalidateRect

InvalidateRect(hWnd,NULL,FALSE); //OS WM_PAINT 메시지 발생시켜줘

InvalidateRect명령을 받으면 바로 WM_PAINT을 발생시킨다. 

 

lnInvalidateRect(hWnd, NULL, FALSE); //무효 영역(다시 그려하는 영역)을 발생 시킨다.

 -다시말해 윈도우창을 최소화했다다시 띄우는 것과 동일한 이벤트이다.

 

lnInvalidateRect(hWnd, NULL, TRUE) 그 전에 그려져 있는 배경을 지운다

lnInvalidateRect(hWnd, NULL, FALSE) 배경을 그래도 둔다.

 

선그리기

#include <windows.h>

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

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)GetStockObject(WHITE_BRUSH);
	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;// | CS_DBLCLKS;
	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;
}

typedef struct _line{
	POINT p[500];
	int iCount;
}line;

line lines[1000];

int iTempCount;
int iLines;

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	static int x;
	static int y;
	//static BOOL bNowDraw=FALSE;
	PAINTSTRUCT ps;

	switch (iMessage) {
	case WM_LBUTTONDOWN:
		x=LOWORD(lParam);
		y=HIWORD(lParam);
	//	bNowDraw=TRUE;
		return 0;
	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {//왼쪽버튼을 누른상태로 움직였을때
			hdc=GetDC(hWnd);
			MoveToEx(hdc,x,y,NULL);//x,y좌표를 옮긴다(펜을옮긴다)
			x=LOWORD(lParam);
			y=HIWORD(lParam);

			lines[iLines].p[iTempCount].x = x;
			lines[iLines].p[iTempCount].y = y;
			iTempCount++;

			LineTo(hdc,x,y);//선을 그린다 //선을그릴때 : MoveTo + LineTo
			ReleaseDC(hWnd,hdc);
		}
		return 0;
	case WM_LBUTTONUP:
	//	bNowDraw=FALSE;
		lines[iLines].iCount = iTempCount;
		iTempCount = 0;
		iLines++;
		return 0;

	case WM_PAINT:   //다시 그려야 된다
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iLines; i++)
		{
			for (int j = 1; j < lines[i].iCount; j++)
			{
				MoveToEx(hdc, lines[i].p[j-1].x, lines[i].p[j-1].y, NULL);
				LineTo(hdc, lines[i].p[j].x, lines[i].p[j].y);
			}
		}
		EndPaint(hWnd, &ps);
		return 0;

	case WM_LBUTTONDBLCLK:
		InvalidateRect(hWnd, NULL, TRUE);

		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

 struct _line의 구조체와 전역변수 

lines[0].p[0].x = 0번째 선의 0번째 좌표 x  / lines[0].p[0].y = 0번째 선의 0번째 좌표 y

lines[0].iCount = 0번째 선의 점의 갯수 / iLines = 선의 갯수 / iTempCount = iCount의 값을 임시로 가지고 있는 변수

switch문 메시지

case WM_LBUTTONDOWN:
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		return 0;

마우스 왼쪽버튼 클릭 시 x, y좌표를 저장한다.

	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {//왼쪽버튼을 누른상태로 움직였을때
			hdc = GetDC(hWnd);
			MoveToEx(hdc, x, y, NULL);//x,y좌표를 옮긴다(펜을옮긴다)
			x = LOWORD(lParam);
			y = HIWORD(lParam);

			lines[iLines].p[iTempCount].x = x;
			lines[iLines].p[iTempCount].y = y;
			iTempCount++;

			LineTo(hdc, x, y);//선을 그린다 //선을그릴때 : MoveTo + LineTo
			ReleaseDC(hWnd, hdc);
            }
		return 0;

if(wParam(마우스 이동) 그리고 왼쪽버튼클릭) true라면

1. 그리기 도구를 얻어온다.

2. 선은 방금 왼쪽 클릭시 받은 x와 y좌표에서 부터시작한다.

3. x와 y좌표 값 다시 설정

4. 나중에 다시 불러오기 위해 x와 y에 입력한 좌표값을 저장해둔다.

5. 마우스가 움직일때마다 다른 배열에 저장하기 위해 iTempCount를 증가시킨다.

6. 선을 그린다

7. 그리기 도구 반환

case WM_LBUTTONUP:
		//	bNowDraw=FALSE;
		lines[iLines].iCount = iTempCount;
		iTempCount = 0;
		iLines++;
		return 0;

마우스 왼쪽버튼 클릭을 해제했을때

1. 지금 선의 iCount에 iTempCount 값을 저장한다.

2. iTempCount의 값을 초기화한다.

3. 한 선이 끝났기 때문에 다음 선값을 저장하기 위해 선의 갯수를 한개 늘린다.

case WM_PAINT:   //다시 그려야 된다
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iLines; i++)
		{
			MoveToEx(hdc, lines[i].p[0].x, lines[i].p[0].y, NULL);
			for (int j = 1; j < lines[i].iCount; j++)
			{
				LineTo(hdc, lines[i].p[j].x, lines[i].p[j].y);
			}
		}
		EndPaint(hWnd, &ps);
		return 0;

OS가 다시 그려야된다는 메시지를 보내면(ex. 윈도우창 최소화)

1. 그리기 도구를 얻고

2. 맨처음 lines[iLines(0)].p[0].x와 lines[iLines(0)].p[0].y값을 불러온다.

두번째 값 lines[0].p[1].x와 lines[0].p[1].y부터 점 갯수(iCount)의 값이 끝날때까지 불러온다.

3. for문 처음으로 돌아가 2번째 선을 그린다. 선의 갯수까지 반복

4. 그리기도구 반납 

	case WM_LBUTTONDBLCLK:
		InvalidateRect(hWnd, NULL, TRUE);
		return 0;

더블클릭시 발생하는 이벤트

1. 무효영역을 콜하고 배경은 지운다.

무효영역에서 배열에 저장된좌표에 다시 그리게 해놔서 그런지 작동하지 않음

 

반응형
반응형

3주차는 동영상강의를 안 듣고온 학생들이 많아서 1, 2주차 복습 위주로 했다.

 

이 강의는 이름은 윈도우즈 프로그래밍이지만 원도우즈 프로그래밍보다 GUI기반 응용프로그램을 만들어보는데 초점을 둔다.

 

실제로 프로그래밍 환경이 많이 진화되어서  윈도우즈 WPF를 쓰면 직접하지 않고 컨트롤로 많이 움직이지만 본질은 모두 GUI기반으로 움직인다.

GUI란

graphical user interface의 약자로 사용자가 컴퓨터와 정보를 교환할 때, 그래픽을 통해 작업할 수 있는 환경을 말한다.

 

원도우 출력시 크기 설정

원도우의 크기 디폴트 값은 pixel이다.

하지만 Mapping을 통해 크기 값, 좌표의 축 등도 바꿀 수 있다.

 

이벤트 발생시

윈도우 창안에서 이벤트가 발생할 경우 OS가 윈도우 프로시져를 호출해준다.

원도우 프로시져 함수(마우스 왼쪽을 클릭하는 이벤트 발생시 원을 그리는 함수이다)

iMessages는 어떤 종류의 메시지인지 어떤 변화가 발생했는가에 관한 정보

ex) WM_PAINT, WM_LBUTTONDOWN 등이 있다

wParam, lParam은 어떤 값이 전달되는지는 메시지별로 다르다.

이 메시지에서는 OS는 lParam에 마우스 좌표를 넣어준다.

 

struct로 만든 원그리기 프로그램

typedef struct tagmyPOINT
{
	LONG  x;
	LONG  y;
	
} myPOINT;
myPOINT p[1000];

int iCount;

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


	switch (iMessage) {
	case WM_LBUTTONDOWN:
		hdc = GetDC(hWnd);
		p[iCount].x = x = (WORD)(lParam);// LOWORD(lParam);
		p[iCount].y = y = HIWORD(lParam);
		iCount++;
		hBrush = CreateSolidBrush(RGB(255, 0, 0));//도형의 색을 선택
		SelectObject(hdc, hBrush); // 그리기 도구 hdc의 색을 지정
		Ellipse(hdc, x - 10, y - 10, x + 10, y + 10);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iCount; i++)
		{
			hBrush = CreateSolidBrush(RGB(255, 0, 0));//도형의 색을 선택
			SelectObject(hdc, hBrush); // 그리기 도구 hdc의 색을 지정
			Ellipse(hdc, p[i].x - 10, p[i].y - 10, p[i].x + 10, p[i].y + 10);
		}
		
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

class로 만든 원그리기 프로그램

class pt{
public:
	int x;
	int y;
};

class mypoints : public pt
{
public:
	pt p[1000];
	int iCount;
};
class pt2{
public:
	int x;
	int y;
};

class mypoints2 : public pt2
{
public:
	pt p[1000];
	int iCount;
};
mypoints my;
mypoints2 my2;
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	int x, y;
	HBRUSH hBrush;


	switch (iMessage) {
	case WM_LBUTTONDOWN:
		hdc = GetDC(hWnd);
		my.p[my.iCount].x = x = (WORD)(lParam);// LOWORD(lParam);
		my.p[my.iCount].y = y = HIWORD(lParam);
		my.iCount++;
		hBrush = CreateSolidBrush(RGB(255, 0, 0));//도형의 색을 선택
		SelectObject(hdc, hBrush); // 그리기 도구 hdc의 색을 지정
		Ellipse(hdc, x-10, y-10, x+10, y+10);
		return 0;
	case WM_RBUTTONDOWN:
		hdc = GetDC(hWnd);
		my2.p[my2.iCount].x = x = (WORD)(lParam);// = LOWORD(lParam);
		my2.p[my2.iCount].y = y = (WORD)(lParam >> 16); //16비트 잘라서 넣는다 HIWORD, LOWORD는 이 명령을 전처리
		my2.iCount++;
		hBrush = CreateSolidBrush(RGB(0, 0, 255));//도형의 색을 선택
		SelectObject(hdc, hBrush); // 그리기 도구 hdc의 색을 지정
		Ellipse(hdc, x - 10, y - 10, x + 10, y + 10);
		return 0;
	case WM_PAINT: 
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < my.iCount; i++)
		{
			hBrush = CreateSolidBrush(RGB(255, 0, 0));//도형의 색을 선택
			SelectObject(hdc, hBrush); // 그리기 도구 hdc의 색을 지정
			Ellipse(hdc, my.p[i].x - 10, my.p[i].y - 10, my.p[i].x + 10, my.p[i].y + 10);
		}
		for (int i = 0; i < my2.iCount; i++)
		{
			hBrush = CreateSolidBrush(RGB(0, 0, 255));//도형의 색을 선택
			SelectObject(hdc, hBrush); // 그리기 도구 hdc의 색을 지정
			Ellipse(hdc, my2.p[i].x - 10, my2.p[i].y - 10, my2.p[i].x + 10, my2.p[i].y + 10);
		}
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

class private을 호출하는 매서드 만들기

헤더파일과 소스파일 분리

class pt
{
private:
	int x;
	int y;
public:
	void Set(int x, int y)
	{
		//x = x;  //문제발생 compile error는 없으나.
		//y = y;
		this->x = x;
		this->y = y;
	}
	
	POINT Get()
	{
		POINT p;
		p.x = x;
		p.y = y;
		return p;
	}
};

class mypoints : public pt  //상속
{
	//public:   
private:
	pt p[1000]; //pt 클래스의 (instance, object) 배열
	int iCount;
public:
	void Add(int x, int y)
	{
		if (iCount < 1000)
		{
			//p[iCount].x = x;
			//p[iCount].y = y;
			p[iCount].Set(x, y);
			iCount++;
		}

	}
	void Draw(HDC hdc)
	{
		HBRUSH hBrush;
		hBrush = CreateSolidBrush(RGB(255, 0, 0));
		SelectObject(hdc, hBrush);
		for (int i = 0; i < iCount; i++)
		{
			Rectangle(hdc, p[i].Get().x - 10, p[i].Get().y - 10,
				p[i].Get().x + 10, p[i].Get().y + 10);
		}
	}
};

 ↑ points.h++

//점들 - objects
// description---> class : Abstraction 
#include "points.h"  //Code reusability 코드재사용
//실체화: instantiation
mypoints points;  // 정의한 클래스의 (instance, object) 선언

LRESULT CALLBACK WndProcedure(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{  //Window Procedure      //window handle(메시지가 발생한)
	HDC hdc;
	PAINTSTRUCT ps;
	int x, y;
	HBRUSH hBrush;
	
	
	switch (iMessage) {
	case 0x0001://WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case  WM_LBUTTONDOWN: //왼쪽버튼을 눌렀을때
		hdc = GetDC(hWnd);
		x = LOWORD(lParam);  //Macro
		y = HIWORD(lParam);
		points.Add(x, y);
		InvalidateRect(hWnd, NULL, FALSE);
		return 0;
	
	case WM_PAINT:  // 화면을 다시 그려라 os가 보내는 MSG
		hdc = BeginPaint(hWnd, &ps);    //len = strlen(buf);  scanf("%d", &a);
		// 화면복구 routine
		points.Draw(hdc);
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 ↑ 소스 파일

반응형
반응형

px와 iCount가 전역 변수인 상태

px와 iCount를 전역변수로 설정하고 1주차의 원을 그리는 프로그램을 돌려보면 원은 그려지지만 WM_PAINT가 최소화했을 때 다시 그리지 못한다 그 이유를 디버깅을 통해 알아보자

Debugging

Debugging 명령어
F5 GO
F9 Break Point
F10 한 줄 실행
Ctrl + F10 Cursor 위치까지 실행

 

디버깅 범위
지역변수 선언시 디버깅 변수 값

지역변수로 선언하면 클릭할때마다 0으로 초기화되기 때문에 WM_PAINT로 다시 그림을 불러올 수 없다.

 

전역변수 선언시 디버깅 변수 값

전역변수로 선언하면 배열에 값이 올바르고 iCount값이 정상적으로 증가하는 걸 볼 수 있다.

 

DC(Device Context)

DC란 출력에 필요한 모든 정보를 가지는 데이터 구조체 이다.

DC를 얻는 방법 2가지

HDC hdc; // hdc선언

방법1

 hdc = GetDC(hWnd);

 RelealseDC()ReleaseDC(hWnd, hdc);

방법2

 hdc = BeginPaint(hWnd, &ps);

 EndPaint(hWnd, &ps);

 

DC도 C의 malloc/free 처럼 GetDC로 함수를 얻고 ReleaseDC로 해제해야 한다.

방법2는 WM_PAINT 메시지 루틴에서만 사용할 수 있다. 

 

문자열 출력

TextOut이라는 API를 통해 문자열을 출력할 수 있다. TextOut 함수는 콘솔의 printf에 해당하는 함수이다.

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

	switch (iMessage) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONDOWN:
		hdc = GetDC(hWnd); //DC가져오기
		//TCHAR* str = TEXT("Hello world"); // 1
		TCHAR str[100] = TEXT("Hello world"); //2 두가지 차이점은?
 		TextOut(hdc, 100, 100, str, lstrlen(str));
		ReleaseDC(hWnd, hdc);
		return 0;


	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

TextOut(hdc, x, y, Text, lstrlen(Text)); 

화면에 무언가를 출력하는 함수의 첫 번째 인수는 항상 hdc이다.

2, 3번째 인수는 좌표를 가리키고

4번째 인수는 문자열(문자열의 시작 주소를 알려준다 TCHAR str[], TCHAR *str을 미리 만들고 넣어도 가능)

5번째는 문자열의 길이를 나타낸다. lstrlen(Text)를 사용하면 문자열이 바뀔 때마다 길이를 직접 세지 않아도 된다.

 

두 가지 차이점 무엇인가? 

 TCHAR* str = TEXT("Hello world"); // 1 VS2019에서 오류 뜸
 TCHAR str[100] = TEXT("Hello world"); // 2

 

그래픽 출력

 

점 선 사각형 원을 그린 프로그램

case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		SetPixel(hdc, 10, 10, RGB(255, 0, 0)); //빨간점 출력
		MoveToEx(hdc, 50, 50, NULL);
		LineTo(hdc, 300, 90);//MoveToEx+LineTo 같이 사용 해 선을 긋는다.
		Rectangle(hdc, 50, 100, 200, 180);//사각형 출력
		Ellipse(hdc, 220, 100, 400, 200);//원 출력
		EndPaint(hWnd, &ps);
		return 0;

SetPixel(hdc, x, y, 색상)

선을 그을려면 MoveToEx와 LineTo를 같이 사용해야 한다.

MoveToEx(hdc,x,y, lpPoint) 선이 시작하는 지점 /LineTo(hdc, x,y)선이 끝나는 지점

사격형과 원은 전 단원에도 다뤘으니 패스하겠다.

 

키보드 입력

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

	static int x = 100;
	static int y = 100;

	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_KEYDOWN://사용자가 키보드를 누르면
		switch (wParam) { //키보드 입력값이 어떤값인지 wParam으로 전달
		case VK_LEFT:
			x -= 8;
			break;
		case VK_RIGHT:
			x += 8;
			break;
		case VK_DOWN:
			y += 8;
			break;
		case VK_UP:
			y -= 8;
			break;
		}

		InvalidateRect(hWnd, NULL, FALSE); //무효영역
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc, x, y, TEXT("A"), 1);
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);

		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 방향키에 따라 좌표를 8픽셀씩 더하거나 뺀다. 

lnInvalidateRect(hWnd, NULL, FALSE); //무효 영역(다시 그려하는 영역)을 발생 시킨다.

 -다시말해 윈도우창을 최소화했다다시 띄우는 것과 동일한 이벤트이다.

 -무효영역 발생시 WM_PAINT가 바로 실행돼서 'A'를 그리게 된다.

 

lnInvalidateRect(hWnd, NULL, TRUE) 배경을 계속 지운다 A가 한 개만 생김

lnInvalidateRect(hWnd, NULL, FALSE) 배경을 그래도 둔다 A가 계속해서 생겨난다.

 

lnInvalidateRect(hWnd, NULL, FALSE)를 지우고 

hdc = GetDC(hWnd);
TextOut(hdc, x, y, TEXT("A"), 1);
ReleaseDC(hWnd, hdc);

위 명령어를 넣어도 A를 그릴 수 있다.  하지만 WM_PAINT에 또 A 그리기를 명령해야 하기 때문에 무효 영역을 통해 

그리기 루틴을 일원화하는 것이 좋다.

사각형 만들기 프로그램 그리기루틴 일원화

int px[1000], py[1000]; //도형의 좌표를 기억하는 변수
int iCount; //사각형의 갯수를 저장하는 변수

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
//당분간은 이 함수를 설명한다.
{
	HDC hdc;
	PAINTSTRUCT ps;
	int x, y;
	HBRUSH hBrush;

	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_LBUTTONDOWN: //왼쪽버튼을 누르면
		px[iCount] = x = LOWORD(lParam);
		py[iCount] = y = HIWORD(lParam);
		iCount++;
		InvalidateRect(hWnd, NULL, FALSE);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iCount; i++)//iCount(도형의 갯수 만큼 반복해서 그림) 
		{
			hBrush = CreateSolidBrush(RGB(0, 0, 255));
			SelectObject(hdc, hBrush);
			Rectangle(hdc, px[i] - 10, py[i] - 10, px[i] + 60, py[i] + 60);
		}
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

WM_CHAR 메시지

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

	static TCHAR str[256];
	int len;

	switch (iMessage) {
	case WM_CHAR:
		len = lstrlen(str);
		str[len] = (TCHAR)wParam;
		str[len+1] = 0;
		InvalidateRect(hWnd, NULL, FALSE);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc,100,100,str,lstrlen(str));
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);

		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

원도우즈 환경은 멀티 태스킹 환경이기 때문에 scanf처럼 입력이 완료될 때까지 프로그램의 실행을 중단할 수 없다.

멀티 태스킹 환경에서는 사용자가 관심을 보이는 프로그램만 CPU시간을 받으며 나머지는 대기한다.

원도우즈는 포커스를 가진 프로그램에게 메시지를 보내며 프로그램은 이런 메시지를 받아 키보드 입력을 처리한다.

 

len = lstrlen(str);
str[len] = (TCHAR)wParam;
str[len+1] = 0;

입력받은 인자를 str배열에 다 넣는다.

lnvalidateRect를 통해 무효영역을 발생시켜 입력할때마다 WM_PAINT로 출력한다.

반응형
반응형

윈도우창 생성

ApiStart.txt
0.00MB

위 텍스트 파일을 c++소스에 넣으면 옆과 같은 윈도우 어플리케이션 창이 만들어진다.

이 번 학기의 목표는 한글같이 키보드를 마우스를 누르면 반응하는 프로그램을 만드는게 목표이다.

원도우 프로그램의 소스

#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));
}

WinMain 함수는 나중에 나중에 배워보도록 하고 당분간 수업에서는 WndProc함수에 대해 본다.

 

사각형을 그리는 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)// 사각형을 그려라
		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));
}

마우스 오른쪽 버튼을 눌렀을 때 사각형을 그리도록 바꾼 WndProc함수이다.

Rectangle(hdc, 좌, 상 ,우, 하)이다

 

누르는 지점에 파란색 사각형을 그리는 WndProc함수

int px[1000], py[1000]; //도형의 좌표를 기억하는 변수
int iCount; //사각형의 갯수를 저장하는 변수

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

	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_LBUTTONDOWN: //왼쪽버튼을 누르면
		hdc = GetDC(hWnd); //원도우 그리기 도구 상자 얻기
		px[iCount] = x = LOWORD(lParam);
		py[iCount] = y = HIWORD(lParam);
		iCount++;
		hBrush = CreateSolidBrush(RGB(0, 0, 255));//도형의 색을 선택
		SelectObject(hdc, hBrush); // 그리기 도구 hdc의 색을 지정
		Rectangle(hdc, x-10, y-10, x+60, y+60);// 사각형을 그려라
		return 0;
	case WM_PAINT: //최소화하고 다시 그려진다
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iCount; i++)//iCount(도형의 갯수 만큼 반복해서 그림) 
		{
			hBrush = CreateSolidBrush(RGB(0, 0, 255));
			SelectObject(hdc, hBrush);
			Rectangle(hdc, px[i] - 10, py[i] - 10, px[i] + 60, py[i] + 60);
		}
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

 

마우스 왼쪽버튼을 누르면 px배열에  클릭한 위치의 좌표가 저장된다. iCount를 통해 다음 배열에 저장

LOWORD=x의 좌표를 HIWORD=y좌표를 두 매크로를 통해 좌표값을 받아올 수 있다.

iCount가 증가하면서 다음 배열에 클릭 횟수를 추가한다. 

HBRUSH 면을 채울 때 사용하는 GDI오브젝트 차후 수업에서 자세하게 다룬다. 여기서는 파란색으로 설정되었다.

SelcetObject 함수를 통해 hdc그리기 도구의 면 색을 hBrush로 설정해준다.

 

윈도우창을 최소화하면 그려진 사각형들이 다 사라지는데 이걸 다시 그려주는 매크로가 WM_PAINT이다.

0부터 도형의 갯수만큼 반복해서 도형을 다시 그린다. 

※ 변수 명명법

접두어 원래말  의미
cb Count of Byte 바이트 수
dw double word 부호없는 long형 정수
h handle 윈도우, 비트맵, 파일 등의 핸들
sz Null Terminated NULL 종료 문자열
ch Character 문자형
a Array 배열
w Word 부호없는 정수형
i lnteger 정수형
p, lp long pointer 포인터형
b Bool 논리형

윈도우즈 프로그래밍에서 변수에 주로 이름을 붙이는 방법이다. 꼭 이 방식으로 변수를 만들필요는 없지만 표준 함수들은 모두 이 방식대로 인수 이름을 작성하므로 접두어들을 외우고 있으면 함수의 인수가 어떤의미를 가지는 파악하기 쉽다.

 

과제

오른쪽 클릭시 파란원 / 왼쪽 클릭시 빨간원을 만드는 프로그램

최소화 했을하고 다시 띄웠을때도 파란원과 빨간원이 그대로 있어야 한다.

#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;
}



int px[1000], py[1000];//빨간원 좌표
int px2[1000], py2[1000];//파란원 좌표
int iCount_Red = 0;//빨간원 갯수
int iCount_Blue = 0;//파란원 갯수

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


	switch (iMessage) {
	case WM_CREATE:
		hWndMain = hWnd;
		return 0;
	case WM_LBUTTONDOWN: //왼쪽버튼을 누르면
		hdc = GetDC(hWnd);
		px[iCount_Red] = x = LOWORD(lParam);
		py[iCount_Red] = y = HIWORD(lParam);
		iCount_Red++;
		hBrush = CreateSolidBrush(RGB(255, 0, 0));
		SelectObject(hdc, hBrush);
		Ellipse(hdc, x - 10, y - 10, x + 10, y + 10);
		return 0;
	case WM_RBUTTONDOWN: //오른쪽버튼을 누르면
		hdc = GetDC(hWnd);
		px2[iCount_Blue] = x = LOWORD(lParam);
		py2[iCount_Blue] = y = HIWORD(lParam);
		iCount_Blue++;
		hBrush = CreateSolidBrush(RGB(0, 0, 255));
		SelectObject(hdc, hBrush);
		Ellipse(hdc, x - 10, y - 10, x + 10, y + 10);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iCount_Blue; i++)
		{
			hBrush = CreateSolidBrush(RGB(0, 0, 255));
			SelectObject(hdc, hBrush);
			Ellipse(hdc, px2[i] - 10, py2[i] - 10, px2[i] + 10, py2[i] + 10);
		}
		for (int i = 0; i < iCount_Red; i++)
		{
			hBrush = CreateSolidBrush(RGB(255, 0, 0));
			SelectObject(hdc, hBrush);
			Ellipse(hdc, px[i] - 10, py[i] - 10, px[i] + 10, py[i] + 10);
		}

		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);

		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

오른쪽클릭시 생성되는 원과 왼쪽클릭시 생성되는 원에 저장되는 좌표와 갯수를 세는 변수를 다르게 해서 구현했다.

원이 2가지 밖에 없어 이런방법으로 접근해도 되지만 여러가지의 원이 있을 때도 초기화되지 않는 방법을 생각해봐야겠다.

 

반응형
반응형

Chapter 11 1차원 배열

 

더보기

배열 int arr[7]는

int = 배열 자료형

arr = 배열 이름

[7] = 배열의 길이

 

로 구성되어 있다.


배열의 크기

int4byte 공간을 차지하기 때문에 int형태가 7개 있음으로 int arr[7]의 크기는 28byte이다.

 

배열 크기 확인법

sizeof(arr)를 이용하여 크기를 확인 할 수 있다.

 


배열 사용 이유

배열을 사용하면 다수의 변수를 간단하게 선언 할 수 있다.

 

또 반복문을 통해 내가 선언한 배열에 순차적으로 접근이 가능하다.

 

for(int I=0; I<7; I++)

arr[i]=10;

 

이렇게 하면 arr[0]~[6]까지 모든 배열에 10이 저장된다.


배열과 동시에 초기화

int arr[5] = {1,2,3,4,5};

int arr[] = {1,2,3,4,5,6,7}; // 이렇게 하면 arr[] 크기는 초기한 배열을 다 담을 수 있을 만큼 선언된다. 괄호 안에 숫자는 7이 된다.


문자열

 

char str[30] = “simple”;

s i m p l e \n                                      

                                                 ↑자동으로 Null 문자 삽입 뒤에도 전부 Null문자가 삽입된다.

 

 

 

길이가 13인 문자 Good morning!을 삽입 하기위해서는 14의 길이의 배열이 필요하다

G

o

o

d

 

m

o

r

n

I

n

g

!

\n

                                                                                               널문자는 문자열이 끝나는 걸 알려준다.

 

C언어가 문자열을 읽을때 배열의 첫번째 부터 첫번째 널문자까지를 문자열로 인식하기 때문에 널문자가 없으면 어디서 문자열을 끝내야 하는지 컴퓨터는 알 수 없다.

그렇기 때문에 입력하려는 글자+널문자 들어갈 공간이 필요하다.

 

만약 위 예제에서 

 

G

o

o

d

\n

m

o

r

n

I

n

g

!

\n

5번째 배열에서 널문자를 만났다면 Good까지만 출력 될 것 이다.

 

 

 

배열.hwp
0.04MB

반응형

+ Recent posts