WinMain()
사용자의 이벤트로부터 발생되는 메시지를 메시지 큐를 통해 받아 메시지 처리 전용 함수인 윈도 프로시저로 전달한다.
윈도우 프로시저
WinMain() 함수에서 메시지 루프를 통해 발생한 메시지를 큐에 저장한다. 해당 메시지를 처리하기 위해 메시지 처리 전용 함수로 전달되어야 한다. 이때 처리 전용 함수가 윈도우 프로시저이다.
WinMain() 함수와는 별도로 WndProc() 함수의 형태로 존재한다.
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam);
함수명은 변경가능하나 일반적으로 WndProc로 사용
첫 번째 매개변수(HWND hWnd)는 메시지를 받을 윈도우 핸들, 한 클래스로부터 여러 개의 윈도우가 만들어졌을 경우 어떤 윈도우로 전달된 메시지 인지 구분해야 하기에 필요
두 번째 매개변수(UINT iMessage)는 전달된 메시지의 값을 가지며 WM_CREATE, WM_PAINT 등의 미리 정해진 매크로, 상수값을 사용하여 어떤 메시지가 전달되었는지 구분
세 번째 네 번째는 둘 다 32비트 정수값이며, 메시지의 추가 정보를 가진다.
윈도우 프로시저의 특징
1. WinMain()에서 호출하는 것이 아닌 윈도우에 의해 호출된다.
2. WinMain() 내에 존재하는 메시지 루프는 메시지 처리 전용 함수로 전달하는 역할만 한다.
3. 윈도우 프로시저는 메시지가 들어오면 호출되며, 메시지에 맞게 내용을 처리한다.
4. 콜백 함수 (CallBack Function)이다.
콜백 함수 : 사용자가 호출하는 것이 아닌 운영체제에 의해 호출되는 함수
즉, WinMain() 함수 메시지 처리를 관여하는 것이 아닌 오로지 메시지 전달하는 역할만 한다.
그렇기에 윈도우 프로시저를 WinMain() 함수에서 호출하는 것이라고 오해할 수 있으니 주의!
메시지 큐
메시지는
메시지 루프
윈도우 프로그램에서 메시지를 받아들이는 부분을 뜻하며, 윈도우 기반의 모든 GUI 프로그램에 반드시 포함되는 루틴이다.
일반적으로 윈도우의 메시지 처리 루프는 아래 형식처럼 GetMessage(PeekMessage), TranslateMessage, DispatchMessage 메서드로 이루어진다.
MSG msg;
whiel(GetMessage(&msg, NULL, 0,0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// struct MSG의 구조
typedef struct tagMSG {
HWND hwnd; // 메시지가 발생한 윈도우 핸들
UINT message; // message id
WPARAM wParam; // 추가 정보
LPARAM lParam; // 추가 정보
DWORD time; // 메시지 발생 시간
POINT pt; // 커서 위치
} MSG;
윈도우 메시지는 큐(queue) 형태의 자료구조로 관리된다.
System Message Queue와 Thread Message Queue가 있다.
전자는 마우스나 키보드의 입력을 저장하는 곳으로 윈도우 시스템에 하나 존재하고,
후자는 전자로부터 메시지를 받기 위한 곳으로 스레드별당 하나씩 존재한다.
메시지들은 경우에 따라 위 언급한 메시지큐를 경유하거나 경유하지 않고 직접 윈도우 프로시저로 전달된다.
- 큐를 경유하는 경우의 전달 경로 : message -> message queue -> message loop -> window procedure
- 큐를 경유하지 않는 경우의 경로 : message -> window porcedure
큐와 메시지루프를 경유하는 경우 (큐 메시지)
보통 이벤트가 발생하는 경우 그 결과로 생성되는 메시지이다.(ex 마우스 혹은 키보드 입력 메시지, 타이머 메시지)
급히 처리해야 하는 (ex WM_PAINT, WM_QUIT) 메시지의 경우는 큐를 경유하지 않고 메시지 루프에서 처리된다.
- 메시지큐로부터 메시지를 얻어오기 위해 사용하는 API는 GetMessage와 PeekMessage가 있다.
GetMessage는 비선점형 (메시지가 없다면 생길 때까지 리턴하지 않고 대기, 그동안은 CPU에 점유 x)
PeekMessage는 선점형 (무조건 리터, while과 함께 쓰이면 CPU를 대부분 차지, 게임 같은 메시지가 없어도 지속적으로 처리가 필요한 프로그램에서는 이런 식으로 사용)
- 메시지를 등록된 윈도우 프로시저로 전달하는 API는 DispatchMessage이다.
만약 키보드 문자 입력에 대해 처리해줘야 할 필요가 있을 경우 TranslateMessage라는 API를 통해 키보드 입력에 대한 메시지를 생성한다.
TranslateMessage는 WM_KEYDOWN메시지가 있는지 검사하고 있는 경우 눌려진 키가 문자열 키면 WM_CHAR 메시지를 메지시큐에 덧붙여준다.
DispatchMessage는 윈도우 프로시저로 메시지를 전달하는데 만약 메시지가 WM_TIMER이고 lParam으로 지정된 콜백함수가 있는 경우 ( lParam != NULL인 경우 ) 직접 내부에서 콜백함수를 호출한다. 프로시저나 콜백함수가 리턴되어야 이 함수가 리턴되며, 리턴값으로 그 함수의 리턴값을 그대로 반환한다.
큐와 메시지루프를 경유하지 않는 경우 (비큐 메시지)
WM_CREATE, WM_SIZE, WM_CLOSE, WM_DESTROY 등의 입력메시지를 제외한 대부분의 메시지는 곧바로 윈도우 프로시저에게 전달된다.
직접 전달된 귀에 윈도우 커널의 흐름에 속할 것 같지만, 이 역시 GetMessage(PeekMessage)에 의해 이루어지며, 애플리케이션의 흐름에 속한다.
또한 프로시저에서 이러한 메시지드들을 처리하는 과정에서 MessageBox나 DialogBox와 같은 API를 호출해서는 안된다.
이유는 API 내부에 자신의 화면을 유지시키기 위한 메시지루프가 있는데 이 루프는 GetMessage를 호출하기 때문이다. GetMessape 내부에서 해당 메시지를 처리하는 도중에 다시 GetMessage를 호출한 셈이 되면서 무한루프에 빠지게 된다.
'C++' 카테고리의 다른 글
STL 컨테이너(list) 사용 및 멤버 함수 (0) | 2023.06.20 |
---|---|
STL 컨테이너(deque, list, set, map) 설명 (0) | 2023.06.20 |
this 포인터, 함수 포인 (0) | 2023.04.13 |
C++ 가상함수 테이블 (0) | 2023.04.12 |
C++ 복사 생성자 (0) | 2023.04.12 |