마이크로소프트 윈도우의 메시지 루프

메시지 루프()는 윈도우 프로그램에서 메시지를 받아들이는 부분을 뜻하며, 윈도우 기반의 모든 GUI 프로그램에 반드시 포함되는 루틴이다.

윈도우의 GUI 프로그램은 기본적으로 이벤트 드리븐으로 동작한다. 윈도우 시스템은 각각 프로세스에 대해서 개별적인 메시지 큐를 할당한다. 어떤 프로세스가 실행되었을 때 해당 프로세스는 메시지 큐를 부여 받으며, 프로세스에 대해서 사용자의 마우스 입력이나 키보드 입력 등 메시지가 발생했을 때 운영체제가 해당 프로세스의 메시지 큐에 메시지를 넣는다. 또는 프로세스가 이런 메시지들을 스스로 자신의 큐에 추가할 수도 있다. 사용자의 입력이나 기타 여러 가지 행동을 받아서 처리하기 위해서 각각의 프로세스는 자신의 큐에서 메시지들을 지속적으로 읽어들이면서 각 메시지에 대응하는 행동을 수행한다. 이를 위해 프로그래머는 프로그램 내부에 메시지를 받아들이는 GetMessage()와 받은 메시지를 메시지 처리 함수로 디스패치(전달; 전파)하는 DispatchMessage()를 무한정 호출하는 루프를 작성함으로써 프로세스가 메시지를 처리할 수 있게 한다. 이를 메시지 루프라고 한다. 이는 일반적으로 메인 프로그램(메인 함수)의 일부로서 메인 스레드에서 동작한다. 프로세스가 소유한 모든 창(윈도우) 각각에 대한 메시지는 메시지 큐를 통해 전달되며, 해당 프로세스의 메시지 루프에 의해 처리(핸들링)된다. 메시지 루프는 이벤트 루프의 일종이기도 하다.

메시지 루프는 기본적으로 다음과 같이 구성된다.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;

    while(1)
    {
        bRet = GetMessage(&msg, NULL, 0, 0);

        if (bRet > 0)  // (bRet > 0 이면 메시지가 처리되어야 한다.)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else if (bRet < 0)  // (bRet == -1 은 에러를 뜻한다.)
        {
            // 에러에 대한 처리나 로그 남김이나 프로그램 종료 등의 구문.
            // ...
        }
        else  // (bRet == 0 은 프로그램 종료를 의미한다.)
        {
            break;
        }
    }
    return msg.wParam;
}

전형적으로 이벤트 루프는 가상 키 입력을 문자열로 변환하기 위한 TranslateMessage()를 각 메시지에 대해 호출한다. TranslateMessage() 호출은 필수는 아니지만 호출되지 않는 경우 문제가 발생할 소지가 있다. 메시지 루프는 DispatchMessage()는 반드시 호출해야 한다.

메시지 루프는 핸들링하고 있는 메시지 자체에 대해서 직접적으로 처리하지는 않는다. 메시지 루프는 DispatchMessage()를 통해서 메시지들을 해당 메시지가 전달되어야 하는 윈도우의 "윈도우 프로시저"에 전달함으로써 메시지를 전파(Dispatch; 디스패치)시킨다. ("윈도우 프로시저"는 윈도우 클래스가 등록될 때 함께 등록되는 콜백 프로시저를 뜻한다. 여러 개의 창이 하나의 윈도우 프로시저를 공유하는 것도 가능하다.)

프로그램 코드를 통해서 윈도우 프로시저에 직접 메시지를 보낼 수도 있으며, 시스템 큐를 거치지 않고 바로 전달되는 이런 메시지들을 가리켜 비큐 메시지(Nonqueued Messages)라고 부른다.

메시지 루프는 윈도우 GUI 프로그램에 반드시 필요한 부분이지만 엄격한 규칙이나 요구사항은 없다. 메시지를 받아들이고 디스패치하는 부분은 프로그램 코드의 어느 부분에라도 구현될 수 있으며 다양한 옵션이 있다. 예를 들어 GetMessage()는 메시지가 들어올 때까지 스레드를 블록시키지만, GetMessage() 대신 PeekMessage()와 같이 메시지가 있을 때는 읽어들이지만 메시지가 없을 경우에는 즉시 반환하는 논블록킹(Non-blocking) 함수를 이용하는 수도 있다. 또한 WaitMessage()를 이용하면 큐에 메시지가 있을 때까지 스레드를 슬립시키는 것도 가능하다.

한편 윈도우 폼, 윈도우 프레젠테이션 파운데이션, MFC, 델파이, Qt와 같은 최근의 GUI 프레임워크로 프로그램을 개발할 때에는 프로그래머가 메시지 루프를 별도로 구현할 필요가 없다. 하지만 프레임워크가 내부적으로 메시지 루프와 같은 형태로 사용자 입력을 처리하는 부분을 구현하고 있으며, 깊이 있는 프로그래밍이 필요한 경우 프로그래머에 의해 수정될 여지가 있다.

외부 링크 
Meandering Through the Maze of MFC Message and Command Routing (MSJ, July 1995)
A Key's Odyssey: the path of a keystroke message through the VCL 
Platform SDK: Messages and Message Queues
Platform SDK: Windows API: Entering the Message Loop
Using Messages and Message Queues (MSDN)
GetMessage function
PeekMessage function

이벤트 (컴퓨팅)
마이크로소프트 API