WIN32 API 기본

Date:     Updated:

카테고리:

태그:

1일차

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

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

#define MAX_LOADSTRING 100
//WCHAR은 2바이트, wchar_t형임. (16비트)
//그거 이외엔 다른 게 없음(유니코드형을 위한 것)
// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
//메인함수가 처음 시작할 때 받아오는 값. 

WCHAR szTitle[MAX_LOADSTRING];                  // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING];            // 기본 창 클래스 이름입니다.
//100길이로 그냥 선언해놓은 것. 

// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
//함수들은 전방 선언해놓은 것.(함수 프로토타입)

//_In_이나 _In_opt는? 
//어떤 지역변수 앞에 그 변수의 용도를 적는 것. 
//SAL 이라고 함. 주석 언어. 

//주석으로 적는 것보다, 어느정도 함축적으로 약속된 키워드를 하는 것
//_In_ 데이터가 입력된다
//_In_opt_ 부가적으로 들어오는 데이터다. 

//hInstance는 프로세스 시작의 메모리 주소값임. 
//hPrevInstance는 프로세스 시작 이전의 메모리 주소 값. 
//프로그램은 하나. 그런데 그 프로그램으로 여러 개의 프로세스 실행시킬때...
//나보다 먼저 실행된 이전 프로세스의 시작 주소. 
//지금은 몇 개의 프로세스를 올려도 hInstance가 다 같은 값이 나옴. 
//왜? 여러 번 생겨나면 서로 다른 별개의 주소에 올라가야 하는 게 아닌가??

//windows는 가상 메모리를 써서 그럼. 프로세스를 여러개 만들어냈지만...
//각 프로그램마다 자신만의 영역이 있음.(그러니까 물리 메모리는 0x2323던, 0x545던
//가상 메모리 시작점은 0x0000이라는 것임 <- 이해가 쓕쓕 되잖아 

//pCmdLine은... 프로그램 실행할 때 문자열 넣어주면, lpCmdLine에 들어옴
//ex) output.exe -e 하면 -e라는 특정 옵션이 되는 것. 그런 느낌. 
//LPWSTR은 wchar_t형의 포인터 타입. 얘내들은 이런 식으로 포인터까지 통으로 자료형으로 만들어버림.

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
		//딱히 신경쓸 건 없음. 얘내 둘은 딱히 쓰이지 않는다... 라는 걸 선언한 느낌. 
		//매크로임. 
    // TODO: 여기에 코드를 입력합니다.

    // 전역 문자열을 초기화합니다.
    //위의 문자열 배열들에다가 문자열을 전달하고 있는데. 
    //visual studio의 리소스 뷰를 보면 String Table이라는 것이 있는데.
    //IDS_APP_TITLE 값 : 103 , 캡션 : Client같은게 있음
    //프로젝트의 기본 설정 테이블로 등록이 되어 있음. 
    //값은 Resource.h에 있는 전처리기로 #define되어 있음. 
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING);
    
    //    LoadStringW(hInstance, 103, szTitle, MAX_LOADSTRING);
	  //    LoadStringW(hInstance, 109, szWindowClass, MAX_LOADSTRING);
	  //프로젝트 리소스 테이블에서 이 번호에 맞는 값을 가져와라. 
	  
	  
	  //여기서는 밑으로 가서 설명하기. 
	  // 윈도우 정보 등록하는 것. 
    MyRegisterClass(hInstance);

    // 애플리케이션 초기화를 수행합니다:
    // 여기서 윈도우 창을 생성함. 유저와 프로그램 사이의 인터페이스, 접점이라는 의미도 됨.
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    
		//단축키 정보. 
		//리소스 뷰 -> Accelerator -> table 
		//특정 키와 조합했을 때, 키값. 
		//테이블 정보 불러와서. 
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT));

    MSG msg;

    // 기본 메시지 루프입니다:
    //윈도우가 꺼지면 이 while문이 꺼짐. 
    //무한 반복문을 돌고 있음. 
    
    //msg.hwnd -> 메시지가 발생한 윈도우. 따라서 메시지가 발생한 창을 알 수 있음
    //한 프로세스에 여러 윈도우를 띄울수도 있고, 0개의 윈도우를 가질수도 있음...
    
    //GetMessage는 Queue임. 
    //현재 포커싱 중인 프로세스 나한테 온 메시지가 있나. 
    //자기 큐쪽으로 메시지를 가져옴. 
    //해당 프로그램쪽으로 발생한 메시지들을 꺼내서 씀. 
    //발생한 메시지의 정보를 채우기 위해 &msg로 넣어줌...
    
    //결국 메시지 큐에서 메시지 확인할 때까지 대기. 
    //true, false는 어떤 메시지였느냐에 따라 다름... 애초에 돌려면 메시지가 있어야함.
    //WMQUIT는 마감용 메시지임(다른 일 다 끝남)
    
    //반드시 메시지가 있어야하고, 메시지 반응형 방식.
    //툭툭 건드려줘야 계속 반응하는 느낌. 메시지 하나만 목이 빠져라 기다림
    //게임 클라이언트로 사용하기엔 부적합함...
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        // msg.message == WM_QUIT이면 FALSE반환함.
        //그 이외에는 TRUE 당연히 계속 유지되어야 하니까. 
    
    
        //hAccelTable로 단축키를 불러와서 도움말의 ID정보가 IDM_ABOUT임.
        //테이블의 단축키를 쓰지 않으면 필요없는 부분. 
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            //해당 윈도우에서 그 메시지를 처리해라 
            //WndProc 함수 포인터로 그걸 처리함. 
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

//
//  함수: MyRegisterClass()
//
//  용도: 창 클래스를 등록합니다.
//  창 클래스를 우리가 정의하고, 등록하는 절차는 윈도우 OS에서 제공하는 함수. 
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
    //구조체 채워서 함수에 전달하기. 
    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    //WndProc이란?  전방 선언 되어 있는 위 함수. 
    //함수 포인터. 
    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);
    //처음 생성되는 메뉴바 null 값을 넣으면 메뉴바 사라짐. 
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_CLIENT);
    
    //이거의 Key값을 szWindowClass로 사용함. 이 MyRegisterClass의 Key값임. 
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
	  //이건 윈도우 OS에서 제공하는 함수임... 
    return RegisterClassExW(&wcex);
}

//
//   함수: InitInstance(HINSTANCE, int)
//
//   용도: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
//   주석:
//
//        이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
//        주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
   //szTitle 대신 "한재훈~" 넣어도 됨. 2번째 변수는 이름 말하는 거임. 
   //szWindowClass 키 값대로 만들어달라. 
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      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를 사용하는 그리기 코드를 추가합니다...
            //여기는 수업에서 넣은 함수. 
            //윈도우 핸들 , 윈도우 좌표, HDC ? 
            //좌상단 좌표는 (0,0). 1 단위는?? px임. 해상도랑 관련 있음...
            Rectangle(hdc, 10, 10, 110, 110);
            
            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;
}

중요한 부분만 요약 정리하자면…

  • WCHAR 형은 wchar_t형이다. 그러면 wchar_t형은 유니코드(16비트)를 위한 char자료형.
  • HINSTANCE hInst는 현재 인스턴스이다.

  • szTitle, szWindowClass는 이름, 기본 창 클래스 이름 자료형 선언해놓은 것.
// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
//함수들은 전방 선언해놓은 것.(함수 프로토타입)

이 부분은 WINAPI 기본 함수들을 전방 선언(함수 프로토타입) 선언해놓은 것.

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)

Main의 *in*, *in opt등은 SAL이라는 주석언어. 함축적으로 집약된 약속된 키워드를 적는 것.*

  • hInstance는 프로세스 시작의 메모리 주소 값이다. (가상 메모리 주소로 작동하여, 출력하면 어떤 프로세스던지 동일한 값 나옴)
  • hPrevInstance는 프로세스 시작 이전의 메모리 주소 값. ← 상술한 가상 메모리라서 요즘엔 잘 안씀..
  • lpCmdLine은 프로그램 실행할 때 문자열 넣어주면 lpCmdLine에 들어감. -e, -t같은 옵션 용도
    // 전역 문자열을 초기화합니다.
    //위의 문자열 배열들에다가 문자열을 전달하고 있는데. 
    //visual studio의 리소스 뷰를 보면 String Table이라는 것이 있는데.
    //IDS_APP_TITLE 값 : 103 , 캡션 : Client같은게 있음
    //프로젝트의 기본 설정 테이블로 등록이 되어 있음. 
    //값은 Resource.h에 있는 전처리기로 #define되어 있음. 
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING);
  • IDS_APP_TITLE, IDC_CLIENT와 같은 테이블에 지정된 값으로 가져와라.

    // 애플리케이션 초기화를 수행합니다:
    // 여기서 윈도우 창을 생성함. 유저와 프로그램 사이의 인터페이스, 접점이라는 의미도 됨.
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    
    
    //단축키 정보. 
		//리소스 뷰 -> Accelerator -> table 
		//특정 키와 조합했을 때, 키값. 
		//테이블 정보 불러와서. 
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT));
  • 여기서 윈도우 창(한 프로세스에 창은 여러개 띄워질 수 있음) 생성함.
  • 그 밑은 단축키 정보. ctrl + A등으로 도움말 불러오기… 그런 기능.
    // 기본 메시지 루프입니다:
    //윈도우가 꺼지면 이 while문이 꺼짐. 
    //무한 반복문을 돌고 있음. 
    
    //msg.hwnd -> 메시지가 발생한 윈도우. 따라서 메시지가 발생한 창을 알 수 있음
    //한 프로세스에 여러 윈도우를 띄울수도 있고, 0개의 윈도우를 가질수도 있음...
    
    //GetMessage는 Queue임. 
    //현재 포커싱 중인 프로세스 나한테 온 메시지가 있나. 
    //자기 큐쪽으로 메시지를 가져옴. 
    //해당 프로그램쪽으로 발생한 메시지들을 꺼내서 씀. 
    //발생한 메시지의 정보를 채우기 위해 &msg로 넣어줌...
    
    //결국 메시지 큐에서 메시지 확인할 때까지 대기. 
    //true, false는 어떤 메시지였느냐에 따라 다름... 애초에 돌려면 메시지가 있어야함.
    //WMQUIT는 마감용 메시지임(다른 일 다 끝남)
    
    //반드시 메시지가 있어야하고, 메시지 반응형 방식.
    //툭툭 건드려줘야 계속 반응하는 느낌. 메시지 하나만 목이 빠져라 기다림
    //게임 클라이언트로 사용하기엔 부적합함...
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        // msg.message == WM_QUIT이면 FALSE반환함.
        //그 이외에는 TRUE 당연히 계속 유지되어야 하니까. 
    
    
        //hAccelTable로 단축키를 불러와서 도움말의 ID정보가 IDM_ABOUT임.
        //테이블의 단축키를 쓰지 않으면 필요없는 부분. 
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            //해당 윈도우에서 그 메시지를 처리해라 
            //WndProc 함수 포인터로 그걸 처리함. 
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
  • While문을 계속 돌면서, 메시지가 들어오길 계속 바람.
//  함수: MyRegisterClass()
//
//  용도: 창 클래스를 등록합니다.
//  창 클래스를 우리가 정의하고, 등록하는 절차는 윈도우 OS에서 제공하는 함수. 
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
    //구조체 채워서 함수에 전달하기. 
    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    //WndProc이란?  전방 선언 되어 있는 위 함수. 
    //함수 포인터. 
    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);
    //처음 생성되는 메뉴바 null 값을 넣으면 메뉴바 사라짐. 
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_CLIENT);
    
    //이거의 Key값을 szWindowClass로 사용함. 이 MyRegisterClass의 Key값임. 
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
	  //이건 윈도우 OS에서 제공하는 함수임... 
    return RegisterClassExW(&wcex);
}
  • 창 클래스를 정의내리는 곳. 창의 스타일, 함수, 아이콘, 커서, 메뉴바와 같은 세팅을 여기서 해서 RegisterClassExW라는 윈도우가 내부적으로 처리하는 함수에 전해준다.
//
//   함수: InitInstance(HINSTANCE, int)
//
//   용도: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
//   주석:
//
//        이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
//        주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
   //szTitle 대신 "한재훈~" 넣어도 됨. 2번째 변수는 이름 말하는 거임. 
   //szWindowClass 키 값대로 만들어달라. 
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

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

   return TRUE;
}
  • 윈도우 창(그 프로세스의 주 창)을 만들어서 표시한다.
  • szWindowClass는 해시키와 같은 느낌으로 생각하면 됨. 프로세스마다 고유 ID가 있으니까 그걸 불러오는 느낌.
//
//  함수: 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를 사용하는 그리기 코드를 추가합니다...
            //여기는 수업에서 넣은 함수. 
            //윈도우 핸들 , 윈도우 좌표, HDC ? 
            //좌상단 좌표는 (0,0). 1 단위는?? px임. 해상도랑 관련 있음...
            Rectangle(hdc, 10, 10, 110, 110);
            
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default: //따로 정의되지 않은 수많은 단축키들. 
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
  • 위 While문에서 msg를 기반으로 계속 무한 루프를 돈다. 그러면 메시지를 여기서 처리한다.

WINAPI 카테고리 내 다른 글 보러가기

댓글 남기기