C++

[강의] 1월 21일 수업정리

k-codestudy 2025. 1. 21. 17:30

오늘은 비트마스크를 이용한 마우스 X, Y값 출력과 API를 클래스화 시키는 부분에 대한 수업을 들었다.

 

1. 비트마스크를 이용한 마우스 X, Y값 출력

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int nPosX{};
	static int nPosY{};

	switch (message)
	{
	case WM_MOUSEMOVE:
	{
		nPosX = lParam & 0x0000ffff;         
		nPosY = (lParam & 0xffff0000) >> 16; 
		InvalidateRect(hWnd, nullptr, true);

		break;
	}
	case WM_KEYDOWN:
	{
		if (wParam == VK_ESCAPE)
		{
			DestroyWindow(hWnd);
		}
		break;
	}
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hWnd, &ps);

		wchar_t strPrint[128]{};
		swprintf_s(strPrint, 128, L"%d, %d", nPosX, nPosY);
		TextOut(hdc, 100, 100, strPrint, 20);

		EndPaint(hWnd, &ps);
	}
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

 

lParam을 이용한 좌표 분리

  • nPosX = lParam & 0x0000FFFF : 하위 16비트에서 X 좌표 추출.
  • nPosY = (lParam & 0xFFFF0000) >> 16 : 상위 16비트를 오른쪽으로 16비트 이동하여 Y 좌표 추출.
  • InvalidateRect : WM_PAINT 메시지를 호출하여 화면 갱신.
  • swprintf_s와 TextOut : 마우스 좌표를 화면에 출력.

wParam의 가상 키 코드 

  • 시스탬에서 사용하는 가상 키 코드에 해당하는 기호 상수 이름, 16진수 값 및 마우스 또는 키보드를 보여준다. 코드는 숫자 순서대로 나열됨 
  • 각각의 키보드에는 고유한 ID가 부여되어 있음
  • wParam : 입력  / lParam : 상태 

WM_KEYDOWN 참고

lParam의 비트 구조:

  • 0-15 비트: 반복 횟수.
  • 16-23 비트: 스캔 코드.
  • 24 비트: 확장 키 여부 (1: 확장 키, 0: 기본 키).
  • 30 비트: 이전 키 상태 (1: 키가 이미 눌린 상태, 0: 처음 누른 상태).

메시지 흐름 정리

  • DestroyWindow : 프로그램 종료 요청.
  • PostQuitMessage : WM_QUIT 메시지를 메시지 큐에 추가.
  • InvalidateRect : WM_PAINT 호출을 트리거.
  • 처리 중인 메시지는 끝까지 처리해라 라는 의미이며 종료 시점을 명확하게 가져가게 하기 위해서 하는 것 

2. API 클래스화

Api.h

#pragma once

#include <Windows.h>

class C_API
{
private:
	static C_API* m_pApi;
	HWND			m_hWnd;
	HINSTANCE		m_hinstance;

	int				m_nData;
private:
	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
	LRESULT apiProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

public:
	C_API() = default;
	bool init(HINSTANCE hInstance);
	void updateMsg();
};

 

 

Api.cpp

#include "api.h"
#include "Resource.h"

C_API* C_API::m_pApi = nullptr;

bool C_API::init(HINSTANCE hInstance)
{
	m_pApi = this;

	WNDCLASSEXW wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINAPI));
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = 0;
	wcex.lpszClassName = L"TEST";
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	RegisterClassExW(&wcex);


	HWND hWnd = CreateWindowW(L"TEST", 0, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

	if (!hWnd)
		return FALSE;

	ShowWindow(hWnd, SW_SHOWDEFAULT); // 안받아도 무관하기에 SW_SHOWDEFAULT
	UpdateWindow(hWnd);

	return true; // 성공 보장 
}

void C_API::updateMsg()
{
	MSG msg;

	while (GetMessage(&msg, nullptr, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

LRESULT CALLBACK C_API::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	return m_pApi->apiProc(hWnd, message, wParam, lParam);
}

LRESULT C_API::apiProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	m_nData = 100;

	switch (message)
	{
	case WM_COMMAND:
	{

	}
	break;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
	}
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

 

C_API* C_API::m_pApi = nullptr;

  • 전역 변수를 제거하기 위해 static C_API* m_pApi를 사용해 정적 멤버로 클래스 자체를 참조.

bool C_API::init(HINSTANCE hInstance)

  • init 함수에서 윈도 초기화 및 생성
  • IDI_WINAPI, IDI_SMALL 이거 리소스에 들어가 있기 때문에 위에 #include "Resource.h"를 붙여줌으로 에러를 없앰 
  • 성공 실패 유무를 확인해야 하기 때문에 init을 bool형으로 바꿔 줌 

void C_API::updateMsg() 

  • 메시지 루프 처리에 대한 부분

LRESULT CALLBACK C_API::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

  • WndProc은 전역 함수이므로 클래스 멤버를 직접 호출할 수 없음.
  • 이를 해결하기 위해 apiProc을 멤버 함수로 작성하고 WndProc에서 호출

LRESULT C_API::apiProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

  • 기존의 WndProc의 기능을 가지고 있다.

main.cpp

#include "framework.h"
#include "winApi.h"
#include "api.h"

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPWSTR    lpCmdLine,
	_In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	C_API cApi{};

	cApi.init(hInstance);
	cApi.updateMsg();
}

 

'C++' 카테고리의 다른 글

[강의] 1월 23일 수업정리  (0) 2025.01.23
[강의] 1월 22일 수업정리  (0) 2025.01.22
[강의] 1월 20일 수업정리  (1) 2025.01.20
[강의] 1월 17일 수업정리  (0) 2025.01.17
[강의] 1월 16일 수업정리  (0) 2025.01.16