WIN32 API 기본
카테고리: WINAPI
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를 기반으로 계속 무한 루프를 돈다. 그러면 메시지를 여기서 처리한다.
댓글 남기기