STUDY/Game Graphic

[C++ / 3D DirectX] 2. Window Creation & 3. Message Loop/WndProc

minari 2022. 2. 14. 20:02

🔗https://www.youtube.com/watch?v=nQTiSLiNyk4&list=PLqCJpWy5Fohd3S7ICFXwUomYW0Wv67pDD&index=3

🔗 https://wiki.planetchili.net/index.php/Hardware_3D_(C%2B%2B_DirectX_Graphics)_Tutorial_2

 

Hardware 3D (C++ DirectX Graphics) Tutorial 0 - Chilipedia

This video talks about the course syllabus for the Hardware 3D tutorial series (the shit we gonna learn) and the prerequisites for following this series (what you need to know, and what you need to have). Use it to get an idea of what this series is about,

wiki.planetchili.net

➕ DirectX11로 시작하는 3D 게임프로그래밍 (이용희 지음)

 

해당 영상과 글을 통해 공부하며 정리한 글입니다

 


💡 Today's TOPIC 💡

  • Win32 Entry Point Parameters
  • WinAPI Calling Convention (stdcall)
  • Microsoft Developer Network (MSDN)
  • Registering Window Class
  • Creating Window Instance

 

 

💠 WinMain이 뭔데 ?

 

https://docs.microsoft.com/ko-kr/windows/win32/learnwin32/winmain--the-application-entry-point

 

WinMain 애플리케이션 진입점 - Win32 apps

모든 Windows 프로그램에는 WinMain 또는 wWinMain이라는 진입점 함수가 포함됩니다.

docs.microsoft.com

 

WinMain() 함수는 윈도우 응용 프로그램의 시작 진입점(Main Entry Point)을 나타내는 함수이다.

응용 프로그램 실행 -> 운영체제는 해당 함수를 실행 => 응용 프로그램 시작

이 함수가 반환되면 응용 프로그램이 종료된다!

 

#include <Windows.h>

int CALLBACK WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
	return 0;
}

 

윈도우 프로그램의 프레임 워크

WinMain() 함수는 일반적인 윈도우 응용 프로그램이 되기 위해서 다음과 같은 기본적인 일을 처리해야한다.

 

① 윈도우 클래스를 시스템에 등록

   Window class : 윈도우의 특성/모양(Class Style)과 동작 방식(Window Procedure)을 나타낸다.
   이를 시스템에 등록하면 해당 클래스의 윈도우를 생성할 수 있다.
   윈도우 클래스의 윈도우 Precedure는 윈도우 클래스에 속하는 모든 윈도우가 윈도우 메시지를 어떻게 처리하는지 나타내는 함수이다.
   윈도우 클래스를 등록하게 해주는 윈도우 API 함수는 RegisterClassEx()

② 응용 프로그램의 초기화와 주 원도우 생성

   주 윈도우를 생성하고 화면에 보이도록 하는 윈도우 API 함수는 CreateWindow()

③ 메시지 루프

   응용 프로그램의 메시지 큐에서 메시지를 가져와서 (GetMessage)
   메시지를 해당 윈도우에게 전달하는 (DispatchMessage) 메시지 루프이다 

 

[1] GDI 와 DC ?

GDI(Graphics Device Interface) : Windows 운영체제가 제공하는 그래픽 출력 기능이다.

모니터와 프린터 등의 출력 장치에서는 그래픽 출력을 위해서 프로그래머가 사용할 수 있는 함수와 구조체를 제공한다.

즉, 사용자는 장치 별로 어떻게 출력시킬지 고민할 필요가 없다.

그냥 GDI만을 사용해서 세세하게 제어하고, 출력할 수 있다!

 

그렇지만 제어할 수 있는 것이 많다는 것은, 그만큼 설정해야하는 것이 많아 불편할 수 있다.

따라서

 

DC(Device Context)의 개념이 생긴다.

이는 Windows Application에서 화면 출력을 위한 출력 속성을 정의하는 구조체로, 기본 옵션값으로 출력이 가능하다.

Window OS는 화면에 출력을 하기 위해서는 반드시 DC가 필요하다. 

DC핸들은 출력 대상을 나타내는 구분 번호로, 모든 GDI 함수는 첫번째 인자로 DC 핸들을 요구한다.

(참고 : https://cking4w.tistory.com/161, https://luckygg.tistory.com/89)

 

 

💠코드 작성

[1] Window Creation & Message Loop

영상 따라서 코드를 작성해보니

" const char* 형식의 인수가 LPCWSTR 형식의 매개 변수와 호환되지 않습니다. " 라는 오류가 발생한다.

프로젝트 속성에서 유니코드 문자 집합 사용 -> 멀티바이트 문자 집합 사용으로 변경해주면 해결 ~!

 

이후 해당 코드를 실행해보면, 아무런 창도 뜨지 않는다.

윈도우를 만들기만 했지, show하지 않았기 때문이다.

따라서 ShowWindow(hWnd, SW_SHOW); 를 추가해준 뒤에 코드를 실행하면

새하얀 윈도우 창이 뜬다! 물론 상호작용은 아무것도 되지 않음 . . 

 

이는 message를 주고받지 못하기 때문이다. 아래 코드를 추가하자.

// message pump
	MSG msg;
	while (GetMessage(&msg, nullptr, 0, 0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

그러면 이제 창을 옮길 수 있고, 최소화, 창 종료가 가능해진다.

그런데 창을 종료했다고 해서 시스템까지 자동으로 종료되는 것은 아님! 디버거로 꺼주자.

 

[2] WndProc

윈도우 프러시저 : 주 윈도우가 처리해야하는 윈도우 메시지가 주 윈도우에게 전달될 때 호출되는 메시지 처리 함수이다.

메시지 Loop에서 DispatchMessage() 호출 -> WndProc() 실행

 

WndProc() 함수를 작성하고(맨 아래에 코드가 첨부되어 있습니다), wc.lpfnWndProc = WndProc;를 해주자.

이후 실행해보면 앞서 실행 결과와 마찬가지고 창이 생성되는데, 이번에는 창 종료시 시스템도 자동으로 종료된다.

 

 

💠 전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <Windows.h>
 
// 윈도우 핸들, 메시지 ID, 2개의 매개변수
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        PostQuitMessage(7);
        break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}
 
int CALLBACK WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
{
    const auto pClassName = "hw3dbutts";
    // 윈도우 클래스 등록
    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(wc);
    wc.style = CS_OWNDC;
 
    // 윈도우 프리시저를 설정
    wc.lpfnWndProc = WndProc; // 처음엔 DefWindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = nullptr;
    wc.hCursor = nullptr; 
    wc.hbrBackground = nullptr;
    wc.lpszMenuName = nullptr;
    wc.lpszClassName = pClassName;
    wc.hIconSm = nullptr;
    RegisterClassEx(&wc);
 
    // create window instance
    HWND hWnd = CreateWindowEx(
        0, pClassName,
        "Happy Hard Window",
        WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
        200200640480,
        nullptr, nullptr, hInstance, nullptr
    );
 
    // show the window
    ShowWindow(hWnd, SW_SHOW);
    
    // message pump
    MSG msg;
    BOOL gResult;
    while ((gResult = GetMessage(&msg, nullptr, 00)) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
    // 오류 조건 밎 경고 조건을 나타내는데 사용
    // '[26004] hw3d.exe' 프로그램이 종료되었습니다(코드: 7 (0x7)). 로 출력된다.
    if (gResult == -1)
    {
        return -1;
    }
    else
    {
        return msg.wParam;
    }
 
    return 0;
}
cs