WinAPI 시작 (1)

Yama·2024년 1월 17일
0

어소트락 수업

목록 보기
47/55

svn을 통한 프로젝트 관리

  • 내 PC에 학원서버에 SVN폴더를 연결 설정을 해서 그날그날 작업을 업데이트해서 받아가는것이다.
    • 원래라면 여러 명이서 커밋하고 다른 사람이 작업한거 받아서 하고 협업할떄 사용.
  • VisualSVN , TortoiseSVN이 대표적이다.

솔루션이랑 프로젝트를 구별을 한 이유.

  • 일반적으로 간단한 프로젝트를 생성하면 하나의 솔루션 안에 하나의 프로젝트가 들어가다보니 이 두개의 이름을 보통은 일치 시킨다.

    • 비쥬얼 스튜디오에서도 직접적으로 안바꾸면 같은 이름으로 생성한다.
  • 하나의 솔루션안에는 프로젝트가 여러가지가 들어갈 수 있다.

    • 다수의 프로젝트가 하나의 솔루션안에 존재하는 구조이기때문에 솔루션명과 프로젝트명을 구분 했다.
  • WinAPI에서는 프로젝트를 여러개 안할것이지만 다렉x에서 부터는 프로젝트 여러가지 할것이다.

    • 솔루션안에 프로젝트인 GameClient가 빌드되서 최종적으로 게임이 될것이다.

원도우 애플리케이션으로 프로젝트만들면 기본적으로 틀 제공

  • WinAPI의 기본적인 프레임 워크와 구동방식을 알아볼것이다.
  • 필요한것들을 구현해 갈것이다.

WinAPI에서의 메인함수의 형태

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
  • WinAPI에서 시작하는 첫번째 메인함수가 호출되면서 게임이 시작할것이다.
  • 원도우 프로그램은 원도우로부터 받는 입력인자가 4개를 받기 위해서 저런 형태로 받는다.(암기 x)
  • 함수가 호출되면서 프로그램이 시작할것이고 원도우 운영체제로부터 중요한 값인 4개를 입력받고 시작할것이다.
    • c++강의에서는 메인 함수에서의 입력인자를 받지 않았다.
	// 1
	hInstance;
    //2
    hPrevInstance;
  • 1번과 2번은 특정 프로그램을 구별하는 id값으로 받을것이다.

  • 1번은 프로세스의 주소값.

    • 프로세스의 실행된 프로그램이 원도우에서 관리 되면서 메모리에 올라가 있는 프로그램 그 자체.
    • 1번을 통해서 프로세스의 각종 정보에 접근이 가능해진다.
  • 2번은 16비트 원도우 시절에 쓰던 인자다.

    • 원래의미 : 이전 프로세스 주소
  • 2번이 의미가 없어진 이유 는 아래.

윈도우 운영 체제의 진화: 메모리 관리와 프로세스 관리

초창기 윈도우의 메모리 관리

  • 초창기 윈도우 운영 체제에서 프로세스와 메모리 관리는 상대적으로 단순했습니다.

  • 프로그램이 실행될 때, 각 프로세스는 실제 물리적 메모리 영역에 직접 할당되었습니다.

    • 예를 들어, 사용자가 그림판 프로그램을 여러 번 실행하면, 각각의 인스턴스가 실제 메모리에 별도로 등록되고 구분되었습니다.

    • 각 프로세스가 고유한 hInstance 값을 가졌으며, 이는 프로세스의 인스턴스 핸들을 나타냈습니다.

    • hPrevInstance는 이전에 실행된 프로세스의 주소를 참조하는 데 사용되었습니다.

    • 하지만 이는 초기 윈도우 버전에서의 구조이며, 현대의 운영 체제에서는 hPrevInstance가 사용되지 않습니다.

현대 운영 체제의 메모리 관리

  • 현대의 윈도우 운영 체제는 가상 메모리 시스템을 도입하여 프로세스 관리와 메모리 사용의 효율성을 크게 향상시켰습니다.

  • 가상 메모리 시스템은 각 프로세스에게 독립된 주소 공간을 제공함으로써, 프로세스가 전체 메모리를 독점적으로 사용하는 것처럼 보이게 만듭니다.

  • 실제로는 운영 체제가 물리적 메모리 자원을 관리하고, 필요에 따라 메모리와 디스크 기반의 스왑 공간 사이에서 데이터를 이동시킵니다.

  • 현대 운영체제의 장점

    1. 현대의 응용 프로그램은 더 큰 메모리 영역을 활용할 수 있다.
    2. 멀티태스킹과 보안 요구 사항을 더 잘 지원할 수 있게 되었습니다.
    3. 모든 프로세스의 hInstance 주소가 동일하게 나타나며, 과거처럼 이전 프로세스의 주소를 참조할 필요가 없어졌습니다.

SAL 주석언어

// 1
_In_, 
// 2
_In_opt_
  • 아무런 영향을 미치지 못하고 함수 입력인자 앞에 붙여서 인자가 무슨 용도인지 간략하게 설명해주는 역할을 한다.

  • 2번 In_opt을 붙여서 입력으로 들어왓지만 전혀 쓰이지 않는다는 의미다.

  • 그래서 2번인자는 쓸모는 없지만 지울수는 없다.

    • why? 필요 없다고 입력인자를 3개로 튜닝 해버리면 옛날 프로그램은 현재 원도우에서 호환이 안될것이다.
      • 운영체제가 역사가 길어서 불필요한 잔재가 남아있다.
	// 3
    _In_ LPWSTR    lpCmdLine,
    // 4
    _In_ int       nCmdShow
  • 3번쨰 인자(lpCmdLine)는 프로그램 실행할떄 커맨드라인 명령어로 실행했을떄 던져주는 문자열들을 주소값(지금 필요 없다.)
 ShowWindow(hWnd, nCmdShow);
  • 4번쨰 인자(nCmdShow)는 ShowWindow함수의 true와 false값으로 보여줄지 여부를 판단(1이면 보여줌 0이면 안보여줌).
    • 메인함수 입력에서 받아올 필요가 굳이 없다.
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
  • 기본틀에서는 매크로로 이 파라미터는 전혀 사용하지 않는다고 걸어버리기까지 했다.
	hPrevInstance;

  • (P)를 받아서 (P)로 대체된다.
  • UNREFERENCED_PARAMETER매크로가 컴파일될떄 저렇게 코드를 바꿔버려서 인자를 쓸모 없게 만들어 버려서 컴파일할떄 아무 의미가 없어지게 만든다.

결론

  • 지금으로서는 WinAPI시작할떄 들어오는 메인함수인자 4개는 필요가 없다.

WINAPI 기본 함수 전방 선언

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
  • 전방 선언 시켜둠 아래에 구현해둠.

전역 문자열을 초기화합니다.

  • 원도우 데스크톱 애플리케이션으로 프로젝트 만들어서 그냥 실행을하면 기본적으로 이 사진처럼 만들어 준다.
	// 1
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    // 2
    LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
    // 3
    MyRegisterClass(hInstance);

  • 1번에서 중단점 걸고 hInstance를 확인하면 이런 프로세스 하나가 생긴걸 확인 가능.
    • 1번 함수는 hInstance를 요구해서 주소를 가보면 우리 프로세스 정보가 들어있고 정보 안에는 각종 리소스들이 로드가 되어있다.
    • 리소스 정보들은 리소스 뷰에서 확인 가능하다.
    • 리소스는 프로세스와 같이 메모리에 올라간다.
    • 리소스 뷰안에 String Table이 있다.
	LoadStringW(hInstance, 103, szTitle, MAX_LOADSTRING);
  • IDS_APP_TITLE 매크로이므로 컴파일할떄 103으로 바뀐다.

    • 저 타이블에 연결된게 GameClient라는 문자열이다.
  • 프로그램이 켜지면 문자열 정보도 프로세스안에 로드가 된다.

  • 1번 함수는 그래서 기능은 hInstance(프로세스 주소)알려주면 리소스로 접근해서 같이 메모리 로딩된 문자열 테이블에서 저 IDS_APP_TITLE에 해당하는 문자열을 지정된 배열에 옮겨주겠다는 것이다.

#define MAX_LOADSTRING 100

WCHAR szTitle[MAX_LOADSTRING];                  // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING];            // 기본 창 클래스 이름입니다.
  • 어차피 MAX_LOADSTRING으로 100으로 재정의 되어 있으니 걍 100으로 적자.

  • WCHAR은 wchar_t로 재정의 해둔것이다.
  • 1번 함수의 szTitle, MAX_LOADSTRING쪽 부분은 szTitle의 크기가 문자열을 가져왓는데 더 크면 예외처리나 그런걸 하기위해서 인자로 받는다.
    • 리소스 뷰에 MY_STRING를 추가해서 104로 값을 주고 문자열을 abcdefg를 적어준 다음
 	LoadStringW(hInstance, , MY_STRING, szTitle, 100);
  • 이걸로 LoadStringW함수를 호출하면
    • 원래 이렇게 출력되던게
    • MY_STRING에 등록한 문자열로 출력이 된다.
  • 전처리기 매크로를 사용한 이유는 104 딱 적어주는 것보다 MY_STRING을 적어서 매크로 구분을 사용하면 직관적이기 떄문에 사용했다.

LoadStringW 행설수설

프로젝트 명을 앱 타이블에 적고 해당 배열로 그 아이디로 부터 문자열 정보를 스트링 테이블에 프로세스랑 같이 올라갈거니까 거기에서 이쪽 배열로 전역변수에 적어두고 그 문자열을 hInstance 안에서 전달 인자로 저걸 넣어주겠다는것. 결과적으로 타이틀창이 프로젝트 명이랑 동일한 형태로 잡아준것이다.

  • 이렇게 빙빙 돌것없이 사실은 그냥 내가 원하는 원도우 창이름을 적었으면 됐다.
  • 리소스 뷰도 설명하고 싶고 구조들 리소스들이 같이 찾아내서 읽어올수 있고 그냥 보여줄려고 이런 쓸대 없는걸 다 기본틀에서 적어둔것이다.

MyRegisterClass()함수

	MyRegisterClass(hInstance);
  • 윈도우 클래스 등록
  • 함수 호출.
//  함수: MyRegisterClass()
//
//  용도: 창 클래스를 등록합니다.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	// 1
    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_WINDOWSPROJECT1));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    // 4
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
    // 5
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	// 2
    return RegisterClassExW(&wcex);
}
  • MyRegisterClasst구현부
    • 1번에 WNDCLASSEXW의 f12눌러서 들어간 사진.
    • 구조체의 맴버들을 알려주고 있다.
    • 2번에 RegisterClassExW는 원도우에서 만든거라 볼수 없다.
      • 1번 2번처럼 원도우에서도 상당히 많은 부분을 이미 구현되어잇는 라이브러리 함수들을 가져와서 쓸 것이다.
      • 회사차원에서 만들어든 기능이 있으면 그걸 써라.
    • MyRegisterClass는 내부에 있지만 내부적으로는 2번처럼 원도우쪽 함수를 호출하고 있다.
	// 3
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
  • 원도우를 생성한다.
    • 그림판을 생성했다면 입력장치(키보드,마우스)를 이용해서 그림판이랑 상호작용 가능하다.
    • 유저와 프로그램간의 인터페이스 중간 연결(마주하는 접점)
      • 프로그램이란거는 컴퓨터 하드웨어 내부적으로 메모리 덩어리인데 값을 넣고 싶다면 그렇다면 cpu를 뜯어 고쳐서 메모리를 넣는게 아니라 저 프로그램 내부적인 데이터를 os를 통해서 출력장치인 모니터를 통해서 메모리 상의 데이터를 시각적으로 보여주고 입력장치를 통해서 조작을 해서 메모리가 변경이 됐기 때문에 이런 그림판에서 변경점들이 보일것이다.
      • 픽셀 데이터에 값을 써버린것이다.(창이 소통의 장)
  • 창하나를 만들떄 프로세스 == 원도우 라고 착각 하면 안된다.
    • 내가 만든 프로그램을 실행시키면 하나의 프로세스가 os가 관리하면서 실행이 될것이다.
    • 이 프로그램을 여러가지 실행시킬수도 있는것이공.
    • 원도우는 인터페이스이기때문에 특정 프로세스는 원도우를 가질 수도 있고 안 가질 수도 있다.(백그라운드 메모리)
  • 원도우 클래스에서 등록한 정보들 토대로 InitInstance가 원도우 생성한다.
	// 4
	wcex.lpszMenuName   = nullptr

  • MyRegisterClass함수의 구현부분에 4번을 nullptr로 막아버리면 메뉴바를 막아버린다.
	// 5
	wcex.lpszClassName  = L"Window Class Key";
  • 키값을 수정.
	// 1
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
   // 2
   HWND hWnd = CreateWindowW(L"Window Class Key", szTitle, WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);   
  • CreateWindow 윈도우 생성

    • 반환값은 윈도우 핸들(ID) 값
  • HWND타입은 구조체 포인터 타입이고 끝나고 값은 주소값이 들어온다.(id값)

  • 1번에 szWindowClass를 2번에 L"Window Class Key"로 수정함.

  • 클래스 네임을 문자열을 키값으로 구조체에 등록해서 hInstance가 키값을 탐색을 해서 원도우 창을 만든다.

  • 작동순서

    1. 구조체 함수 호출
    2. 함수 선언부분으로 가서 구조체 맴버들의 값들로 초기화 해준다.
      hInst = hInstance;
    1. 초기화할때 키값들로 프로세스를 생성한다.
    2. 현재 인스터스 프로세스의 구조체 맴버들의 정보들을 토대로 원도우를 생성한다.
    if (!InitInstance (hInstance, nCmdShow)) // 인스턴스 핸들을 전역 변수에 저장합니다.
    {
        return FALSE;
    }
  • 프로세스를 만들떄 안에서 잘못된 상황이 발생하면 핸들값을 받아서 프로그램이 종료되던 코드다.

커널 오브젝트

  • OS 가 관리하는 오브젝트, 직접적인 접근이 불가능하고 ID 값을 통해서 제어함

  • 운영체제의 특징 운영체제들은 핵심 기능을 순수하게 메모리값을 공개하지 않는다.

  1. 운영 체제에서의 커널 오브젝트: 커널 오브젝트는 OS의 필수적인 구성 요소로, 시스템 자원에 대한 인터페이스 역할을 합니다. 시스템 자원과 프로세스를 관리하는 데 중요한 역할을 합니다.

  2. 윈도우 OS에서의 멀티태스킹: 윈도우에서 동시에 여러 프로그램을 실행할 수 있는 것이 운영 체제에서 중요한 발전이었다는 것을 언급하고 있습니다.

  3. 운영 체제가 순수한 메모리 값을 공개하지 않는 것

    • 운영 체제가 메모리 관리를 위해 제공하는 추상화 계층을 의미하며, 보안과 안정성을 유지하는 데 도움이 됩니다.
    • 메모리 자체를 다이렉트로 알려주지않는다 운영체제에서 핵심기능이기 떄문에 메모리를 숨기고 id(핸들)값을 알려준다.
  4. 객체를 사용한 윈도우 생성

    • 이는 윈도우 OS에서 그래픽 사용자 인터페이스 요소들, 즉 윈도우들이 어떻게 생성되고 관리되는지를 나타냅니다.
  5. 윈도우에서 핸들의 개념

    • 핸들은 윈도우가 시스템 오브젝트들을 참조하기 위해 사용하는 식별자들입니다. 여기에는 윈도우, 파일, 프로세스 등이 포함됩니다.
  6. 인터페이스를 통한 커널 오브젝트 핸들의 조작

    • 커널 오브젝트가 특정 인터페이스(함수)를 사용하여 핸들 값으로 조작된다는 것을 설명합니다.
  7. 커널 오브젝트 핸들의 종류: 윈도우, 펜, 브러시 등이 예시로 들 수 있습니다.

  8. 백그라운드 프로세스와 멀티태스킹

    • 이는 OS가 동시에 여러 프로세스 및 애플리케이션을 실행할 수 있는 능력을 나타냅니다.
  9. 프로세스 및 윈도우 관리: 하나의 프로세스가 여러 윈도우를 가질 수 있고, 동일한 ID를 공유하더라도 커널 오브젝트를 식별하고 구별할 수 있다는 개념입니다.

    struct WND
    {
        int id;
    };

    struct BRUSH
    {
        int id;
    };
  • 변수명이 같은 id여도 구조체안에 있기 때문에 구분할수 있다.
  • 핸들(id)하나를 정수로 담고 있는것인데 전혀 다른 구조체를 가지고 있어서 같은 id(정수)를 가져도 구분할수 있다.
    • 커널 오브젝트의 그룹군들이 달라서 구조체로 가서 그 구조체의 id별로 간다.!
	// 1
   ShowWindow(hWnd, true);//false);
   // 2
   UpdateWindow(hWnd);
  • 1번에 true하면 화면에 보여주고 false하면 안보여준다.
    • false일떄 화면이 안보이는것이지 프로세스는 돌아가고 있다.
  • 2번은 원도우 업데이트 해주는 용도로 지금은 딱히 필요가 없다.

아까 메인함수의 인자 4개중에 1번뺴고는 다 쓸모 없다.

메세지 루프 간단하게

    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
        	// 분석
            TranslateMessage(&msg);
            // 처리
            DispatchMessage(&msg);
        }
    }
  1. 반응형 프로그램

    • 윈도우 기반의 프로그램은 일반적으로 반응형입니다. 즉, 특정 이벤트나 사용자의 입력과 같은 특정한 상황에만 반응합니다. 아무런 이벤트가 발생하지 않으면 프로그램은 아무런 동작도 하지 않습니다.
  2. 메시지 큐의 생성

    • 반응이 일어나면, 즉 사용자의 입력이나 시스템 이벤트가 발생하면 메시지 큐가 생성됩니다. 모든 프로세스는 생성되고 난 후 자신의 메시지 큐를 갖게 됩니다.
  3. 메시지 처리

    • 메시지 큐에 이벤트가 발생하면, 프로세스는 GetMessage 함수를 사용하여 윈도우 메시지가 들어왔는지 확인합니다. 이 함수는 메시지 큐에서 메시지를 검색하고, 메시지가 있을 경우 해당 메시지를 처리하기 위한 정보를 반환합니다.
  4. 무한 루프

    • 프로그램은 종료되지 않고 무한 루프 상태로 실행됩니다. 이 루프는 메시지 큐를 지속적으로 확인하면서 새로운 이벤트나 메시지에 반응합니다.
  5. 메시지 분석 및 처리

    • 메시지 큐에 반응이 일어나면, GetMessage를 통해 메시지가 들어왔는지 확인한 후 메시지 큐가 발생합니다. 그 후에는 이 메시지를 분석하고 적절히 처리합니다.
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
       switch (message)
      {
      case WM_LBUTTONDOWN:
          ShowWindow(g_hWnd, false);
          break;
       }
       return 0;
    }
  • WndProc 함수가 메세지 처리함수이다.
  • WM_LBUTTONDOWN 왼쪽키가 눌렸다면 화면 끄는 매크로
  • 반응형은 게임이랑 맞는 방식은 아니다.

강의 코드

main.cpp

// GameClient.cpp : 애플리케이션에 대한 진입점을 정의합니다.
//

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

#include "Resource.h"

#define MAX_LOADSTRING 100

// 전역 변수:
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)
{
    hInstance;      // 프로세스 주소
    hPrevInstance;  // 이전 프로세스 주소

    // 전역 문자열을 초기화합니다.
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, 100);
    LoadStringW(hInstance, IDC_GAMECLIENT, szWindowClass, 100);

    // 윈도우 클래스 등록
    MyRegisterClass(hInstance);
        
    // 윈도우 생성
    hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
        
    //HWND;
    //HWND__*;
    //struct HWND__
    //{
    //    int unused;
    //}; typedef struct HWND__ *HWND

    // 각종 커널오브젝트 타입에 맞는 핸들ID 타입 자료형들
    HPEN;
    HWND;
    HBRUSH;
    HDC;
    HBITMAP;

    // 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;

    // 기본 메시지 루프입니다:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    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);
}


// 메세지 처리함수
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_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.17
2차 24.01.18
3차 24.01.19
4차 24.01.22
5차 24.01.23

0개의 댓글