Replicated

Windows API로 진입점 만들기 본문

DirectX 11

Windows API로 진입점 만들기

라구넹 2025. 4. 7. 02:24

https://github.com/Lagooneng/DirectX11Study

 

GitHub - Lagooneng/DirectX11Study: DirectX11 공부

DirectX11 공부. Contribute to Lagooneng/DirectX11Study development by creating an account on GitHub.

github.com

 

#pragma once
#include "DirectXMath.h"

using int8 = __int8;
using int16 = __int16;
using int32= __int32;
using int64 = __int64;
using uint8 = unsigned __int8;
using uint16 = unsigned __int16;
using uint32 = unsigned __int32;
using uint64 = unsigned __int64;

using Vec2 = DirectX::XMFLOAT2;
using Vec3 = DirectX::XMFLOAT3;
using Vec4 = DirectX::XMFLOAT4;
using Color = DirectX::XMFLOAT4;
#pragma once
#include "Types.h"

const int32 GWinSizeX = 800;
const int32 GWinSizeY = 600;

일단 이런식으로 필요한 헤더 선언해주고

 

미리 컴파일된 헤더 사용 설정

헤더 이름은 pch.h로 해주고

 

pch.cpp 속성 가서 pch에는 미리 컴파일된 헤더에서 만들기로 설정

 

#pragma once

#include "Types.h"
#include "Values.h"
#include "Struct.h"

//STL
#include <vector>
#include <list>
#include <map>
#include <unordered_map>
using namespace std;

// WIN
#include <Windows.h>
#include <assert.h>

// DX
#include <d3d11.h>
#include <d3dcompiler.h>
#include <wrl.h>
#include <DirectXMath.h>
#include <DirectXTex/DirectXTex.h>
#include <DirectXTex/DirectXTex.inl>
using namespace DirectX;
using namespace Microsoft::WRL;

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")

#ifdef _DEBUG
#pragma comment(lib, "DirectXTex\\DirectXTex_debug.lib")
#else
#pragma comment(lib, "DirectXTex\\DirectXTex.lib")
#endif // _DEBUG

pch에는 필요한 거 다 넣어놓기

 

추가 파일, 링크할 라이브러리 파일 디렉터리 연결

 

필요한 거 다 넣어주기

 


 

// GameCoding.cpp : 애플리케이션에 대한 진입점을 정의합니다.
//
#include "pch.h"
#include "framework.h"
#include "GameCoding.h"
#include "Game.h"

#define MAX_LOADSTRING 100

// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
HWND hWnd;

// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    // 윈도우 창 정보 등록
    MyRegisterClass(hInstance);

    // 윈도우 창 생성
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    Game game;
    game.Init(hWnd);

    MSG msg = {};

    // 기본 메시지 루프입니다:
    while (msg.message != WM_QUIT)
    {
        if ( ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            game.Update();
            game.Render();
        }
    }

    return (int) msg.wParam;
}



//
//  함수: MyRegisterClass()
//
//  용도: 창 클래스를 등록합니다.
//
ATOM MyRegisterClass(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_GAMECODING));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = L"GameCoding";
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   함수: InitInstance(HINSTANCE, int)
//
//   용도: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
//   주석:
//
//        이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
//        주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

   RECT windowRect = { 0, 0, GWinSizeX, GWinSizeY };
   ::AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, false);

   hWnd = CreateWindowW(L"GameCoding", L"Client", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ::ShowWindow(hWnd, nCmdShow);
   ::UpdateWindow(hWnd);

   return TRUE;
}

//
//  함수: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  용도: 주 창의 메시지를 처리합니다.
//
//  WM_COMMAND  - 애플리케이션 메뉴를 처리합니다.
//  WM_PAINT    - 주 창을 그립니다.
//  WM_DESTROY  - 종료 메시지를 게시하고 반환합니다.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 메뉴 선택을 구문 분석합니다:
            switch (wmId)
            {
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

이거 만들면 나오는 거 조금 변경된 코드

메뉴 창 같은 거 날리고 게임이랑 연결

 

1. 진입점

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    // 윈도우 창 정보 등록
    MyRegisterClass(hInstance);

    // 윈도우 창 생성
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    Game game;
    game.Init(hWnd);

    MSG msg = {};

    // 기본 메시지 루프입니다:
    while (msg.message != WM_QUIT)
    {
        if ( ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            game.Update();
            game.Render();
        }
    }

    return (int) msg.wParam;
}

이 부분은 진입점 코드

hInstance : 프로그램의 인스턴스 핸들. 윈도우를 만들거나 리소스 로딩 시 필요

lpCmdLine : 명령줄 인자 문자열

nCmdShow : 윈도우가 시작 시 어떤 상태로 보여줄지(최소화, 최대화 등)

 

MyRegisterClass(hInstance);

윈도우의 클래스 정보 등록

 

if (!InitInstance (hInstance, nCmdShow)) {
    return FALSE;
}

윈도우 실제 생성 및 화면 표시

 

Game game;
game.Init(hWnd);

게임 초기화

 

MSG msg = {};
while (msg.message != WM_QUIT)
{
    if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        game.Update();
        game.Render();
    }
}

게임 루프

PeekMessage() : 마우스 클릭, 키보드 입력 등을 확인하고 처리

메시지가 없을 때만 Update랑 Render를 돌리는 구조

 

return (int) msg.wParam;

프로그램 종료 시 종료 코드 리턴

 

2. 클래스 등록

윈도우를 만들기 전 창이 어떤 모습인지 정의할 필요가 있음

WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);

WNDCLASSEXW : 윈도우 창의 속성을 정의하는 구조체

그 밑에는 구조체 사이즈 지정

 

wcex.style = CS_HREDRAW | CS_VREDRAW;

창이 바뀌면 다시 그려라

 

CS_HREDRAW: 가로 크기 바뀌면 다시 그림

CS_VREDRAW: 세로 크기 바뀌면 다시 그림

 

wcex.lpfnWndProc = WndProc;

윈도우 메시지를 처리할 함수 지정

 

wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;

 

클래스/윈도우에 추가적인 메모리를 붙일 건지 설정

 

wcex.hInstance = hInstance;

현재 실행 중인 앱 인스턴스 핸들

 

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GAMECODING));

앱 아이콘 로딩

 

wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);

마우스 커서 모양 지정 (IDC_ARROW는 기본 화살표)

 

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

배경색 설정. 시스템 기본 윈도우 배경색

 

wcex.lpszMenuName = NULL;

메뉴 안 씀

 

wcex.lpszClassName = L"GameCoding";

이 클래스의 이름. CreateWindow()에서 이 이름으로 창을 생성함

 

wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

작은 아이콘 (작업 표시줄용)

 

return RegisterClassExW(&wcex);

이걸 호출하면 OS에 “이런 스타일의 창을 만들 거야” 하고 등록하는 거고,

나중에 CreateWindow(L"GameCoding", ...) 이런 식으로 이 클래스를 써서 창을 만듦.

 

 

3. 실제 윈도우 창 만들기

hInst = hInstance;

전달받은 앱 인스턴스 핸들을 전역 변수 hInst에 저장. → 나중에 리소스 로딩 등에 사용될 수 있음.

 

RECT windowRect = { 0, 0, GWinSizeX, GWinSizeY };
::AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, false);

원하는 클라이언트 영역 크기를 가진 창을 만들기 위해 윈도우 전체 크기를 계산

GWinSizeX, GWinSizeY는 전역 상수로 가로세로 크기

AdjustWindowRect() - WS_OVERLAPPEDWINDOW 설정을 통해 타이틀바, 테두리 포함된 실제 창 크기로 조정해줌

 

hWnd = CreateWindowW(
    L"GameCoding",          // 윈도우 클래스 이름 (MyRegisterClass에서 등록한 이름)
    L"Client",              // 윈도우 타이틀
    WS_OVERLAPPEDWINDOW,    // 윈도우 스타일 (타이틀, 크기 조절 등 포함)
    CW_USEDEFAULT, 0,       // 기본 위치
    windowRect.right - windowRect.left,   // 너비
    windowRect.bottom - windowRect.top,   // 높이
    nullptr, nullptr,       // 부모 윈도우, 메뉴 없음
    hInstance,              // 앱 인스턴스 핸들
    nullptr);               // 추가 데이터 없음

윈도우 생성

성공하면 hWnd에 윈도우 핸들이 들어감

 

if (!hWnd) {
    return FALSE;
}

창 생성 실패하면 FALSE 반환해서 wWinMain()에서 바로 종료하도록 함

 

::ShowWindow(hWnd, nCmdShow);
::UpdateWindow(hWnd);

 

ShowWindow: 윈도우를 화면에 표시 (최소화/최대화 방식은 nCmdShow에 따라)

UpdateWindow: 윈도우를 강제로 즉시 다시 그림 (WM_PAINT 메시지 발생)

 

4. 이벤트 수신

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

 

hWnd: 이벤트가 발생한 윈도우의 핸들

message: 어떤 메시지가 왔는지 (예: 마우스 클릭, 키 입력, 윈도우 닫힘 등)

wParam, lParam: 메시지에 따라 달라지는 추가 정보

 

case WM_COMMAND:
{
    int wmId = LOWORD(wParam); // 어떤 메뉴/컨트롤인지 구분
    switch (wmId)
    {
    case IDM_EXIT:
        DestroyWindow(hWnd); // 윈도우 파괴 요청 → WM_DESTROY 발생함
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam); // 기본 처리
    }
}
break;

명령 발생 시

일단 종료 명령만 구분하고

그 외는 DefWindowProc에 맡겨서 OS 기본 동작 수행하게 함

 

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    // hdc(디바이스 컨텍스트)로 그리기 작업 가능
    EndPaint(hWnd, &ps);
}
break;

 

윈도우 다시 그려야 하는 경우

여기서 hdc를 사용해서 텍스트, 도형, 이미지 등을 직접 그릴 수 있음

 

case WM_DESTROY:
    PostQuitMessage(0); // 메시지 루프에 WM_QUIT 전송 → 앱 종료 신호
    break;

DestroyWindow() 호출하면 이 메시지가 옴

앱 종료 마무리 동작

 

default:
    return DefWindowProc(hWnd, message, wParam, lParam);

위에서 처리하지 않은 메시지는 모두 Windows OS 기본 동작에 맡김

 


 

#include "pch.h"
#include "Game.h"

Game::Game()
{
}

Game::~Game()
{
}

void Game::Init(HWND hand)
{
	_hwnd = hand;
	_width = GWinSizeX;
	_height = GWinSizeY;
}

void Game::Update()
{
}

void Game::Render()
{
}

이제 기본 세팅 끝나서 Game 클래스만 조정하면 됨

'DirectX 11' 카테고리의 다른 글

그래픽스 개요  (1) 2025.04.06