VisualSVN
, TortoiseSVN
이 대표적이다.일반적으로 간단한 프로젝트를 생성하면 하나의 솔루션 안에 하나의 프로젝트가 들어가다보니 이 두개의 이름을 보통은 일치 시킨다.
하나의 솔루션안에는 프로젝트가 여러가지가 들어갈 수 있다.
WinAPI에서는 프로젝트를 여러개 안할것이지만 다렉x에서 부터는 프로젝트 여러가지 할것이다.
GameClient
가 빌드되서 최종적으로 게임이 될것이다.int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
// 1
hInstance;
//2
hPrevInstance;
1번과 2번은 특정 프로그램을 구별하는 id값으로 받을것이다.
1번은 프로세스의 주소값.
2번은 16비트 원도우 시절에 쓰던 인자다.
2번이 의미가 없어진 이유 는 아래.
초창기 윈도우 운영 체제에서 프로세스와 메모리 관리는 상대적으로 단순했습니다.
프로그램이 실행될 때, 각 프로세스는 실제 물리적 메모리 영역에 직접 할당되었습니다.
예를 들어, 사용자가 그림판 프로그램을 여러 번 실행하면, 각각의 인스턴스가 실제 메모리에 별도로 등록되고 구분되었습니다.
각 프로세스가 고유한 hInstance
값을 가졌으며, 이는 프로세스의 인스턴스 핸들을 나타냈습니다.
hPrevInstance
는 이전에 실행된 프로세스의 주소를 참조하는 데 사용되었습니다.
하지만 이는 초기 윈도우 버전에서의 구조이며, 현대의 운영 체제에서는 hPrevInstance
가 사용되지 않습니다.
현대의 윈도우 운영 체제는 가상 메모리 시스템을 도입하여 프로세스 관리와 메모리 사용의 효율성을 크게 향상시켰습니다.
가상 메모리 시스템은 각 프로세스에게 독립된 주소 공간을 제공함으로써, 프로세스가 전체 메모리를 독점적으로 사용하는 것처럼 보이게 만듭니다.
실제로는 운영 체제가 물리적 메모리 자원을 관리하고, 필요에 따라 메모리와 디스크 기반의 스왑 공간 사이에서 데이터를 이동시킵니다.
현대 운영체제의 장점
hInstance
주소가 동일하게 나타나며, 과거처럼 이전 프로세스의 주소를 참조할 필요가 없어졌습니다.SAL
주석언어// 1
_In_,
// 2
_In_opt_
아무런 영향을 미치지 못하고 함수 입력인자 앞에 붙여서 인자가 무슨 용도인지 간략하게 설명해주는 역할을 한다.
2번 In_opt을 붙여서 입력으로 들어왓지만 전혀 쓰이지 않는다는 의미다.
그래서 2번인자는 쓸모는 없지만 지울수는 없다.
// 3
_In_ LPWSTR lpCmdLine,
// 4
_In_ int nCmdShow
lpCmdLine
)는 프로그램 실행할떄 커맨드라인 명령어로 실행했을떄 던져주는 문자열들을 주소값(지금 필요 없다.) ShowWindow(hWnd, nCmdShow);
nCmdShow
)는 ShowWindow
함수의 true와 false값으로 보여줄지 여부를 판단(1이면 보여줌 0이면 안보여줌). UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
hPrevInstance;
UNREFERENCED_PARAMETER
매크로가 컴파일될떄 저렇게 코드를 바꿔버려서 인자를 쓸모 없게 만들어 버려서 컴파일할떄 아무 의미가 없어지게 만든다.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);
hInstance
를 확인하면 이런 프로세스 하나가 생긴걸 확인 가능.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로 재정의 해둔것이다.szTitle, MAX_LOADSTRING
쪽 부분은 szTitle
의 크기가 문자열을 가져왓는데 더 크면 예외처리나 그런걸 하기위해서 인자로 받는다.MY_STRING
를 추가해서 104로 값을 주고 문자열을 abcdefg
를 적어준 다음 LoadStringW(hInstance, , MY_STRING, szTitle, 100);
LoadStringW
함수를 호출하면MY_STRING
에 등록한 문자열로 출력이 된다.104
딱 적어주는 것보다 MY_STRING
을 적어서 매크로 구분을 사용하면 직관적이기 떄문에 사용했다. 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);
}
WNDCLASSEXW
의 f12눌러서 들어간 사진.RegisterClassExW
는 원도우에서 만든거라 볼수 없다.MyRegisterClass
는 내부에 있지만 내부적으로는 2번처럼 원도우쪽 함수를 호출하고 있다. // 3
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
프로세스 == 원도우
라고 착각 하면 안된다.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 윈도우 생성
HWND타입은 구조체 포인터 타입이고 끝나고 값은 주소값이 들어온다.(id값)
1번에 szWindowClass
를 2번에 L"Window Class Key"
로 수정함.
클래스 네임을 문자열을 키값으로 구조체에 등록해서 hInstance
가 키값을 탐색을 해서 원도우 창을 만든다.
작동순서
hInst = hInstance;
if (!InitInstance (hInstance, nCmdShow)) // 인스턴스 핸들을 전역 변수에 저장합니다.
{
return FALSE;
}
OS 가 관리하는 오브젝트, 직접적인 접근이 불가능하고 ID 값을 통해서 제어함
운영체제의 특징 운영체제들은 핵심 기능을 순수하게 메모리값을 공개하지 않는다.
운영 체제에서의 커널 오브젝트: 커널 오브젝트는 OS의 필수적인 구성 요소로, 시스템 자원에 대한 인터페이스 역할을 합니다. 시스템 자원과 프로세스를 관리하는 데 중요한 역할을 합니다.
윈도우 OS에서의 멀티태스킹: 윈도우에서 동시에 여러 프로그램을 실행할 수 있는 것이 운영 체제에서 중요한 발전이었다는 것을 언급하고 있습니다.
운영 체제가 순수한 메모리 값을 공개하지 않는 것
객체를 사용한 윈도우 생성
윈도우에서 핸들의 개념
인터페이스를 통한 커널 오브젝트 핸들의 조작
커널 오브젝트 핸들의 종류: 윈도우, 펜, 브러시 등이 예시로 들 수 있습니다.
백그라운드 프로세스와 멀티태스킹
프로세스 및 윈도우 관리: 하나의 프로세스가 여러 윈도우를 가질 수 있고, 동일한 ID를 공유하더라도 커널 오브젝트를 식별하고 구별할 수 있다는 개념입니다.
struct WND
{
int id;
};
struct BRUSH
{
int id;
};
// 1
ShowWindow(hWnd, true);//false);
// 2
UpdateWindow(hWnd);
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
// 분석
TranslateMessage(&msg);
// 처리
DispatchMessage(&msg);
}
}
반응형 프로그램
메시지 큐의 생성
메시지 처리
GetMessage
함수를 사용하여 윈도우 메시지가 들어왔는지 확인합니다. 이 함수는 메시지 큐에서 메시지를 검색하고, 메시지가 있을 경우 해당 메시지를 처리하기 위한 정보를 반환합니다.무한 루프
메시지 분석 및 처리
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
왼쪽키가 눌렸다면 화면 끄는 매크로 // 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