WinAPI
Windows API는 Microsoft Windows에서 사용하는 C언어 기반의 API(Application Programming Interface)를 말한다.
기본적으로는 C언어 기반이지만, C++에서도 사용이 가능하다.
윈도우에서 실행되는 모든 종류의 어플리케이션들은 내부적으로 전부 이 윈도우 API 함수를 호출하는 형태로 바뀐다.
이제 visual Studio로 WinAPI를 다루어 볼껀데, Win32 프로젝트를 생성하는 방법은 다음과 같다.
윈도우 프로시저(Window Procedure)
WndProc를 말하며, 윈도우 클래스당 하나씩 배정되며 메시지에 대응하는 방식을 정의함으로써 윈도우의 행동 양식을
결정한다. 그래서 같은 윈도우 클래스로부터 만들어진 윈도우들은 모두 같은 윈도우 프로시저를 공유하며
동일한 동작을 하는 것이다.
WndClass구조체의 lpfnWndProc 멤버에 함수의 포인터가 대입된다. 이후 메시지 루프의 DispatchMessage 함수가
메세지 내용을 분석하여 적절한 윈도우 프로시저를 호출하며 이 윈도우 클래스로부터 생성된 모든 윈도우의 메시지는
이 함수가 처리한다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//어떤 메세지가 발생되었는가를 통해 처리할 조건문
switch (message)
{
//그리라는 메세지가 들어온경우
case WM_PAINT:
{
PAINTSTRUCT ps;
//hdc-> 도화지
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
}
//파괴하거나 닫으라는 메세지가 들어온경우
case WM_DESTROY: case WM_CLOSE:
{
//프로그램 종료 호출 함수
PostQuitMessage(0);
break;
}
}
return (DefWindowProc(hWnd, message, wParam, lParam));
}
여기서 함수명은 사용자가 변경할순 있지만, 일반적으로 WndProc을 사용한다고 한다.
첫 번째 인수 hWnd는 이 메시지를 받을 윈도우 핸들이다. 한 클래스로부터 여러 개의 윈도우가 만들어졌을 경우는
어떤 윈도우로 전달된 메시지인지 구분해야 하므로 이 인수가 필요하다.
두 번째 인수 message는 전달된 메시지의 값을 가지며 WM_CREATE, WM_PAINT 등의 미리 정해진 매크로 상수값을
사용하여 어떤 메시지가 전달되었는지 구분한다.
세 번째, 네 번째 인수는 둘 다 32비트 정수 값으로, 메시지의 추가 정보를 가진다. 메시지에 따라 이 값들의 의미는
달라진다.
윈도우 메시지(Window Message)
메시지는 프로그램에 변화가 생겼을 때 Windows가 프로그램에게 알리는 정보를 말한다.
예를 들어 사용자가 마우스의 버튼을 눌렀다거나 키보드를 눌렀거나 윈도우가 최소화 되었거나 하는 변화에
대한 정보들이 모두 메시지이다.
메시지가 발생하면 프로그램에서 어떤 정보를 담고 있는지 분석하여 어떤 루틴으로 호출할지 결정한다.
주어진 메시지에 대한 반응을 정의하는 방식으로 프로그램이 실행되는 것이다.
메시지는 구조체로 정의되어 있는데 윈도우 핸들, 메시지 식별 번호, 추가 정보, 시각, 커서 위치 등이 포함되어 있다.
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
각각의 멤버는 다음과 같다.
- hwnd : 메시지의 받을 윈도우 핸들
- message : 어떤 종류의 메시지인지 구분
- wParam, IParam : 전달된 메시지에 대한 부가적인 정보를 가짐. 32비트 값을 가지며, 메시지 별로 의미가 다름
- time : 메시지가 발생한 시간
- pt : 메시지가 발생했을 때 마우스의 위치
메시지 루프
사용자에 의해 입력된 메시지는 시스템 메시지 큐에 일단 저장되고 스레드 메시지 큐로 이동했다가 메시지 루프에 의해
해당 윈도우의 윈도우 프로시저로 보내져 처리된다.
메시지는 최종 처리되기 직전까지 계속 큐에 유지되는데, 이때 메시지는 위에 윈도우 메시지 구조체의 형태로 존재한다.
//윈도우 메시지 구조체
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
메시지 루프는 메시지 큐에서 메시지를 꺼내 메시지 처리 함수로 보내는 일을 한다. 보통 WinMain의 제일 끝에 위치하여
다음과 같이 그 형태가 정형화 되어 있다.
//**메시지 루프**
// 기본 메시지 루프입니다:
//런타임동안 반복시킬 반복문
while (true)
{
//GetMessage 발생한메세지를 줄때까지 계속 기다려서 받는애
//Peek 발생할때만 집어서 가져오는애
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//메세지를 집어오지 않을때
}
}
//프로그램 종료 준비
총 세 개의 함수 호출과 while 루프로 싸여 있는데 이 루프는 프로그램이 끝날 때까지 계속 반복된다.
메시지 큐
메시지는 크게 메시지 큐로 들어가는 큐(Queued) 메시지와 큐에 들어가지 않고 곧바로 윈도우 프로시저로 보내지는
비큐(Non Queued) 메시지로 구분된다. 큐 메시지는 주로 사용자의 입력으로부터 발생된다.
큐 메시지는 발생 직후 시스템 메시지 큐에 저장되어 스레드 메시지 큐로 보내지며 최종적으로 윈도우 프로시저에 의해
입력된 순서대로 처리된다.
큐 메시지의 예로는 WM_KEYDOWN, WM_LBUTTONDWON, WM_PAINT, WM_TIMER, WM_QUIT 이 있다.
큐 메시지는 입력된 순서대로 큐에 쌓여 있다가 차례대로 처리된다는 점인데 입력된 키를 바로 처리하지 못하는
일이 종종 발생하는 할 때 처리하지 못한 키를 대기시키기 위한 완충 장치로 메시지 큐가 존재하는 것이다.
비큐 메시지는 윈도우에게 특정 사실을 알리거나 명령을 보내기 위해 큐를 통하지 않고 바로 윈도우 프로시저로
보내는 메시지이며 대부분의 메시지들은 비큐 메시지이다. 메시지 큐와 메시지 루프를 거치지 않으므로 신속하게
처리된다.
운영체제는 하나의 시스템 메시지 큐를 관리하며 또한 각 스레드 별로 하나씩 메시지 큐를 생성한다.
메시지 큐는 들어온 순서대로 메시지를 쌓아 놓는 곳이다. 시스템 메시지 큐는 시스템 전체에 유일한 메시지
큐이며 모든 큐 메시지는 먼저 이곳에 저장된다.
사용자가 마우스를 조작하거나 키보드를 두드리면 이 입력은 디바이스 드라이버에 의해 메시지로 변환되어
시스템 메시지 큐에 넣어진다.
시스템은 큐의 메시지를 하나씩 꺼내 어떤 스레드로 보낼 메시지인지 판단하여 스레드 메시지 큐로 보내고 시스템
메시지 큐에서 메시지를 지운다. 이 작업을 하는 프로세스를 시스템 아이들(System Idle)이라고 한다.
시스템은 스레드를 생성할 때 디폴트로 메시지 큐를 가지지 않는 상태로 만든다. 윈도우를 전혀 가지지 않는 작업
스레드는 메시지를 받아들이지 않으며 따라서 불필요하게 메시지 큐 생성하여 메모리가 낭비되지 않도록 하기 위함이다.
'C++' 카테고리의 다른 글
STL deque / map (0) | 2023.01.17 |
---|---|
C++ 배우기 29(시프트 연산자, 비트 단위 연산자) (0) | 2022.12.13 |
C++ 배우기 27(함수포인터) (0) | 2022.12.01 |
C++ 배우기 26(LValue / RValue) (0) | 2022.11.30 |
C++ 배우기 25(복사생성자, 팩토리 패턴) (2) | 2022.11.29 |