원도우 메세지 루프

Yama·2024년 1월 18일
0

어소트락 수업

목록 보기
48/55

코드를 볼때 청녹색 계열을 보면

  • f12를 눌러서 확인하면된다.
  • 기본자료형을 재정의 해둔 경우가 많다.
  • typedef가 연속으로 있는 경우다
    • __int64(8바이트) 를 INT_PTR로 재정의한 경우다.
    • INT_PTR__int64이 자료형에 포인터를 붙여서 재정의 한것이 PINT_PTR이다.
	typedef int MYINT
    typedef int * PMYINT;
  • intMYINT로 재정의 한것이다.
  • MYINTint니까 거기에 포인터 붙인걸 PMYINT로 재정의 한것.
	MYINT* pA = &a;
	PMYINT pA = &a;
  • 두 코드는 이제 같은 의미다.
	typedef int MYINT, *PMYINT;
  • 겹치는 int를 연달아 지은것.
  • 코드를 간략화 한것이다.
	typedef int INT;
	typedef int TIME;
	TIME time;
  • 같은 자료형도 마이크로소프트애들은 가독성을 높이기 위해서 재정의를 많이 해뒀다.
    • 강사님은 자료형으로 구분하는것보다 변수명으로 구분하는걸 더 선호하신다.
      • 가독성이 오히려 떨어진다고 생각한다.
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
  • 이제 설명할용도아니고서는 필요 없는 기본 함수들이니 다 지워버린다.

메세지 루프

단축키 체크

	HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GAMECLIENT));
  • 리소스(단축키 테이블 핸들 얻는 테이블)
  • LoadAccelerators함수는 단축키를 체크한다
    • 리소스 뷰에 accelerator테이블에 가면 있다.
      • 사용하는 아이디값은 IDC_GAMECLIENT
    • 단축키(특징 키)가 IDM_ABOUT에 저장이 되어 있다.
    • 테이블 정보의 핸들값을 받아온다.
    • accelerator테이블도 직접적으로 접근하지 못하기 때문에 커널오브젝트인것이다.
    • hAccelTable에 단축키가 들어올때 핸들값을 저장한다.
    • 원도우에 들어온 것이 단축키 조합이 발생한지 체크한다.
    while (GetMessage(&msg, nullptr, 0, 0))
    {
    	// 1
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int)msg.wParam;
  • 1번에서 단축키가 눌렀는지 2번쨰인자(hAccelTable)로 판단한다.
	// 1
	LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {	
    // 케이스 코드 많이 생략
		// 2
   		case WM_COMMAND:
        {
        	// 3
  	       case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        }
    }
  • 단축키가 눌렸다면 1번 함수에서 메세지 처리를 한다.

    • 2번에서 키가 눌렀다는 케이스 체크한다.
      • 3번케이스인 ALT + ?를 눌르면 IDM_ABOUT의 아이디 케이스로 들어가서 다이얼 로그가 생기는 것이다.
  • 단축키가 발생하면 메세제는 WM_COMMAND가 발생하고 해당 메세지의 부수적인 파라미터가 들어오는데 wParam에 어떤 단축키인지 조합에 해당하는 아이디값인 IDM_ABOUT의 값이 들어오고 저 아이디의 값인 케이스인 3번으로 가서 다이얼 로그 창을 만든다.

  • 단축키가 발생하면 처리하는 함수가 있다 정도로 알고 가자.

메세지 구조체

  • 일반적인 메세지 처리 하기.
  MSG msg;
  • 메세지 루프

  • 메세지 정보를 받을 구조체 변수다.

typedef struct tagMSG {
	// 1
    HWND        hwnd;
    // 2
    UINT        message;
    // 3
    WPARAM      wParam;
    // 3
    LPARAM      lParam;
    // 4
    DWORD       time;
    // 5
    POINT       pt;
#ifdef _MAC
    DWORD       lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
  • MSG구조체 내부 맴버들
    • 1번은 어느 원도우에서 발생한지 체크하는용도의 원도우의 핸들값
      • 하나의 프로세스가 여러개의 프로세스가 관리할수도 있기 떄문이다.
          while (GetMessage(&msg, nullptr, 0, 0))
          if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
          {
          	// 처리
              TranslateMessage(&msg);
              // 분석
              DispatchMessage(&msg);
          }
      • 프로세스에서 while문을 메세지를 꺼내봤더니 어떤 원도우창에서 클릭이 발생한지 구별해서 처리해준다.
  • 2번은 그래서 원도우의 메세지들을 받는 맴버변수.
  • https://wiki.winehq.org/List_Of_Windows_Messages
    - 원도우 메세지를 확인하는 사이트

    - 특정 몇개만 쓴다.
  • 디버깅중에는 이런 매크로만 확인된다.
    • 0x0100 == 256(10)
  • 중단걸어서 값을 확인하면 256이 들어있을것이다.
  • 2번에서 WM_KEYDOWN처럼 키가 눌렀다는 걸 알고
    • 3번들은 추가적인 파라미터로 들어오는지 확인하는 구조체 맴버다.
  • 4번은 메세지가 발생한 시간
  • 5번은 메세지가 어느 위치에서 발생했는데 알려주는
    • 마우스 관련 메세지였다면 좌표값이 들어온다.

GetMessage

        while (GetMessage(&msg, nullptr, 0, 0))
  • msg에 구조체의 주소를 알려줘야 발생한 메세지의 정보들을 채워줄것이다.

    • 구조체의 크기가 커서 주소로 받았다.
  • 특징

    1. 리턴 조건 - 메세지 큐에 메세지가 있으면 리턴
      • 포커싱 되어 있는 프로세스로 메세지 큐가 들어간다.
    • GetMessage함수는 메세지 큐에서 정보를 꺼내고 큐를 비워버린다.
    • while문이 무한 반복으로 돌면서 메세지 큐가 반응하는지 체크한다.
      • 반응형 프로그램
      • 메세지가 있건 없건 게임은 돌아가야되는데 반응형프로그램은 반응을 해야 작동하기 때문에 게임에 맞지 않다.
    1. WM_QUIT이 발생하면 GetMessage함수는 false를 반환.
      • WM_QUIT이 들어왔다는 것은 프로세스 종료하라는 의미다.
        - WM_QUIT 이외 메세지면 GetMessae 함수는 true를 반환.
      • true면 반복문이 분석하고 처리한다.

CallBack

  • 함수의 주소를 알려줘서, 특정 상황(조건) 이 맞으면 알려준 함수가 호출되는 구조

메인윈도우 CallBack 상황

// while문 안에 있다.
	DispatchMessage(&msg);
  • msg.hwnd에 담긴 핸들값에 원도우 정보의 핸들값으로 그 함수의 주소를 호출한다.

  • DispatchMessage함수안에서 함수 포인터로 프로시저를 각각의 프로시저를 호출한다.

  • 함수 포인터의 기능을 사용해서 각각의 프로시저 함수에 접근한다.

	LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  • 우리가 만든 원도우 메세지를 처리하는 프로시저함수다.

    • &msg로 프로세스의 종류도 파악가능하니 각각의 메세지 큐에서 이 원도우는 어떤 방식으로 처리하겠다는 함수가 각각 존재해야 한다.
      • 각각의 원도우 발생한 메세지를 처리하는 함수다.
  • 함수의 주소를 알려줘서, 특정 상황(조건)이 맞으면 알려준 주소의 맞는 함수가 호출되는 구조

  • 상황이 발생하면 딱 들어온다(콜백)

  • 코드흐름은 내가 짠대로 함수를 호출하면 함수를 그대로 호출했다. but 콜백은 다름

    • 함수를 호출했는데 다른 함수가 띵하고 들어옴
      • 함수 내부로 들어가서 안에서 함수의 포인터를 이용해서 다른 함수를 호출
      • 부르면 응답.
// while문 안에 있다.
	DispatchMessage(&msg);
// 프로시저함수
 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
  • 반복문이 돌다가 갑자기 프로시저의 함수로 가버린다.
  • 해당 메인 원도우에 무슨일이 발생하면 호출하는 함수를 알려주었기 때문에 처리할떄 무슨일이 발생하면 프로시저 함수로 CallBack한다.

도움말 원도우의 프로시저 함수

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
	// 3
    case WM_COMMAND:
    	// 4
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
        	// 5
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;

    }
    return (INT_PTR)FALSE;
}
  • 아이디값이 About일떄 작동하는 프로세스의 프로시저 함수
  • 이 창도 상황이 발생하면 처리기 함수가 따로있을것이다.
// 메인 윈도우(WndProc)를 프로시저 함수다.
    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // 메뉴 선택을 구문 분석합니다:
        switch (wmId)
        {
        // 1
        case IDM_ABOUT:
        	// 2
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;   
            // 아랫부분 생략
        }
  • 메인윈도우가 돌아가고있을떄 단축키가 발생하면 IDM_ABOUT값이 1번 케이스의 DialogBox라는 원도우 생성 함수다.

  • CreateWindowW원도우랑 똑같은 거다.

    • 어떤 형태를 갖출지 클래스 정보를 가져왔었다.
      • 생짜로 원도우를 만드는것이다.(알려줄 것들이 많다 그래서 클래스만듬)
  • DialogBox는 정보를 갖출 클래스 정보가 없다.

    • 프로시저 호출만 함수를 호출하고 끝이다.
    • 2번은 간단한 버전의 원도우 생성버전이다.
      - 2번의 2번쨰 인자로 클래스 정보 대신 id(IDD_ABOUTBOX)값을 받아간다.

      - 리소스뷰에서 내가 이미 디자인을 했기 때문에 활용을 해서 간단하게 창을 만들 수 있다.
      - 리소스 디자인을 만들고 형태를 갖춘다음에 이 id값을 부여하고 적어주면 창을 만들수 있다.
  • 이 간단한 원도우도 메세지가 발생하면 처리해야하는 함수인 프로시저가 필요하기 때문에 4번쨰 인자로 About을 받았다.

  • 프로시저 함수의 3번 케이스에 걸려서 사진 4번의 사진의 확인을 클릭하면 종료된다.

    • 주석으로 5번을 막아버리면 확인을 눌러도 안꺼진다.
  • x버튼은 IDCANCEL것을 원도우가 기본적으로 정의해놨다.

  • 4번의 조건이 발생해도 IDCANCEL``IDOK이 발생하더라도 아무일도 안 일어나서 창이 종료되지않는다.

프로시저함수 정리

  • 각 윈도우들은 해당 윈도우에 메세지가 발생했을 때 처리를 해줄 프로시저함수를 등록해야한다.
  • 메인 윈도우는 MyRegisterClass 함수 안에서 윈도우 정보를 만들때 호출함 함수의 주소를 등록해둠
  • 도움말 윈도우는 다이얼로그 형태로서, DialogBox 함수를 호출할때 사용할 프로시저 함수의 주소를 입력으로 넣어줬다.

반응형 메세지 루프로 게임을 만들수 없는것은 아니다.

  • GetMessage특징이 리턴조건이 메세지 큐에 메세지가 있어야 반환이 된는걸 활용해서 강제로 메세지 큐에 메세지를 넣어줄것이다.
    • 장작넣어주는거랑 비슷
 	SetTimer(g_hWnd, 0, 50, nullptr );
  • 첫번째 인자로 g_hWnd 핸들값을 넣어준다.
  • 두번째 인자로 몇번쨰 타이머를 의미하는 인자니까 0번쨰 타이머라고 적어주고
  • 세번쨰 인자는 50 == 0.05초마다 타이머 발동
    • 1000이 1초다.
  • 네번쨰 인자는 타이머가 발동할 떄마다 함수포인터를 달라고하는 인자
    • 굳이 함수포인터를 안알려줘도 되서 nullptr
// WndProc프로시저 안에 있는 케이스
		case WM_TIMER;
		{
			int a = 0;
		}
		break;
  • 프로시저에서 케이스를 추가해서 케이스를 추가함.
  • 0.5초마다 메시지 큐에 메세지를 넣어주고 있다.
    • 너무 거쳐거쳐와서 비효율적이다.
  • 타이머를 사용해서 구현을 할수는 있지만 성능적으로 문제가 있어서 사용하지 않을것이다.
	KillTimer(g_hWnd, 0);
  • 핸들에 접근해서 0번째 타이머를 종료한다.
  • 아이디에 맞는(Timer)에 맞는 커널 오브젝트 종료함수도 존재한다.
    • 커널 오브젝트는 내가 직접 관리를 못하고 id값만 알려주었기 떄문에 메모리에서 삭제를 프로그래머가 직접 못해서 윈도우에서는 id를 부여하니까 그 id에 맞는 삭제하는 함수도 알려준다.
  • 원도우도 우릭가 삭제해야하지 않나용? GetMessageWM_QUIT 나왔다는거 자체가 종료한다는 것.
    • 내부적으로 삭제한다.
// WndProc프로시저 안에 있는 케이스
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
  • 이 함수를 호출햇다는거 자체가 이 원도우가 꺼지면 난 프로세스를 종료할거라는 의지가 담긴 코드다.
    • 메세지 큐에 WM_QUIT를 넣고 프로세스랑 같이 디진다.
  1. PostQuitMessage의 역할과 순서

    • PostQuitMessage 함수는 윈도우가 종료되기 전에 호출되며, 프로그램이 종료되기 전에 메시지 큐에 WM_QUIT 메시지를 넣습니다.
    • 이 함수는 윈도우가 실제로 종료되기 전에 처리되며, 'X' 버튼을 눌렀을 때 윈도우가 종료되는 과정에서 사용됩니다.
  2. 프로세스와 윈도우의 관계

    • 하나의 프로세스는 여러 윈도우를 가질 수 있으며, 윈도우가 메모리에서 해제될 때 프로세스가 종료될 수 있습니다.
    • 원도우 어플리케이션에서 기본으로 제공하는 틀을 조금 튜닝한 원도우는 프로세스와 원도우가 1대1 매칭으로 종료된다.
      • 애플리케이션의 설계에 따라서 모든 프로세스와 원도우가 1대1로 종료되는것은 아니다.
  3. PostQuitMessage와 프로세스 종료

    • PostQuitMessage는 "이 윈도우가 닫히면 프로세스를 종료할 것"이라는 의도를 나타내지만, 항상 즉각적인 프로세스 종료를 의미하지는 않습니다.
    • 이 함수는 메시지 큐에 WM_QUIT 메시지를 넣어 메시지 루프가 종료되면 프로세스도 종료될 수 있도록 합니다.
    • 하지만 프로세스가 여러 윈도우를 가지고 있거나, 다른 종료 조건이 있는 경우 즉시 종료되지 않을 수도 있습니다.
  4. 메시지 큐와 WM_QUIT

    • PostQuitMessage는 메시지 큐에 WM_QUIT 메시지를 넣고, 이 메시지는 애플리케이션의 메시지 루프에 의해 처리됩니다.
    • WM_QUIT 메시지를 받으면 메시지 루프가 종료되며, 이는 프로세스의 종료로 이어질 수 있지만, 애플리케이션의 전체적인 설계와 동작에 따라 달라집니다.
  5. 요약

    • PostQuitMessage는 윈도우가 파괴될 때 호출되어 WM_QUIT 메시지를 메시지 큐에 넣는 역할을 합니다. 이 메시지는 프로세스가 종료될 수 있도록 하는 신호이지만, 프로세스의 종료 여부는 애플리케이션의 설계에 따라 달라집니다.

PeekMessage 함수

  • peek : 엿보다.

    • 메세지 큐가 있는지 없는지 훔쳐 보는것.
  • GetMessage`의 문제

    • GetMessage는 메시지 큐에 메시지가 있을 때만 반환하고, 큐에서 메시지를 꺼내어 처리합니다.

    • WM_QUIT 메시지가 큐에 있을 경우 false를 반환하여 메시지 루프 종료를 알리며, 다른 메시지에 대해서는 true를 반환하여 루프가 계속 실행되도록 합니다.

  • PeekMessage로 문제 해결

    • 메세지가 있던 없던 리턴된다.
      • 메세지 있다면 true, 메세지 없다면 false
    • 큐안에서 제거를 안 할수 있다.
  • 메세지를 안꺼내올수도 있다. 확인을 하되 메세지 큐안에서 제거를 안할수 있다.

  • PeekMessage를 반복문에 조건으로 사용해버리면 프로그램이 바로 종료되어 버린다.

    • 한순간에라도 메세지 큐에 잠깐이라도 없는 순간이 있다면 프로그램을 꺼버리기 때문이다.

    • 1초에 수십만번을 돌고 앵간한 메세지는 순식간에 처리되고 없는 상태로 존나 빠르게 돈다.

	// 1
	while (true)
	{
    	// 2
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
        	// 4
			if (msg.message == WM_QUIT)
				break;
          	// 5 
			if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
        // 3
		else
		{
        	// Game Logic
		}
  • 1번처럼 PeekMessage기반으로 하면 while문 조건을 true로 변경한다.

  • 2번은 PeekMessage에 메세지가 있다는 의미다.

    • 메세지를 처리하게 해야한다.

    • 1~3번인자는 GetMessage인자랑 같다.

    • 마지막인자는 추가된다.

      • 메세지를 엿보기만하고 메세지큐에서 처리를 안해버리고 같은 메세지를 계속 처리하게 된다.

      • GetMessage대용으로 쓸때에는 훔쳐보더라도 메세지가 있었으면 큐에서 뺴내야 된다.PM_REMOVE

      • PM_REMOVE큐에서 제거하면서 메세지 구조체에 담아간다.

      • PM_NOREMOVE는 0이니까 디폴트로 0을 넣어버리면 메세지 큐를 처리하지않는다.

  • 3번은 메세지가 큐에 없을 때에는 게임 코드 실행

  • 4번은 꺼내온 메세지가 WM_QUIT 이면 프로그램 종료

    • 4번이 없다면 WM_QUIT이 들어와도 처리만하기때문에 WM_QUIT가 들어오면 반복문을 탈출하는 조건을 추가해줘야 한다.

      • 어플리케이션 종료하라는 메세지니까 처리할게 아니라 종료하자.
  • 5번은 꺼내온 메세지 처리한다.

강의 코드

main.cpp

#include "pch.h"

#include "framework.h"
#include "GameClient.h"

#include "Resource.h"

// 전역 변수:
HINSTANCE   hInst;                       // 현재 인스턴스입니다.
HWND        g_hWnd;                     // 메인 윈도우 핸들

wchar_t szTitle[100];                  // 제목 표시줄 텍스트입니다.
wchar_t szWindowClass[100];            // 기본 창 클래스 이름입니다.

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


// SAL : 주석 언어
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    // 윈도우 클래스 등록
    MyRegisterClass(hInstance);
        
    // 윈도우 생성
    hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
     
    // CreateWindow 윈도우 생성
    // 반환값은 윈도우 핸들(ID) 값,
    //  커널 오브젝트 : OS 가 관리하는 오브젝트, 직접적인 접근이 불가능하고 ID 값을 통해서 제어함
    g_hWnd = CreateWindowW(L"Window Class Key", L"My Game", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!g_hWnd)
        return FALSE;    

    // 윈도우를 화면에 보여줄지 말지 설정
    ShowWindow(g_hWnd, true);
    UpdateWindow(g_hWnd);
    
    // 리소스(단축키 테이블 핸들 얻기)
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GAMECLIENT));

    // 메세지 루프
    // 메세지 정보를 받을 구조체 변수
    MSG msg;    
    
    //{
    //    // 1000 이 1초 
    //    SetTimer(g_hWnd, 0, 50, nullptr);

    //    // GetMessage
    //    // 1. 리턴 조건 - 메세지 큐에서 메세지가 있으면 리턴
    //    // 2. WM_QUIT 이 발생하면 GetMessage 함수는 false 를 반환
    //    //    WM_QUIT 이외 메세지면 GetMessage 함수는 true 를 반환
    //    while (GetMessage(&msg, nullptr, 0, 0))
    //    {
    //        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    //        {
    //            TranslateMessage(&msg);
    //            DispatchMessage(&msg);
    //        }
    //    }

    //    KillTimer(g_hWnd, 0);
    //}
 
    {
        while (true)
        {
            // Peek 엿보다
            // 메세지가 있던 없던, 리턴된다.
            // 메세지가 있었으면 true, 메세지가 없었으면 false
            if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
            {
                // 꺼내온 메세지가 WM_QUIT 이면 프로그램 종료
                if (msg.message == WM_QUIT)
                    break;

                // 꺼내온 메세지 처리
                if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }

            else
            {
                // 메세지가 큐에 없을 때에는 게임 코드 실행
                                 
            }
        }
    }

    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_GAMECLIENT));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    //wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_GAMECLIENT);
    wcex.lpszClassName  = L"Window Class Key";
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}


// CallBack
// 함수의 주소를 알려줘서, 특정 상황(조건) 이 맞으면 알려준 함수가 호출되는 구조

// 각 윈도우들은 해당 윈도우에 메세지가 발생했을 때 처리를 해줄 프로시저함수를 등록해야한다.
// 메인 윈도우는 MyRegisterClass 함수 안에서 윈도우 정보를 만들때 호출함 함수의 주소를 등록해둠
// 도움말 윈도우는 다이얼로그 형태로서, DialogBox 함수를 호출할때 사용할 
// 프로시저 함수의 주소를 입력으로 넣어줬다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONDOWN:
        ShowWindow(g_hWnd, false);
        break;

    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 메뉴 선택을 구문 분석합니다:
            switch (wmId)
            {
            case IDM_ABOUT:
                // 윈도우 생성 함수
                // 
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            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_TIMER:
    {
        int a = 0;
    }
        break;


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

// 정보 대화 상자의 메시지 처리기입니다.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            //EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

1차 24.01.18
2차 24.01.19
3차 24.01.22
4차 24.01.23
5차 24.01.24

0개의 댓글