독고솜의 인생일지

Win API - 기본 코드 설명 본문

프로그래밍 언어/Win APi

Win API - 기본 코드 설명

독고솜 2022. 10. 9. 22:08

본 게시글은 C++17을 기준으로 작성하였으며, https://google.github.io/styleguide/cppguide.html 의 Google의 코딩 스타일을 준수합니다.

 

수정및 완본 예정일(2022-12-01) 예전부터, 지금까지, 앞으로의 배울 내용을 이쁘게, 정리하면서 나를 위해 쓰고 다른 누군가가 이 글을 보고 행복을 떠오르기를 빕니다.

글을 한번에 갑작스럽게 올라오거나 멈추거나... 비가 오는 것처럼 내려옵니다.

 

검수안했습니다. 검수안했습니다 

뇌가 하라는 대로 썼습니다.

 

 

 

세상은 무언가를 잘하는 것을
재능이라고 한다.

내가 보기에는 무언가를 열심히 하는 것 또한
재능이라고 생각한다.

-With You-

 

 

 

 

 

Win API란?

 

Win API란 모든 windows에서 실행되는 애플리케이션을 개발하는 용도로 제공해주는 소스코드를 API라고 부릅니다.

이전에는 Win32 라고 불렀지만 요즘에는 API라고 부릅니다. (64비트 시대이기 때문에)

즉 . API란 애플리케이션을 만들기 위한 개발자와 프로그램 사이의 접점이라고도 표현할 수 있습니다. MFC는 망했기 때문에 설명하지 않겠습니다.

 

 

tipsware

 

자세히 보면 커널도 있고 그런데 그런건 모르셔도 됩니다.

 

 

 

Source code 분석

새 프로젝트 -> Windows 애플리케이션 마법사

기본코드에 살짝 주석을 더 달았습니다.

 

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

#include "framework.h"
#include "Client.h"

#define MAX_LOADSTRING 100

// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
WCHAR szTitle[MAX_LOADSTRING];                  // 제목 표시줄 텍스트입니다. 유니코드 문자열 배경
WCHAR szWindowClass[MAX_LOADSTRING];            // 기본 창 클래스 이름입니다.


// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM); // 내가 그 함수 쓸거다 다른 놈 ㄴㄴ 함수 포인터도 있음. 해당당 윈도우가 만들어짐녀 이 윈도우에 발생하는 메시지를 처리해줄 함수의 주소까지 나옴.
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

//main 함수 앞에 _in_ 이상한거 있음. -> 지역번수 앞에 용도적음 SAL. 굳이 주석을 달아서 적지 말고 함축되게 키워드
//in > 데이터 입력
// in opt 부가적인 데이터. (딱히 필요 없)
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, //프로세스 시작될때 메모리 주소. 메모리 할당받은 그 시작주소 
                     _In_opt_ HINSTANCE hPrevInstance,  //exe 계속 실행시켜, 초창기 = 여러개할때 이전 프로세스 주소, 지금 = 같은 값 나옴?? 각각 여러분 생겨나면 서로 다른 별개의 주소에 담아야 하는데.. 윈도우가 가상 메모리 시스템을 쓰기 때문에
                     _In_ LPWSTR    lpCmdLine, // 윈도우에서는 잘 안씀. 옵션 굳이? 그냥 가능함 주소 포인터 w_char*
                     _In_ int       nCmdShow) // 
{

    //hPrecv 실제는 각각이지만 가상으로 잡아줌 다 똑같아 보이게. 실제 주소를 알 순 없음. 500번지에 올라갔어요~ 다음 놈도 500번지
    UNREFERENCED_PARAMETER(hPrevInstance); // 이거 그냥 안쓰임 알려줌 컴파일러가 무시함.
    UNREFERENCED_PARAMETER(lpCmdLine);// 이거 그냥 안쓰임 이거 알려줌 컴파일러가 무시함.

    // TODO: 여기에 코드를 입력합니다.

    // 전역 문자열을 초기화합니다. 리소스 뷰(자원목록)에 string table이라고 있는데 내거 지어준 프로그램 이름을 테이블에 지정시키고 ID줌 
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); // loadstring  TITLE 창 이름
    LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING); //103, 109에 해당 번호를 넣어라. 
   //szWindowsClass에 문자열을 넣음
    
    MyRegisterClass(hInstance); // 윈도우 정보 등록 *1

    // 애플리케이션 초기화를 수행합니다:
    //창이라는 윈도우 생성
    // 2* 윈도우 만들고
    if (!InitInstance (hInstance, nCmdShow)) //아까 등록해논 정보 키값으로 찾기
    {
        return FALSE;
    }

    //단축키 정보 로딩
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT)); 

    MSG msg;

    // 기본 메시지 루프입니다: 
    //무한 반복분 돌고 있음.
    //큐 스택 구분. 큐 먼저 넣은 데이터 먼저 나옴. 현재 포커깅 중인 프로그램이 있을거임. message 처리함 넣어주면 걔가 맨 위놈이
    //그림판 클릭 메시지가 발생하면 수행함. 마우스 클릭이 발생함녀 메시지 q에 들어오고 꺼내서 칠함.
    //포커이 되어있지 않으면 뒤에꼐 발생 그게 vs가 가져가고 그림판이 가져가고 이렇게 클릭된 포커싱이

    //get massge : 메세지큐에서 메시지 확인 될 까지 대기
    //언제 true ? 언제 false? false를 반환하면 whiel문이 실패하기 떄문에 종료됨.. 즉 프로그램의 종료.
    //getMessage가 언제 false 
    // true false의 의미는 메세지가 어떤거에 따라 나눠짐
    // 확인해본 msg == WM_QUIT 이면 false 발생 -> 프로그램 종료
    // 이 프로그램 파괴되기 전에 소유하고 있던 윈도우부터 해지가 되고 윈도우가 종료되야함.
    //모든 작업이 완료됬다는 뜻. 윈도우 들 부터 먼저 해지하는 메시지 발생했을 거임 그리고 이게 나온거임.
    //msg가 존재하지 않으면 동작하지 않음. 반응형 형태로 구성됨
    // 게임 클라로 사용하기엔 부적합 왜? 나중에 알려줌~ ㅋㅋㄹㅃㅂ 앞으로 게임 적합한 형태로 바꿈
    while (GetMessage(&msg, nullptr, 0, 0))  // 해당 프로그램에 발생한 메시지 메시지 Q에 담아놨는데 꺼내놈. while문 계속 돌면서 꺼냄. 쏙~ 
    { //msg 주소에 넘김 발생한 메시지 정보를 저기에 채울려고. 메시지 발생한 종류가 뭔지 
        //msg.time; msg.message, ms.pt; 이렇게 
        //get이 메시지 꺼내줘서 msg에 저장해줌
        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_CLIENT));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_CLIENT); // nullptr로 생성하면 
    wcex.lpszClassName  = szWindowClass; //lpszClassname의 키 값은 sz
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

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

//등록시킨 정보 찾아옴.
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, // sz라는 키값으로 검색
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 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_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를 사용하는 그리기 코드를 추가합니다...


            //윈도우 핸들
            //윈도우 좌표 창 하나가 있을떄 작업 영역이라는게 있음. 타이플 + 메뉴 아래부터 시작하는게 작업영억 
            //0,0은 맨 위 좌상단 1단위. 1씩 pixel 단위 픽셀 하나하나는 메모리 4000 개가 모여 구성 하면 출력함
            // 전부타 0,0에서 시작함 거기에 얼마나 떨어질지 그렇개ㅔ
            //hdc ?
            Rectangle(hdc,10,10,100,100);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam); // default windowsproc에서 처리 윈도우 제공 한거
    }
    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;
}

 

 

framwork.h와 client.h파일을 전처리합니다.

#include "framework.h"
#include "Client.h"

 

framwork.h에는

// header.h: 표준 시스템 포함 파일
// 또는 프로젝트 특정 포함 파일이 들어 있는 포함 파일입니다.
//

#pragma once

#include "targetver.h"
#define WIN32_LEAN_AND_MEAN             // 거의 사용되지 않는 내용을 Windows 헤더에서 제외합니다.
// Windows 헤더 파일
#include <windows.h>
// C 런타임 헤더 파일입니다.
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

해당 코드가 들어있는데 쉽게 설명하면 전처리들을 한곳에 모아놨다고 설명하면 쉽습니다.

API 코드들도.

 

client.h는 프로젝트 이름을 clinet라고 지어서 생겼습니다.

 

 

#define MAX_LOADSTRING 100

 

는 MAX_LOADSTRING라는 곳에 숫자 100을 저장

read(stdin,buf,크키)

크기에 MAX_LOADSTRING을 적으면 100으로 인식.

 

 

 


 

HINSTANCE, WCHAR이라는 자료형으로 변수를 선언합니다

// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
WCHAR szTitle[MAX_LOADSTRING];                  // 제목 표시줄 텍스트입니다. 유니코드 문자열 배경
WCHAR szWindowClass[MAX_LOADSTRING];            // 기본 창 클래스 이름입니다.

 

 

HINSTANCE hInst;

#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name

이렇게 구조체로 지정되어 있는데 좀 봐보면.

int unsed 자료형이네요. 즉 정수 변수 하나를 저장하네요

 

 

WCHAR는 

typedef wchar_t WCHAR;    // wc,   16-bit UNICODE character

wchar_t라는 함수를 WCHAR로 그냥 바꿔준겁니다

 

밑에꺼도 똑같겠죠?

즉 

WCHAR szTitle[MAX_LOADSTRING];    는 아까 선언한 define구문에서 100만큼의 크기의 szTitle변수를 wchar_t로 선언했네요

 

밑에도 동일하겠죠?

 

 


ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM); // 내가 그 함수 쓸거다 다른 놈 ㄴㄴ 함수 포인터도 있음. 해당당 윈도우가 만들어짐녀 이 윈도우에 발생하는 메시지를 처리해줄 함수의 주소까지 나옴.
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

ATOM도

typedef WORD                ATOM;   //BUGBUG - might want to remove this from minwin

WORD형식으로 선언했고 WORD란?

 DWORD unsigned long  4 byte 
 bool  char  1 byte 
 BOOL  int  4 byte 
 BYTE  unsigned char  1 byte 
 WORD unsigned short  2 byte 
 UINT  unsigned int  4 byte 

16bit의 크기로 CPU가 처리할 수 있는 하나의 단위로, CPU가 한번에 지나갈 수 있는 데이터 단위를 word 입니다

 

 

LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM); 

4개의 매개 변수가 있는데 

 

hwnd 는 창에 대한 핸들입니다.
uMsg 는 메시지 코드입니다. 예를 들어 WM_SIZE 메시지는 창의 크기가 조정되었음을 나타냅니다.
wParam 및 lParam 에는 메시지와 관련된 추가 데이터가 포함되어 있습니다. 정확한 의미는 메시지 코드에 따라 달라집니다.

 

입니다.

 

이 부분은 함수를 설명하면서 더 자세히...

 


 

wWinMain으로 진입점 함수입니다. 즉 int main이죠

int APIENTRY wWinMain(_In_ HINSTANCE hInstance, //프로세스 시작될때 메모리 주소. 메모리 할당받은 그 시작주소 
                     _In_opt_ HINSTANCE hPrevInstance,  //exe 계속 실행시켜, 초창기 = 여러개할때 이전 프로세스 주소, 지금 = 같은 값 나옴?? 각각 여러분 생겨나면 서로 다른 별개의 주소에 담아야 하는데.. 윈도우가 가상 메모리 시스템을 쓰기 때문에
                     _In_ LPWSTR    lpCmdLine, // 윈도우에서는 잘 안씀. 옵션 굳이? 그냥 가능함 주소 포인터 w_char*
                     _In_ int       nCmdShow) // 
{

    //hPrecv 실제는 각각이지만 가상으로 잡아줌 다 똑같아 보이게. 실제 주소를 알 순 없음. 500번지에 올라갔어요~ 다음 놈도 500번지
    UNREFERENCED_PARAMETER(hPrevInstance); // 이거 그냥 안쓰임 알려줌 컴파일러가 무시함.
    UNREFERENCED_PARAMETER(lpCmdLine);// 이거 그냥 안쓰임 이거 알려줌 컴파일러가 무시함.

네 개의 매개변수가 있는데.

먼저 __in__, __in_opt__ 는 그냥 이 주석으로 in은 데이터 입력 이렇게 보면 됩니다. 굳이 주석적기말라고 마소에서 만들었는데 굳이..

 

  • hInstance 는 "인스턴스에 대한 핸들" 또는 "모듈에 대한 핸들"이라고 합니다. 운영 체제는 메모리에 로드될 때 실행 파일(EXE)을 식별하기 위해 이 값을 사용합니다. 인스턴스 핸들은 특정 Windows 기능(예: 아이콘 또는 비트맵 로드)에 필요합니다.
  • hPrevInstance 는 의미가 없습니다. 16비트 Windows에서 사용되었지만 지금은 항상 0입니다.
  • pCmdLine 은 명령줄 인수를 유니코드 문자열로 포함합니다.
  • nCmdShow 는 기본 응용 프로그램 창이 최소화, 최대화 또는 정상적으로 표시되는지 여부를 나타내는 플래그입니다.

1. 프로세스 시작 메모리 위치, 즉 윈도우즈 운영체제에서 실행되는 프로그램들을 구별하기 위한 ID값 입니다.

2. 의미가 없다는데 좀 알아가보면 지금은 가상메모리 때문에 똑같은 프로그램을 10000000개를 열어도 메모리의 시작 주소는 똑같아 보입니다. 예전에는 물리 메모리로 어찌고 했는데 지금은 가상 메모리로 인해 시작 메모리가 다 똑같이 보입니다.

3. 명령줄 인수로 우리가 cmd에서 프로그램 명령어 쓰거나 그런거 비슷합니다

4. nCmdShow는 윈도우가 처음 실행될 때 어떻게 보여졌으면 하는 건데 이게 프로그램 속성에 들어가보면 

RUN -> Normal windows, Minimized, max~~~ 이런게 있는데 그거입니다.

int값으로 반환하네요 해당 함수는

 

 

밑에 있는 함수는 함수의 뜻대로 입니다.

    UNREFERENCED_PARAMETER(hPrevInstance); // 이거 그냥 안쓰임 알려줌 컴파일러가 무시함.
    UNREFERENCED_PARAMETER(lpCmdLine);// 이거 그냥 안쓰임 이거 알려줌 컴파일러가 무시함.

#define UNREFERENCED_PARAMETER(P)          (P)

라고 선언되어있는데 그냥 이 함수를 그대로 반환시켜줍니다.

요즘에는 안쓰이니까 이렇게 알려주고 컴파일러는 이를 최적화때문에 무시합니다.

 

 


 

 

 

 

LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); // loadstring  TITLE 창 이름
    LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING); //103, 109에 해당 번호를 넣어라. 
   //szWindowsClass에 문자열을 넣음

LoadStringW 의 함수 구조는

LoadStringW(
    _In_opt_ HINSTANCE hInstance,
    _In_ UINT uID,
    _Out_writes_to_(cchBufferMax,return + 1) LPWSTR lpBuffer,
    _In_ int cchBufferMax
    );
    
    
    
 해석
 
 int LoadStringW(
  [in, optional] HINSTANCE hInstance,
  [in]           UINT      uID,
  [out]          LPWSTR    lpBuffer,
  [in]           int       cchBufferMax
);

먼저 LoadStringW 와 ''A 버전이 있는데 UNICODE이므로 W버전을 사용하고  

 

▶hInstance : 문자열 리소스를 가진 인스턴스 핸들

▶uID : 문자열 리소스의 ID

lpBuffer : 문자열을 읽을 버퍼

▶nBufferMax : 버퍼의 크기. 

 

라고 제공하고 있는데.

 

 

즉 

hInstance 의 위치에.. (hInstance  는 실행되고 있는 프로그램 위치)

 

uID  위치에  IDS_APP_TITLE 가 있는데 

 

 

리소스 뷰 > string table을 보면 Client 라는 단어가 보입니다.

 

그러면 hInstance  위치에 Client라는 문자열을!

lpBuffer 즉 szTitle에 넣었네요 

 

크기는? 빵빵하게 MAX_LOADSTRING 정도

즉 이제 szTitle 라는 곳에 Client라는 문자열이 들어갔네요.

 

 

참고. 윈도우즈에서는 문자열도 리소스의 일종으로 취급합니다. 변수에 문자열 저장하면? 나중에 큰일납니다...

 

 

첫 번째 인수는 문자열 리소스를 가진 인스턴스 핸들인데,g_hInst = hInstance 라는 구문이 왜 존재하는지 알려준다.

두 번째 인수는 읽어올 문자열의 ID를 전달하는 역할을 한다.

세 번째 인수는 전달받은 문자열의 ID에서 문자열을 읽을 버퍼를 지정한다.

네 번째 인수는 버퍼의 길이인데, 버퍼의 길이가 필요한 이유는 여백이 남지 않아 생략하겠습니다.

ppodo-programming-world

 

'API' 카테고리의 글 목록

 

ppodo-programming-world.tistory.com

 

 

LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING); //103, 109에 해당 번호를 넣어라. 

이 함수도 똑같겠죠?

 

 


 

MyRegisterClass(hInstance); // 윈도우 정보 등록 *1

 

//
//  함수: 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_CLIENT));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_CLIENT); // nullptr로 생성하면 
    wcex.lpszClassName  = szWindowClass; //lpszClassname의 키 값은 sz
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

 

함수인데요 매우 복잡하죠?

하지만 우리는 킹.갓 C++을 배운사람으로써 쉽게 볼 수 있습니다.

 

윈도우 클래스를 등록하는데요.....

win32의 가장 기본이 되는 작업입니다.

 

 

먼저 hinstance로 누구인지 받고.

 

wcex라는 구조체를 볼 수 있는데 여러가지 값을 한번에 구조체로 받을려고 한거같습니다.

 

 

    wcex.style          = CS_HREDRAW | CS_VREDRAW;

는 내가 윈도우 창을 줄이든 늘리든 반응형으로 그리라는 뜻으로 받아들이시면 될거같습니다.

세로 가로축 크기 변경이 새로 그림.

 

    wcex.lpfnWndProc    = WndProc

WinProc는 안에서 발생하는 메시지에 대한 처리를 하는 곳인데 밑에서 더 자세히 보여드리겠습니다

즈기 Winproc에서 메시지에 대한 처리를 하고 WinMain에서 윈도우 창을 만드는 일을 하는 겁니다.

 

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

메모리를 공유하고 개별 메모리 설정하는 건데 0으로 보통 설정합니다.

 

    wcex.hInstance      = hInstance;

윈도우즈 운영체제가 실행된 응용 프로그램을 구별하기 위한 것.


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

좌측 상단에 표시되는 로고 이미지
해당 거기에 IDI_CLIENT 

 

이러한 로고

 

hCursor

커서를 가져오는 건데 기본 커서를 사용하네요.

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=eldkrpdla121&logNo=220452315327 

 

[5장] 커서 다루기-WM_SETCURSOR메시지 HCUSOR핸들

이번에는 커서를 다뤄보자. 커서를 불러오는 함수는 CWinApp클래스의 매쏘드인 LoadStandardCursor함...

blog.naver.com

 

 

hbrBackground

배경을 그릴 떄 사용할 무슨 브러시인지 그런거..?

 

wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_CLIENT); 

좌측 상단에 나타나는 이름입니다. 

 

wcex.lpszClassName  = szWindowClass; //lpszClassname의 키 값은 sz

해당 창의 클래스 이름

최대 256길이.

 

 

 

이게 왜 필요한데??

https://stackoverflow.com/questions/54973029/why-the-myregisterclass-function-is-necessary-in-win32-projects

 

Why the MyRegisterClass() function is necessary in WIN32 projects?

In the default code that Visual Studio generates when creating a Win32 project, there's a function called MyRegisterClass with the comment: // This function and its usage are only necessary if ...

stackoverflow.com

 

cbSize 바이트 단위의 크기WNDCLASSEX
style 창의 스타일
lpfnWndProc 메시지 수신기로 사용할 WNDPROC 콜백에 대한 포인터
cbClsExtra 추가 플래그 값 세트
cbWndExtra 추가 플래그 값 세트
hInstance 현재 애플리케이션 인스턴스에 대한 핸들
hIcon 아이콘에 대한 핸들
hCursor 커서에 대한 핸들
hbrBackground 브러시 색상에 대한 핸들
lpszMenuName 메뉴 이름
lpszClassName 이 창의 클래스 이름

 

 

 

RegisterClassExW(&wcex);

 

윈도우 클래스를 등록하는 코드입니다.

WINCLASSEXW 구조체 변수 wcex에 값을 세팅하고 RegisterClassExW() 함수를 통해 윈도우 클래스를 등록합니다.

wcex 구조체 변수에 값을 셋팅할 때 안 쓰는 값이라도 0으로 초기화를 해주어야 합니다. 그렇지 않으면 쓰레기 값으로 셋팅이 되어 문제가 발생하게 됩니다.

이렇게 등록된 윈도우 클래스를 통해 윈도우를 생성하게 됩니다.

 


 

if (!InitInstance (hInstance, nCmdShow)) //아까 등록해논 정보 키값으로 찾기
    {
        return FALSE;
    }

    //단축키 정보 로딩
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT)); 

    MSG msg;

    // 기본 메시지 루프입니다: 
    //무한 반복분 돌고 있음.
    //큐 스택 구분. 큐 먼저 넣은 데이터 먼저 나옴. 현재 포커깅 중인 프로그램이 있을거임. message 처리함 넣어주면 걔가 맨 위놈이
    //그림판 클릭 메시지가 발생하면 수행함. 마우스 클릭이 발생함녀 메시지 q에 들어오고 꺼내서 칠함.
    //포커이 되어있지 않으면 뒤에꼐 발생 그게 vs가 가져가고 그림판이 가져가고 이렇게 클릭된 포커싱이

    //get massge : 메세지큐에서 메시지 확인 될 까지 대기
    //언제 true ? 언제 false? false를 반환하면 whiel문이 실패하기 떄문에 종료됨.. 즉 프로그램의 종료.
    //getMessage가 언제 false 
    // true false의 의미는 메세지가 어떤거에 따라 나눠짐
    // 확인해본 msg == WM_QUIT 이면 false 발생 -> 프로그램 종료
    // 이 프로그램 파괴되기 전에 소유하고 있던 윈도우부터 해지가 되고 윈도우가 종료되야함.
    //모든 작업이 완료됬다는 뜻. 윈도우 들 부터 먼저 해지하는 메시지 발생했을 거임 그리고 이게 나온거임.
    //msg가 존재하지 않으면 동작하지 않음. 반응형 형태로 구성됨
    // 게임 클라로 사용하기엔 부적합 왜? 나중에 알려줌~ ㅋㅋㄹㅃㅂ 앞으로 게임 적합한 형태로 바꿈
    while (GetMessage(&msg, nullptr, 0, 0))  // 해당 프로그램에 발생한 메시지 메시지 Q에 담아놨는데 꺼내놈. while문 계속 돌면서 꺼냄. 쏙~ 
    { //msg 주소에 넘김 발생한 메시지 정보를 저기에 채울려고. 메시지 발생한 종류가 뭔지 
        //msg.time; msg.message, ms.pt; 이렇게 
        //get이 메시지 꺼내줘서 msg에 저장해줌
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) // 키 확인 검사 함. 
        {
            TranslateMessage(&msg); // 각자 윈도우는 처리할 함수를 가지고 있음.
            DispatchMessage(&msg); //처리해줌
        }
    }

    return (int) msg.wParam;
}

 

    if (!InitInstance (hInstance, nCmdShow)) //아까 등록해논 정보 키값으로 찾기
    {
        return FALSE;
    }

 

만약 아까 등록한 클래스가 없다면 FALSE로 프로그램이 종료됩니다.

 

 

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, // sz라는 키값으로 검색
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

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

   return TRUE;
}

이렇게 생겼는데

프로그램이 시작하면서 실제로 hinst변수에 프로그램의 ID를 넣어주고,

 

hWnd라는 변수를 통해 안전하게 윈도우 창을 생성합니다.

즉 이 함수가 메인에서 호출 될 때 윈도우가 직접 만들어 지는 것입니다.

 

첫 번째 인자로 윈도우 클래스를 등록할 때 셋팅했던 wcex.lpszClassName 값과 동일한 값을 인자로 주어야 이 값을 참조하여 등록된 윈도우를 찾아서 윈도우를 생성하게 됩니다.

 

두 번째 인자로 szTitle은 아까 우리가 리소스에서 지정한 곳 타이틀 이름. -> L"HACKKKK" 이렇게 바꾸면 내용이 바꿔 나옵니다.

 

CW_USEDEFAULT, 0, CW_USEDEFAULT, 0 창을 띄울 x y 좌표

 

lpCreateParams WndProc함수 에서 추가 사용을 위해 전달할 수 있는 인수
hInstance 연결된 애플리케이션의 인스턴스
hMenu 메뉴에 대한 핸들
hwndParent 부모로 사용할 창에 대한 핸들
cy 창의 높이
cx 창의 너비
x 창의 왼쪽 위치
y 창의 상단 위치
style 창의 스타일
lpszName 창의 이름(제목)
lpszClass 창의 클래스 이름입니다. wcex.lpszClassName다음 에서 일치해야 합니다 .WNDCLASSEX
dwExStyle 사용하지 않음

 

 

 

 

 

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT)); 

    MSG msg;

    // 기본 메시지 루프입니다: 
    //무한 반복분 돌고 있음.
    //큐 스택 구분. 큐 먼저 넣은 데이터 먼저 나옴. 현재 포커깅 중인 프로그램이 있을거임. message 처리함 넣어주면 걔가 맨 위놈이
    //그림판 클릭 메시지가 발생하면 수행함. 마우스 클릭이 발생함녀 메시지 q에 들어오고 꺼내서 칠함.
    //포커이 되어있지 않으면 뒤에꼐 발생 그게 vs가 가져가고 그림판이 가져가고 이렇게 클릭된 포커싱이

    //get massge : 메세지큐에서 메시지 확인 될 까지 대기
    //언제 true ? 언제 false? false를 반환하면 whiel문이 실패하기 떄문에 종료됨.. 즉 프로그램의 종료.
    //getMessage가 언제 false 
    // true false의 의미는 메세지가 어떤거에 따라 나눠짐
    // 확인해본 msg == WM_QUIT 이면 false 발생 -> 프로그램 종료
    // 이 프로그램 파괴되기 전에 소유하고 있던 윈도우부터 해지가 되고 윈도우가 종료되야함.
    //모든 작업이 완료됬다는 뜻. 윈도우 들 부터 먼저 해지하는 메시지 발생했을 거임 그리고 이게 나온거임.
    //msg가 존재하지 않으면 동작하지 않음. 반응형 형태로 구성됨
    // 게임 클라로 사용하기엔 부적합 왜? 나중에 알려줌~ ㅋㅋㄹㅃㅂ 앞으로 게임 적합한 형태로 바꿈
    while (GetMessage(&msg, nullptr, 0, 0))  // 해당 프로그램에 발생한 메시지 메시지 Q에 담아놨는데 꺼내놈. while문 계속 돌면서 꺼냄. 쏙~ 
    { //msg 주소에 넘김 발생한 메시지 정보를 저기에 채울려고. 메시지 발생한 종류가 뭔지 
        //msg.time; msg.message, ms.pt; 이렇게 
        //get이 메시지 꺼내줘서 msg에 저장해줌
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) // 키 확인 검사 함. 
        {
            TranslateMessage(&msg); // 각자 윈도우는 처리할 함수를 가지고 있음.
            DispatchMessage(&msg); //처리해줌
        }
    }

    return (int) msg.wParam;
}

 

//단축키 정보 로딩
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT)); 

는 Accelerators를 보면 단축키들이 있는데 그걸 로드하는 겁니다

 

 

MSG 구조체로 msg를 선언하고 Getmessage를 함수를 실행하는 데요..

 

메시지는 사용자나 시스템 내부적ㅇ니 동작에 의해 발생된 정보를 말합니다.

버튼을 누르거나 단축키를 눌렀을때.. 그런거죠 (f12 확인)

 

    while (GetMessage(&msg, nullptr, 0, 0))  // 해당 프로그램에 발생한 메시지 메시지 Q에 담아놨는데 꺼내놈. while문 계속 돌면서 꺼냄. 쏙~ 
    { //msg 주소에 넘김 발생한 메시지 정보를 저기에 채울려고. 메시지 발생한 종류가 뭔지 
        //msg.time; msg.message, ms.pt; 이렇게 
        //get이 메시지 꺼내줘서 msg에 저장해줌
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) // 키 확인 검사 함. 
        {
            TranslateMessage(&msg); // 각자 윈도우는 처리할 함수를 가지고 있음.
            DispatchMessage(&msg); //처리해줌
        }
    }

 

GetMEssage는 큐로부터 메시지를 가져오는 역할을 하는데요

queue는 FIFO구조로 LTFO 구조가 아니므로 엄청 빠르게 타자를 쳐도 잘 나오는 게 FIFO방식이니 때문입니다..

 

TranslateAccelerator 단축키 관련 명령어인지 일딴 확인합니다..

 


TranslateMessage(&msg);

키보드.마우스같은 사용장 입력을 읽어야 하는 경우 이 함수가 이벤트 메시지로 바꾸고


DispatchMessage(&msg);
가공된 메시지를 WndProc에 전달. 처리하게 함.

 

메시지가 없담녀 deadtime 정지 상태에 들어가는데 이 형태는 게임 제작 형태에 적합하지 않습니다(언리얼은 1프레임씩 처리) 자세한건 다음 파트떄..

 

 

 

 

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // 이 함수 호출함. 다 세팅 받았기 떄문에 너가 처리하라고 함
{
    switch (message) // 다 놓을 수 없기 떄문에
    {
    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를 사용하는 그리기 코드를 추가합니다...


            //윈도우 핸들
            //윈도우 좌표 창 하나가 있을떄 작업 영역이라는게 있음. 타이플 + 메뉴 아래부터 시작하는게 작업영억 
            //0,0은 맨 위 좌상단 1단위. 1씩 pixel 단위 픽셀 하나하나는 메모리 4000 개가 모여 구성 하면 출력함
            // 전부타 0,0에서 시작함 거기에 얼마나 떨어질지 그렇개ㅔ
            //hdc ?
            Rectangle(hdc,10,10,100,100);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam); // default windowsproc에서 처리 윈도우 제공 한거
    }
    return 0;

 

4개의 매개변수를 가지고 있는데 어디 에서, 발생한 이벤트 , 들어온 문자, 뒤따라온 문자

 

메시지가 어떤 거에 따라 switch문에 들어가고 case로 빠져나옵니다.더 자세히 파고들어가면 HIWORD매크로, LOWORD 매크로 And연산 시켜서 하는데... 어짜피 이 형식은 안씁니다.

 

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

이 함수는 단순히 기본 창 프로시저를 호출하여 WndProc 에서 잡히지 않은 모든 메시지를 처리합니다 . 이 기능이 끝나면 메시지 루프로 돌아가 메시지 대기열에 있는 메시지를 계속 처리하거나 메시지가 수신될 때까지 기다립니다.