WDmil 2023. 6. 15. 23:04
728x90

윈도우 API를 사용하여 창을 띄우고 그곳에 이미지를 출력할 수 있다.

 

#include <Windows.h>
#include <cassert>
#include <string>

LRESULT CALLBACK WndProc(HWND handle, UINT message, WPARAM wparam, LPARAM lParam);
int APIENTRY WinMain( // 운영체제에서 필요할 때 호출되는 함수
	HINSTANCE hInstance, // 윈도우의 창의 실체를 말한다. 창을 띄우는 자체에 대한 식별자 를 말한다.
	HINSTANCE prevInstace, // 이전에 대한 정보값.
	LPSTR lpszCmdParam, // 문자열. 형식. 헝가리안 표기법 을 사용한다.
	int nCmdShow // cmd : 명령프롬프트.[ command window ] 화면에 어떤식으로 나타낼 것인가. 문자열형태로 나타낼것인가 를 표시한것.
	// 위 항목들은 작성하지 않아도 자동으로 들어간다. nullptr 같은 느낌
)
{
	WNDCLASSA wnd_class; // A : ASCII 윈도우에 지정할것 이라는 의미.

	wnd_class.cbClsExtra = 0;
	wnd_class.cbWndExtra = 0; // 확장된 공간을 사용할 것 인가.
	wnd_class.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)); // 백그라운드 색상을 어떻게 사용할 것 인가.
	wnd_class.hCursor = LoadCursor(nullptr, IDC_CROSS); // 커서 모양을 십자가 모양으로 사용하겠다. 라는 의미.
	wnd_class.hIcon = LoadIcon(nullptr, IDI_ERROR); // 프로그램 실행했을 때 작업표시줄에 나타나는 아이콘 모양을 ERROR로 표시할것 이다.
	wnd_class.hInstance = hInstance;
	wnd_class.lpfnWndProc = WndProc; // 윈도우의 포인터와 함수펑션을 연결해주는 역할
	wnd_class.lpszClassName = "First Window"; // 파일화 시켰을 때, 클래스 이름을 정의해주는것.
	wnd_class.lpszMenuName = nullptr; // 메뉴바 를 이야기한다.
	wnd_class.style = CS_HREDRAW | CS_VREDRAW; // 수평 또는 수직이 바뀌었을 때 다시그려준다는 의미.

	RegisterClassA(&wnd_class); // 위 클래스 항목들을 레지스터에 등록하여 이름으로 찾아쓸 수 있다.

	HWND hwnd = CreateWindowA
	(
		"First Window",
		"Hello, Window!",
		WS_OVERLAPPEDWINDOW, // 스타일 중 하나. 여러개중 하나 선택하여 사용가능.
		0, 
		0,
		1080,
		720,
		nullptr, // 윈도우의 부가창을 만드는 것.
		nullptr, // 메뉴
		hInstance,
		nullptr
	); // 화면을 구성하였다.
    
    assert(hwnd != nullptr);

	ShowWindow(hwnd, nCmdShow);
	ShowCursor(TRUE);

	MSG message;
	ZeroMemory(&message, sizeof(MSG)); // 윈도우 메모리 초기화 방식.
    
    	while (GetMessage(&message, nullptr, 0, 0))
	{
		TranslateMessage(&message);
		DispatchMessage(&message);
	}

	DestroyWindow(hwnd); // 윈도우 창 날리기
	UnregisterClassA("First Window", hInstance); // 윈도우 창을 해제해준다.

	return 0;
}

LRESULT CALLBACK WndProc(HWND handle, UINT message, WPARAM wParm, LPARAM lParam)
{
	static POINT position;
	static POINT start;
	static POINT end;
	static BOOL is_clicked = FALSE; // 마우스 클릭 여부 확인.
	
	static RECT rect1 = { 100, 100, 200, 200 };
	static RECT rect2 = { 300, 300, 400, 400 };
	static BOOL is_intersect = FALSE;
	
	switch (message) // 들어온 메세지 처리하는 부분.
	{
    case WM_CLOSE:
	case WM_DESTROY:
		PostQuitMessage(0); // 메세지에 0 이 들어오면서 윈도우를 종료해준다. 문제가 없으면 0 있으면 1이 리턴된다.
		break;
	default:
		return DefWindowProcA(handle, message, wParm, lParam); // 처리 안한게 있는 상태라면 이곳으로 리턴시킨다.
	}

	return 0;
}

위 코드가 기본적으로  1080 / 720 사이즈의 윈도우 창을 띄워주는 코드이다.

 

여기에서 WndProc 의 함수를 수정하여. 다양한 구성을 시도할 수 있다.

 

다음 밑 부터 작성된 코드는

switch (message)

{

의 밑

case WM_CLOSE: 의 위 에 위 코드를 삽입하여 출력해보면

 

다음과 같은 결과가 나타난다.


기본 텍스트 출력하기.

	case WM_LBUTTONDOWN :
	{
		std::string str = "Mouse Clicked!!";

		HDC hdc = GetDC(handle); // GetDc : 화면과 관련된 디바이스 컨텍스트 핸들을 얻어오려는 것. [ HDC : handle to Device Context 윈도우 운영체제 그래픽작업 수행해주는 것. ]
		// 핸들을 사용해서 관련 상수를 얻어와야함.
		// 원 삼각형 사각형 등을 그릴 수 있게 하기위해 받아온다.
		TextOutA(hdc, 500, 300, str.c_str(), str.length());
		ReleaseDC(handle, hdc); // 핸들을 반환해준다.
	}
	break;
	case WM_PAINT: // 윈도우가 다시 그려질 때 발생하는 메세지. 처음 생성시. 움직여서 가려질 때 다시출력해주어야 하는데, 그때 호출이 된다.
	{
		std::string str = "WM_PAINT message occurred!!";

		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(handle, &ps); // 그리기 시작할 때 불러옴
		TextOutA(hdc, 500, 350, str.c_str(), str.length());

		EndPaint(handle, &ps);
	}
	break;

 

위 코드를 설명해보자. case WM_LBUTTONDOWN 의 경우. UINT의 값을 받아온 message임으로.

 

윗부분 코드 줄 중 wnd_class.lpfnWndProc 에 WndProc이 들어감으로. 

정의 피킹으로 위치참조를 해보면, 다음과 같이. CALLBACK* WNDPROC 형태로 함수포인터 로 사용됨을 확인할 수 있다.

 

즉, 입력값을 참조하여 인터럽트가 일어났을 경우, switch의 case를 전부 참조하여. 해당되는 case에 대한 코드를 실행하게 만드는 것이라고 이해하면 된다.

 

다음으로 넘어가면. WM_LBUTTONDOWN. 그러니까 마우스의 왼쪽버튼을 다운 시켰을 때. 동작한다.

string 형태의 str 변수를 생성하고 " Mouse Clicked!! " 이라는 문장을 삽입한다.

 

그 후 HDC 형태의 hdc 변수를 생성하고. 매개변수로 받아온 HWND를 GetDC 함수를 사용하여

HDC에서 불필요한 HWND값을 절삭하고 HDC 형태의 hdc 변수에 복사대입한다.

 

그 후, TextOutA 함수를 사용해서 x위치 500 y위치 300 의 위치에 string 형태의 str을 c타입의 char 배열로 변환한 뒤, 배열사이즈를 입력하여 문자열을 출력한다는 것을 hdc의 주소에 기입해준다.

 

ReleaseDC를 통해 handle를 참조하여 hdc의 핸들을 os에 반환해준다.

 

WM_PAINT 을 통해 Switch로 윈도우가 다시 그려질 때 갱신하여 해당됨으로 출력해준다.

string 형태의 str을 선언하여 WM_PAINT message occurred!! 를 str에 문자열을 기입해준다.

 

PAINTSTRUCT 형태의 ps를 선언한다.

 

HDC 형의 hdc를 선언하여, BeginPaint를 사용하여 handle을 참조하여 ps를 hdc에 복사대입한다.

 

TextOutA 를 통하여, hdc의 주소값의 x500 y350에 해당하는 위치에 str의 문자열을 대입한다.

 

그리고 ps를 할당해제 해준다.

 

위와같은 코드열을 읽어보면 출력문이 나타난다.


키보드 인풋으로 문자 이동하기

	case WM_KEYDOWN:
		if	(wParm == VK_UP) position.y -= 10;
		else if (wParm == VK_DOWN) position.y += 10;
		if	(wParm == VK_RIGHT) position.x += 10;
		else if (wParm == VK_LEFT) position.x -= 10;

		InvalidateRect(handle, nullptr, TRUE); // 잔상없이 움직일 수 있도록 만들었다. 화면을 다 날렸다가 다시 표시하라.
		break;

	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(handle, &ps);
		TextOut(hdc, position.x, position.y, "♥", 2);
		EndPaint(handle, &ps);
		break;
	}

위와 같이 키보드의 십자방향키 를 통해 좌표를 변경해줄 수 있다.


마우스의 좌표를 표시한다.

	case WM_MOUSEMOVE: // 마우스 움직임
		position.x = LOWORD(lParam); // 마우스 이벤트 lParam값을 16비트로 추출한다. 
		position.y = HIWORD(lParam); // 마우스 이벤트로 y좌표를 얻어온다. 16비트로

		InvalidateRect(handle, nullptr, TRUE);
		break;
	case WM_PAINT:
	{
		std::string str = "";
		str += std::to_string(position.x); // 정수를 문자열로 변경시켜준다.
		str += ", ";
		str += std::to_string(position.y); // 정수를 문자열로 변경시켜준다.

		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(handle, &ps);
		TextOutA(hdc, position.x, position.y, str.c_str(), str.length());

		//Line
		MoveToEx(hdc, 100, 100, nullptr);
		LineTo(hdc, 500, 500);

		//도형
		Rectangle(hdc, 500, 500, 600, 600);
		Ellipse(hdc, 300, 300, 400, 400);

		EndPaint(handle, &ps);

	}
	break;

위치 좌표에 대한 값을 출력해준다.


그림판 처럼 그림을 그릴 수 있다.

	case WM_LBUTTONDOWN: // 좌클릭 했을 때 그려짐
		position.x = LOWORD(lParam);
		position.y = HIWORD(lParam);
		is_clicked = TRUE;
		break;

	case WM_MOUSEMOVE:
		if (is_clicked)
		{
			HDC hdc = GetDC(handle);
			MoveToEx(hdc, position.x, position.y, nullptr);
			position.x = LOWORD(lParam);
			position.y = HIWORD(lParam);
			LineTo(hdc, position.x, position.y); // 선 그려주기
			ReleaseDC(handle, hdc);
		}
		break;

	case WM_LBUTTONUP:
		is_clicked = FALSE;
		break;


드래그 하여 사각형을 그린다.

	case WM_LBUTTONDOWN:
		start.x = LOWORD(lParam);
		start.y = HIWORD(lParam);
		is_clicked = TRUE;
		break;

	case WM_MOUSEMOVE:
		if (is_clicked)
		{
			end.x = LOWORD(lParam);
			end.y = HIWORD(lParam);
			InvalidateRect(handle, nullptr, TRUE);
		}
		break;
	case WM_LBUTTONUP:
		is_clicked = FALSE;
		break;

	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(handle, &ps);

		HPEN cur_pen = CreatePen(PS_DASH, 1, RGB(0, 255, 0));
		HPEN old_pen = static_cast<HPEN>(SelectObject(hdc, cur_pen)); // 생성 정보를 다시 담아준다.

		HBRUSH cur_brush = CreateSolidBrush((RGB(100, 4, 25)));
		HBRUSH old_brush = static_cast<HBRUSH>(SelectObject(hdc, cur_brush));

		Rectangle(hdc, start.x, start.y, end.x, end.y);
		SelectObject(hdc, old_pen);
		DeleteObject(cur_pen);

		SelectObject(hdc, old_brush);
		DeleteObject(cur_brush);

		EndPaint(handle, &ps);
	}
		break;

 

728x90