C++

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

k-codestudy 2025. 1. 22. 17:20

오늘은 API 클래스화에 대한 수업을 들었다.

 

api.h

#pragma once

#include <windows.h>
#include "msgProc.h"

class C_API
{
private:

	static C_API* m_pApi;
	HWND				m_hWnd;
	HINSTANCE			m_hInstance;
	C_MSGPROC			m_cMsgProc;

private:
	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
	C_API() = default;

public:
	static void createApi();
	static C_API* getApi();
	static void releaseApi();
	bool init(HINSTANCE hInstance);
	void Msg();
};

 

api.cpp

#include "api.h"
#include "resource.h"

C_API* C_API::m_pApi = nullptr;

void C_API::createApi()
{
    if (!m_pApi)
        m_pApi = new C_API{};
}

C_API* C_API::getApi()
{
    return m_pApi;
}

void C_API::releaseApi()
{
    if (m_pApi)
    {
        delete m_pApi;
        m_pApi = nullptr;
    }
}

bool C_API::init(HINSTANCE hInstance)
{
    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);

    m_cMsgProc.init();

    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);
    UpdateWindow(hWnd);

    return true;
}

void C_API::Msg()
{
    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->m_cMsgProc.apiProc(hWnd, message, wParam, lParam);
}

C_API 클래스는 윈도우 애플리케이션을 초기화하고 메시지 루프를 관리하며, Singleton 패턴을 사용하여 애플리케이션 내에서 단일 인스턴스만 유지된다.

 

createApi()

  • C_API 클래스의 인스턴스를 생성한다
  • 이미 인스턴스가 존재하면 아무 작업도 수행하지 않는다

getApi()

  • 생성된 C_API 인스턴스를 반환한다

releaseApi()

  • C_API 인스턴스를 삭제하고 메모리를 해제한다

init(HINSTANCE hInstance)

  • 윈도우 클래스를 등록하고, 윈도우를 생성 및 초기화한다
  • 성공하면 true, 실패하면 false를 반환한다

Msg()

  • 메시지 루프를 실행한다
  • GetMessage 함수로 메시지를 대기 및 처리한다

WndProc

  • 윈도우 프로시저 콜백 함수이다
  • 메시지가 발생할 때 호출되며, C_MSGPROC의 메시지 처리 함수로 위임한다

msgProc.h

#pragma once

#include <windows.h>


class C_MSGPROC
{
private:
	LRESULT(C_MSGPROC::* m_arProc[WM_USER])	(HWND, UINT, WPARAM, LPARAM);  // WM_USER 여기까지 쓸꺼라고 예약이 되어있음 그렇기에 값을 WM_USER까지라고 하면 됨  

	LRESULT onPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
	LRESULT onDestroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
	LRESULT onKeyDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

public:
	void init();
	LRESULT apiProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

msgProc.cpp

#include "msgProc.h"

LRESULT C_MSGPROC::onPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hWnd, &ps);
	EndPaint(hWnd, &ps);

	return 0;
	//return LRESULT(); // 함정 : 이게 뭘까 -> 메시지 처리의 서명된 결과 ( 성공이 0 이다. )
}

LRESULT C_MSGPROC::onDestroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PostQuitMessage(0);
	return 0;
}

LRESULT C_MSGPROC::onKeyDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (wParam == VK_ESCAPE)
		DestroyWindow(hWnd);

	return 0;
}

void C_MSGPROC::init()
{
	m_arProc[WM_PAINT] = &C_MSGPROC::onPaint;
	m_arProc[WM_DESTROY] = &C_MSGPROC::onDestroy;
	m_arProc[WM_KEYDOWN] = &C_MSGPROC::onKeyDown;
}

LRESULT C_MSGPROC::apiProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message >= WM_USER || !m_arProc[message])
		return DefWindowProc(hWnd, message, wParam, lParam);

	return (this->*m_arProc[message])(hWnd, message, wParam, lParam);

}

C_MSGPROC 클래스는 개별 메시지(WM_PAINT, WM_DESTROY, WM_KEYDOWN)에 대한 처리 로직을 정의하고 이를 관리합니다.

 

init()

  • 처리할 메시지를 배열 m_arProc에 등록한다
  • 각 메시지는 해당 메시지에 대응하는 멤버 함수 포인터로 매핑된다

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

  • 전달된 메시지를 처리한다
  • 등록된 메시지인지 확인한 후, 해당 메시지 처리 함수를 호출한다
  • 등록되지 않은 메시지는 기본 윈도우 프로시저(DefWindowProc)로 처리한다

onPaint()

  • WM_PAINT 메시지를 처리합니다. 창을 다시 그릴 때 호출된다

onDestroy()

  • WM_DESTROY 메시지를 처리합니다. 창이 종료될 때 호출되며 메시지 루프를 종료한다

onKeyDown()

  • WM_KEYDOWN 메시지를 처리합니다. 키보드 입력(예: ESC 키)을 처리한

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::createApi();
	C_API::getApi()->init(hInstance);
	C_API::getApi()->Msg();
	C_API::releaseApi();
}

윈도우 애플리케이션의 진입점으로, C_API의 생명주기를 관리

  1. C_API::createApi()로 C_API 인스턴스를 생성한다
  2. init(hInstance)로 윈도우 초기화 및 생성에 성공하면, 메시지 루프(Msg())를 실행한다
  3. 애플리케이션 종료 후 C_API::releaseApi()로 메모리를 해제한다