자료 저장소


윈도우즈가 이전 운영체제인  도스와 구분되는 가장 큰 차이점은 이벤트 드리븐, 즉 메시지 기반의 운영체제라는 점이다. 윈도우즈에서 실행되는 응용 프로그램은 사용자의 입력을 받기 위해 함수를 직접적으로 호출하는 경우가 없으며 시스템이 보내는 메시지를 기다릴 뿐이다. 메시지는 윈도우즈 프로그래밍의 핵심 사항이므로 충분한 시간을 두고 연구해 보아야 한다.


# 윈도우 프로시저

윈도우 프로시저(Window Procedure)는 윈도우 클래스당 하나씩(윈도우당 하나씩이 아니라) 배정되며 메시지에
대응하는 방식을 정의함으로써 윈도우의 행동 양식을 결정한다. 그래서 같은 윈도우 클래스로부터 만들어진
윈도우들은 모두 같은 윈도우 프로시저를 공유하며 동일한 동작을 하는 것이다.

윈도우 프로시저는 다음과 같은 원형을 가지는 함수이며 WNDCLASS구조체의 lpfnWndProc 멤버에 이 함수의
포인터가 대입된다. 이후 메시지 루프의 DispatchMessage 함수가 메시지 내용을 분석하여 적절한 윈도우
프로시저를 호출하며 이 윈도우 클래스로부터 생성된 모든 윈도우의 메시지는 이 함수가 처리한다.


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam);
함수명은 사용자가 마음대로 변경이 가능하나 일반적으로 WndProc이라는 이름을 사용한다.

첫번째 인수 hWnd는 이 메시지를 받을 윈도우 핸들이다. 한클래스로부터 여러 개의 윈도우가 만들어졌을 경우는
어떤 윈도우로 전달된 메시지 인지 구분해야 하므로 이 인수가 필요하다.

두번째 인수 iMessage는 전달된 메시지의 값을 가지며 WM_CREATE, WM_PAINT 등의 미리 정해진 매크로
상수값을 사용하여 어떤 메시지가 전달되었는지 구분한다.

세번째, 네번째 인수는 둘 다 32비트 정수값이며 메시지의 추가 정보를 가진다. 메시지에 따라 이 값들의 의미는
달라진다.


# 메시지 큐

메시지는 크게 메시지 큐로 들어가는 큐(Queued) 메시지와 큐에 들어가지 않고 곧바로 윈도우 프로시저로
보내지는 비큐 (Non Queued)메시지로 구분된다. 큐 메시지는 주로 사용자의 입력으로 부터 발생된다.
큐 메시지는 발생 직후 시스템 메시지 큐에 저장되어 스레드 메시지큐로 보내지며 최종적으로 윈도우 프로시저에
의해 입력된 순서대로 처리된다.
큐 메시지의 예로는 WM_KEYDOWN, WM_LBUTTONDWON, WM_PAINT, WM_TIMER, WM_QUIT 이 있다.

큐 메시지는 입력된 순서대로 큐에 쌓여 있다가 차례대로 처리된다는 점인데 입력된 키를 바로 처리하지 못하는
일이 종종 발생하는 할 때 처리하지 못한 키를 대기시키기 위한 완충 장치로 메시지 큐가 존재 하는 것이다.

비큐 메시지는 윈도우에게 특정 사실을 알리거나 명령을 보내기 위해 큐를 통하지 않고 바로 윈도우 프로시저로
보내는 메시지이며 대부분의 메시지들은 비큐 메시지이다. 메시지 큐와 메시지 루프를 거치지 않으므로 신속하게
처리된다.

운영체제는 하나의 시스템 메시지 큐를 관리하며 또한 각 스레드별로 하나씩 메시지 큐를 생성한다.
메시지 큐는 들어온 순서대로 메시지를 쌓아 놓는 곳이다. 시스템 메시지 큐는 시스템 전체에 유일한 메시지
큐이며 모든 큐 메시지는 먼저 이곳에 저장된다.
사용자가 마우스를 조작하거나 키보드를 두드리면 이 입력은 디바이스 드라이버에 의해 메시지로 변환되어
시스템 메시지 큐에 넣어진다.

시스템은 큐의 메시지를 하나씩 꺼내 어떤 스레드로 보낼 메시지인지 판단하여 스레드 메시지 큐로 보내고 시스템
메시지 큐에서 메시지를 지운다. 이 작업을 하는 프로세스를 시스템 아이들(System Idle)이라고 한다.

시스템은 스레드를 생성할 때 디폴트로 메시지 큐를 가지지 않는 상태로 만든다. 윈도우를 전혀 가지지 않는 작업
스레드는 메시지를 받아들이지 않으며 따라서 불필요하게 메시지 큐 생성하여 메모리가 낭비되지 않도록 하기 위함이다.


# 메시지 루프

사용자에 의해 입력된 메시지는 시스템 메시지 큐에 일단 저장되고 스레드 메시지 큐로 이동했다가 메시지
루프에 의해 해당 윈도우의 윈도우 프로시저로 보내져 처리된다. 메시지는 최종 처리되기 직전까지 계속 큐에
유지되는데 이때 메시지는 다음과 같이 정의된 구조체의 형태로 존재한다.

typedefstruct tagMSG 
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
앞의 4개의 멤버는 WndProc의 인수와 같으며, 메시지 정보외에 발생한 시간과 발생시의 마우스 좌표가 들어간다.
하지만 시간과 마우스 좌표는 모든 메시지들이 필요로 하는 정보가 아니므로 필요 할 경우 직접 조사하여야 한다.

DWORD GetMessagePos(void) // 하위 워드에 x좌표, 상위 워드에 y좌표를 리턴한다.
LONG GetMessageTime(void) // 메시지 발생 시간을 부팅된 후 경과된 1/1000초 단위로 리턴한다.
메시지 루프는 메시지 큐에서 메시지를 꺼내 메시지 처리 함수로 보내는 일을 한다. 보통 WinMain의 제일 끝에
위치하며 다음과 같이 그 형태가 정형화 되어 있다.

while(GetMessage(&Message,NULL,0,0)) 
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
총 세개의 함수 호출과 while 루프로 싸여져 있는데 이 루프는 프로그램이 끝날 때까지 계속 반복된다.

BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax);
이 함수는 스레드 메시지 큐에 대기중인 메시지를 꺼내 첫번째 인수로 전달된 MSG 구조체에 복사한다.
그리고 이 메시지를 큐에서 제거하고 TRUE를 리턴하되 만약 큐에서 가져온 메시지가 WM_QUIT이면 FALSE를
리턴하여 메시지 루프를 탈출 할 수 있도록 한다. 나머지 인수는 특정 메시지만 골라서 가져오는 메시지 필터링에
사용된다. hWnd 인수에 윈도우 핸들을 주면 이 윈도우와 그 차일드로 보내지는 메시지만 가져오며 NULL이면
현재 스레드의 모든 메시지를 가져온다.

GetMessage 함수에 의해 큐로부터 가져온 메시지는 WM_QUIT가 아닌 한은 TranslateMessage 함수로 전달된다.
이 함수는 가상 키 입력을 문자 입력(WM_CHAR)으로 바꾸는 역할을 하며 가상키 입력이 아닌 경우는 아무 처리도 하지 않는다.

DispatchMessage는 메시지를 윈도우 프로시저로 보내 처리하도록 하는데 MSG 구조체의 hWnd 멤버를 보고
정확하게 목적 윈도우의 메시지 처리 함수로 배달된다.
윈도우 핸들을 알면 이 윈도우가 소속된 윈도우 클래스를 알 수 있으므로 윈도우 클래스에 등록된 윈도우 프로시저를
호출할 수 있다.



[악셀레이터가 등록되어있을 경우 메시지 루프]
while(GetMessage(&Message,NULL,0,0)) 
{
if(!TranslateAccelerator(hWnd,hAccel,&Message))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
}

[특정 메시지에 대해 특수한 예외 처리를 할 경우의 메시지 루프]

while(GetMessage(&Message,NULL,0,0)) 
{
TranslateMessage(&Message);
if(Message.message != WM_KEYDOWN)
{
DispatchMessage(&Message);
}
else
{
/* 키보드가 눌렸을때에 대한 예외 처리*/
}
}

메시지 루프는 단순한 C 루프일 뿐이므로 전혀 신비롭게 생각할 필요가 없고 어려워할 필요도 없다.
단 윈도우즈 프로그램의 연료 공급장치에 해당하는 핵심 루틴이므로 동작 원리를 정확하게 알고 조심스럽게 다루어야 한다.
댓글 로드 중…

최근에 게시된 글