관리 메뉴

Kim's Programming

WinApi - 분석(1/2) 본문

Programming/Windows API

WinApi - 분석(1/2)

Programmer. 2015. 8. 26. 13:28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<Windows.h>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("첫번째");
 
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst = hInstance;
    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;
    WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    WndClass.hInstance = hInstance;
    WndClass.lpfnWndProc = WndProc;
    WndClass.lpszClassName = lpszClass;
    WndClass.lpszMenuName = NULL;
    WndClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&WndClass);
 
    hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, (HMENU)NULL, hInstance, NULL);
    ShowWindow(hWnd, nCmdShow);
 
    while (GetMessage(&Message, NULL, 00))
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return (int)Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    switch (iMessage)
    {
    case WM_DESTROY:
        PostQuitMessage(0);    
        return 0;
        break;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

WinMain
    • 헤더 파일

      우선 제일 첫 행을 보면 windows.h하나만 인클루드 되어있습니다. 도스에서 사용하는 함수의 종류에 따라서 여러 개의 헤더 파일을 포함하지만 윈도우즈에서는 하나의 헤더파일에 모든 API함수들의 원형과 사용하는 상수들이 전부 정의되어 있기 떄문에 windows.h만 포함하면 됩니다. 특별한 경우에는 해당하는 헤더 파일을 포함해야 하지만 지금은 windows.h만 포함하면 됩니다. windows.h 파일은 기본적인 데이터 타입, 원형, 매크로 상수 등을 정의하며 그 외 윈도우즈 프로그래밍에 필요한 보조 헤더 파일을 포함하고 있습니다. 그래서 윈도우즈 프로그램의 첫 줄은 항상 window.h로 시작됩니다. 도스에서의 #include<stdio.h>로 시작하는 것과 같습니다.

    • 시작점

      다음으로 차이나는 점은 프로그램의 시작점인 엔트리 포인트(Entry Point)가 main이 아니라 WinMain이라는 점입니다. 윈도우즈 프로그램의 시작점은 main이 아닌 WinMain이며 모든 윈도우즈 프로그램은 WinMain에서부터 실행을 시작합니다. 원형은 다음과 같습니다.
      int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
      도스에서의 main함수는 인수 사용 여부에 따라 여러 가지 원형이 있지만 WinMain의 원형은 위와 같이 고정이 되어 있습니다. APIENTRY 지정자는 윈도우즈의 표준 호출 규약인 __stdcall을 사용한다는 뜻인데 일단 없다고 생각도 무방합니다. 다음은 인수들에 대한 의미입니다.

      인수

       의미 

       hInstance

       프로그램 인스턴스 핸들입니다.

       hPrevInstance

       바로 앞에 실행된 현재 프로그램의 인스턴스 핸들입니다. 없을 경우는 NULL이 되며 Win32에서는 항상 NULL입니다. 16비트와 호환성을 위해서만 존재하는 인수이므로 신경쓰지 않아도 됩니다. 역사가 오래되다 보니 어쩔 수 없이 생기는 일종의 찌꺼기 입니다.

       lpszCmdParam

      명령행으로 입력된 프로그램 인수입니다. 도스의 argv인수에 해당하며 보통 실행직후에 열 파일의 경로가 전달됩니다.

       nCmdShow 

      프로그램이 실행될 형태이며 최소화, 보통 모양등이 전달됩니다. 

    • 이 중 hInstance 이외에는 잘 사용되지 않습니다. 인스턴스(Instance)라는 말은 실행중인 프로그램 하나를 칭하는 용어입니다. 윈도우즈는 여러 개의 프로그램이 동시에 실행되는 멀티 태스킹 시스템일 뿐만 아니라 하나의 프로그램이 여러 번 실행될 수도 있습니다. 이때 실행 되고 있는 각각의 프로그램을 프로그램 인스턴스라고 하며 간단히 줄여서 인스턴스라고 합니다. 만약 메모장을 2개를 띄워 놓았다고 하면 메모장 각각은 서로 다른 인스턴스 핸들을 가지며 운영체제는 이 인스턴스 핸들값으로 각각의 메모장을 구분하게됩니다. hInstance란 프로그램 자체를 일컫는 정수 값이며 프로그램 내부에서 자기 자신을 가리키는 1인칭 대명사입니다.

      많은 API 함수들이 hInstace값을 인수로 요구하는데 그래서 이 소스에서는 WinMain의 인수로 전달된 hInstance의 값을 전역변수 h_hInst에 대입해 두었습니다. hInstance 인수는 기억 부류가 지역변수이기 떄문에 WInMain 밖에서는 사용할 수 없기 떄문입니다. 이 소스에서는 h_hInst값을 당장 사용하지 않지만 앞으로는 이 값을 수시로 사용하게 됩니다. g_hInst외에 lpszClass라는 전역 문자열이 정의되어 있는데 이 문자열은 윈도우 클래스를 정의하는데 사용되며 윈도우의 타이틀 바에 표시되기도 합니다. 이 예제이서는 다음 두 변수를 전역으로 선언해 사용합니다.
      HINSTANCE g_hInst;
      LPCTSTR lpszClass=TEXT("첫번째");

    • 메세지 처리 함수

      이 프로그램은 두 개의 함수만 정의하고 있습니다. 하나는 프로그램의 시작점인 WinMain이며 나머지 하나는 WndProc입니다. WndProc은 사용자와 시스템이 보내오는 메시지를 처리하는 중요한 일을 합니다. 도스에선 main 함수만으로도 프로그램을 작성할 수 있지만 윈도우즈에서는 특별한 경우를 제외하고는 이 두개의 함수가 모두 있어야 합니다. WinMain은 메인 윈도우를 만들고 화면에 윈도우를 표시하기만 할 뿐이며 대부분의 일은 WndProc에서 이루어집니다. WinMain은 프로그램을 초기화하고 시작시키기만 하므로 모양이 대체로 일정한데 비해 WndProc은 프로그램의 실직적이고도 고유한 처리를 하는 곳이므로 프로그램에 따라 천차만별로 달라집니다. 그래서 소스를 분석할 때 주의 깊게 봐야 할 부분은 WinMain이 아니라 WndProc입니다. WinMain 바로 윗부분에 WndProc 함수의 원형이 선언되어 있습니다.
      LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
      함수 원형에 CALLBACK이라는 매크로가 사용되었는데 이 매크로도 APIENTRY와 마찬가지로 __stdcall로 정의되어 있습니다. 이 외에 WINAPI라는 매크로도 사용되는데 역시 __stdcall입니다. 이런 매크로들은 그 자체로 어떤 의미를 가진다기보다는 호환성과 이식성을 위해 존재하는 것들이므로 당분간은 크게 신경쓰지 않아도 상관없습니다. Win32에서는 __stdcall을 표준 호출 규약으로 사용하는데 그렇지않은 시스템으로의 이식을 고려하여 중간 매크로를 두는 것 뿐입니다.

WinMain

WinMain 함수에서 하는 가장 중요한 일은 메인 윈도우를 만드는 일입니다. 윈도우가 있어야 사용자로부터 입력을 받을 수 있고 작업한 결과를 출력할 수도 있기 떄문입니다. 윈도우를 만들려면 윈도우 클래스를 먼저 등록한 후 CreateWindow 함수를 호출해야 합니다. 윈도우 클래스를 먼저 등록한 후 CreateWindow 함수를 호출해야 합니다. 윈도우 클래스는 만들어질 윈도우의 여러가지 특성을 정의하는 구조체이며 모든 윈도우는 윈도우 클래스의 정보를 기반으로 하여 만들어집니다. 윈도우 클래스는 windows.h에 다음과 같이 정의되어 있는 구조체 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct tagWNDCLASS
{
    UINT                style;
    WNDPROC        lpfnWndProc;
    int                    cbClsExtra;
    int                     cbWndExtra;
    HINSTANCE        hInstance;
    HICON                hIcon
    HCURSOR            hCursor;
    HBRUSH            hbrBackground;
    LPCSTR            lpszMenuName;
    LPSTR                lpszClassName;
}WNDCLASS;
cs


10개나 되는 멤버를 가지고 있는데 WNDCLASS 구조체의 각 멤버 의미는 다음과 같습니다.


    • style

      윈도우의 스타일을 정의합니다. 즉 윈도우가 어떤 형태를 가질 것인가를 지정하는 멤버입니다. 이 멤버가 가질 수 있는 값은 무척 많지만 가장 많이 사용하는 값이 CS_HREDRAW와 CS_VREDRAW입니다. 이 두 값을 OR연산자(|)로 연결하여 사용합니다. 이 스타일은 윈도우의 수직(또는 수평) 크기가 변할 경우 윈도우를 다시 그린다는 뜻입니다. 이 밖에도 많은 값이 올 수 있습니다.

    • lpfnWndProc

      이 멤버는 윈도우의 메시지 처리 함수를 지정합니다. 메시지가 발생할 떄마다 이 멤버가  지정하는 함수가 호출되며 이 함수가 모든 메시지를 처리합니다. 메시지 처리 함수의 이름은 물론 마음대로 정할 수 있지만 거의 WndProc으로 정해져 있는 편입니다. 위의 소스에서 이 멤버에 WndProc함수를 대입하고 있으므로 이 윈도우 클래스로부터 만들어지는 윈도우의 메시지는 WndProc 함수가 처리하게됩니다.

    • cbClsExtra, cbWndExtra

      일종의 예약 영역입니다. 윈도우즈가 내부적으로 사용하며 아주 특수한 목적에 사용되는 여분의 공간입니다. 예약 영역을 사용하지 않을 경우는 0으로 지정합니다.

    • hInstance

      이 윈도우 클래스를 등록하는 프로그램의 번호이며 WinMain의 인수로 전달된 hInstance값을 그대로 대입하면 됩니다. 운영체제는 이 윈도우 클래스를 누가 등록했는지 기억해 두었다가 프로그램이 종료될 때 등록을 취소합니다.

    • hIcon, hCursor

      이 윈도우가 사용할 마우스 커서와 아이콘을 지정합니다. LoadCursor 함수와 LoadIcon 함수를 사용하여 커서, 아이콘을 읽어와 이 멤버에 대입하면 됩니다. 사용자가 직접 아이콘과 커서를 만들어 사용할수도 있지만 여기서는 윈도우가 디폴트로 제공하는 아이콘과 커서를 사용하고 있습니다. 커서는 기울어진 화살표 모양과 기본 아이콘을 가집니다.

    • hbrBackground

      윈도우의 배경 색상을 지정합니다. 좀 더 정확하게는 윈도우의 배경 색상을 채색할 브러시를 지정하는 멤버입니다. GetStockObject라는 함수를 사용하여 윈도우즈에서 기본적으로 제공하는 브러시를 지정하거나 아니면 COLOR_WINDOW같은 스스템 색상을 지정할 수도 있습니다. 지정할 수 있는 브러시에는 여러 가지 종류가 있지만 여기서는 가장 일반적인 흰색 브러시(WHITE_BRUSH)를 사용하였으며 윈도우의 배경이 흰색으로 칠해집니다.

    • lpszMenuName

      이 프로그램이 사용할 메뉴를 지정합니다. 메뉴는 프로그램 코드에서 실행중에 만드는 것이 아니라 리소스 에디터에 의해 별도로 만들어져 링크시에 합쳐집니다. 메뉴를 사용하지 않을 경우엔 이 멤버에 NULL을 대입합니다.

    • lpszClassName

      윈도우 클래스의 이름을 문자열로 정의합니다. 여기서 지정한 이름은 CreatWindow 함수에 전달되며 CreateWindow 함수는 윈도우 클래스에서 정의한 특성값을 참조하여 윈도우를 만듭니다. 어디까지나 이름일 뿐이므로 변수명이나 함수명을 만들 듯 자유롭게 정할 수 있습니다. 중간에 공백을 포함하거나 한글을 쓰는것도 가능합니다. 하지만 윈도우 클래스의 이름은 실행 파일의 이름과 일치시켜 작성하는 것이 보통이며 이 예제의 경우 전역변수 lpszClass에 "첫번쨰"를 대입한 후에 이 이 문자열을 윈도우 클래스 이름으로 사용하였습니다. 멤버의 수가 너무 많아 한 번에 다 익히기 힘들지만 이 중에서 제일 중요한 멤버는 윈도우 클래스의 이름을 정의하는 lpszClassName과 메시지 처리 함수를 지정하는 lpfnWndProc입니다. 나머지는 대부분 디폴트나 0,NULL 같은 값들을 주면 무난합니다. 윈도우 클래스 구조체를 정의한 후 RegisterClass함수를 호출하여 윈도우 클래스를 등록합니다.
      ATOM RegisterClass(CONST WNDCLASS *lpWndClass)
      RegisterClass 함수의 인수로 WNDCLASS 구조체의 번지를 전달합니다. 이런 특성을 가진 윈도우를 앞으로 사용하겠다는 등록 과정이며 운영체제는 이 윈도우 클래스의 특성을 잘 기억해놓습니다. WinMain의 앞부분을 보면 WNDCLASS 구조체의 각 멤버에 값을 대입하고 윈도우 클래스를 등록하는 코드가 있습니다. 단순한 대입문의 연속이지만 이 값들을 살펴보겠습니다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      WNDCLASS WndClass;
       
          g_hInst = hInstance;
          WndClass.cbClsExtra = 0;
          WndClass.cbWndExtra = 0;
          WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
          WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
          WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
          WndClass.hInstance = hInstance;
          WndClass.lpfnWndProc = WndProc;
          WndClass.lpszClassName = lpszClass;
          WndClass.lpszMenuName = NULL;
      cs

      가장 일반적인 윈도우를 만들 수 있는 무난한 값들을 대입했습니다. 여분 메모리는 쓰지 않고 흰색 배경에 커서와 아이콘은 운영체제가 제공하는 것을 사용했으며 메뉴는 사용하지 않습니다. 이 윈도우 클래스를 등록한 프로그램은 hInstance이며 전달되는 메시지는 WndProc 함수가 처리하도록 했습니다. WNDCLASS는 멤버가 좀 많은 구조체일 뿐입니다. 그래서 다음과 같이 선언과 동시에 초기화하는 것도 가능합니다.


윈도우 생성
  

1
HWND CreateWindow(lpszClassName, lpszWindowName, swStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam)
cs

윈도우라는 것이 그다지 간단한 것이 아닙니다. 보다시피 인수가 많습니다. 윈도우즈 API함수들은 대체로 인수가 많습니다. 각 인수의 의미를 알아보겠습니다.

    • lpszClassName

      생성하고자 하는 윈도우의 클래스를 지정하는 문자열입니다. CreateWindow 함수는 윈도우 클래스에 정의된 속성대로 윈도우를 생성합니다. 앞에서 등록한 WNDCLASS 구조체의 lpszClassName 멤버의 이름을 여기에 기입합니다. 우리의 예제에서는 lpszClass 전역변수에 윈도우 클래스 이름을 기억시켜 두었으므로 이 문자열을 그대로 넘기면 됩니다.

    • lpszWindowName

      윈도우의 타이틀 바에 나타날 문자열입니다. 여기서 지정한 문자열이 윈도우의 타이틀 바에 나타납니다. 프로그래머가 마음대로 지정할 수 있는데 이 책에서는 예제의 프로젝트명(lpszClass 전역 문자열)을 타이틀 바에 표시하고 있습니다. lpszClass가 "첫번쨰"로 정의되어 있으므로 윈도우의 타이틀 바에도 "첫번째"라는 문자열이 표시됩니다.

    • dwstyle

      만들고자 하는 윈도우의 형태를 지정하는 인수입니다. 일종의 비트 필드값이며 거의 수십 개를 해아리는 매크로 상수들이 정의되어 있고 이 상수들을 OR연산자로 연결하여 윈도우의 다양한 형태를 지정합니다. 크기 조절이 가능하도록 할 것인가, 타이틀 바를 가질것인가, 또는 스크롤 바의 유무 등등을 세세하게 지정할 수 있습니다. 가능한 스타일값에 관한 자세한 내용은 앞으로 천천히 알아볼 것이되 일단은 WS_OVERLAPPEDWINDOW스타일을 사용하면 가용하면 가장 무난한 윈도우가 됩니다. 즉 시스템 메뉴, 최대 취소 버튼, 타이틀 바, 경계선을 가진 윈도우를 만드는데 메모장과 비슷한 표준 윈도우라고 생각하면 됩니다.

    • X, Y, nWidth, nHeight

      인수의 이름이 의미하듯이 윈도우의 크기와 위치를 지정하며 픽셀 단위를 사용합니다. 이 값을 100,100,640,480으로 주면 화면의 (100,100)위치에 폭 640픽셀 높이 480 픽셀 크기로 윈도우가 만들어 집니다. 정수값을 바로 지정해도 되며 CW_USERDEFAULT를 사용하면 운영체제가 화면 크기에 맞게 적당한 크기와 위치를 알아서 설정합니다. 예제에서는 모두 CW_USEDEFAULT를 사용했습니다.

    • hWndParent

      부모 윈도우가 있을 경우 부모 윈도우의 핸들을 지정합니다. MDI 프로그램이나 팝업 윈도우는 윈도우끼리 수직적인 상하관계를 가져 부자(parent-child)관계가 성립되는데 이 관계를 지정하는 인수입니다. 부모 윈도우가 없을 경우, 즉 자신이 최상위 윈도우일 경우는 이 값을 NULL로 지정하는데 이렇게 하면 데스크 탑을 부모로 가져 바탕화면 어디나 돌아다닐 수 있는 윈도우가 됩니다.

    • hmenu

      윈도우에서 사용할 메뉴의 핸들을 지정합니다. 윈도우 클래스에도 메뉴를 지정하는 멤버가 있는데 윈도우 클래스의 메뉴는 그 윈도우 클래스를 기반으로 하는 모든 윈도우에서 공통적으로 사용되는 반면 이 인수로 지정된 메뉴는 현재 CreateWindow 함수로 만들어지는 윈도우에서만 사용됩니다. 윈도우 클래스에서 지정한 메뉴를 그대로 사용하려면 이 인수를 NULL로 지정하며 다른 메뉴를 사용하려면 이 인수에 원하는 메뉴 핸들을 지정합니다. 위의 소스의 경우는 윈도우 클래스에도 메뉴가 지정되어있지 않고 CreateWindo 함수에서도 메뉴를 지정하지 않았으므로 메뉴없는 윈도우가 만들어집니다.

    • hinst

      윈도우를 만드는 주체 즉 프로그램의 핸들을 지정합니다. WinMain의 인수로 전달된 hInstance를 대입하면 됩니다. 운영체제는 누가 윈도우를 만들어는지 기억해 두었다가 프로그램이 종료될 때 파괴되지 않은 윈도우를 자동으로 파괴합니다.

    • lpvParam

      CREATESTRUCT라는 구조체의 번지이며 여러 개의 윈도우를 만들 떄 각 윈도우에 파라메터를 전달하는 특수한 목적에 사용됩니다. 보통은 NULL값을 사용하며 잘 사용되지 않으므로 일단은 무시하겠습니다.

지금 당장 CreateWindow의 모든 인수에 대해 다 외울필요는 없고 예제를 통해서 의미를 알아보고 가겠습니다.

BOOL ShowWindow(hWnd,nCmdSHow);
hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며 CreateWindow 함수가 리턴한 핸들을 그대로 넘기면 됩니다. nCmdShow는 윈도우를 화면에 출력하는 방법을 지정하며 다음과 같은 매크로 상수들이 정의되어 있습니다.

매크로 상수 

 의미

 SW_HIDE

 윈도우를 숨긴다.

 SW_MINIMIZE

 윈도우를 최소화하고 활성화시키지 않습니다.

 SW_RESTORE

 윈도우를 활성화합니다.

 SW_SHOW

 윈도우를 활성화하여 보여줍니다.

 SW_SHOWNORMAL

 윈도우를 활성화하여 보여줍니다.

nCmdShow 인수에 어떤 값을 넘겨줄 것인가는 전혀 고민할 필요가 없으며 WinMain 함수의 인수로 전달된 nCmdShow를 그대로 전달하면 됩니다. 이 값은 프로그램을 실행시킨 쉡로부터 전달된 것이며 이는 곧 사용자가 프로그램 등록 정보 대화상자에서 지정한 값입니다. 그래서 ShowWindow(hWnd, nCmdShow);와 같이 거의 호출형식이 정해져 있는 셈입니다. 설명이 길지만 윈도우를 만들고 화면에 나타내는 코드는 다음 두줄 입니다.

1
2
3
hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, (HMENU)NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
 
cs

윈도우를 만드는 과정을 간단히 하면 다음과 같이 나오게됩니다.

WndClass 정의(윈도우 속성 정의) -> RegisterClass(윈도우 클래스 등록) -> CreateWindow(메모리상에 윈도우 생성) -> ShowWindow(윈도우를 화면에 표시) -> 메시지 루프(사용자로부터의 메시지를 처리)



메세지 루프

윈도우즈를 메시지 구동 시스템(Message Driven System)이라고 하는데 이 점이 도스와 가장 뚜렷한 대비를 이루는 윈도우즈의 특징입니다. 도스에서는 프로그래머에 의해 미리 입력된 일련의 명령들을 순서대로 실행하는 순차적 방법을 사용합니다. 윈도우즈는 이와 다르게 프로그램의 실행 순서가 명확하게 정해져 있지 않으며 상황에 따라 실행 순서가 달라지는데 여기서 말하는 상황이란 바로 어떤 메시지가 발생했는가를 의미합니다.

메세지란 사용자나 시스템의 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보입니다. 예를 들어 사용자가 마우스 버튼을 클릭했다거나 키보드를 눌렀다거나 최소화가 되었다거나 하는 변화에 대한 정보들이 메세지입니다. 메세지를 받은 프로그램은 메세지가 어떤 정보를 담고 있는가를 분석하여 무슨 동작을 할 것인가를 결정합니다. 순서를 따르지 않고 주어진 메시지에 대한 반응을 정의하는 방식으로 프로그램이 실행됩니다. 윈도우즈 프로그램에서 메시지를 처리하는 부분을 메시지 루프(Message Loop)라고 하며 보통 WinMain 함수의 끝에 다음과 같은 형식으로 존재합니다. 메인 윈도우를 만든 직후 WinMain 함수의 끝에 다음과 같은 형식으로 존재합니다. 메인 윈도우를 만든 직후 WinMain은 메세지 루프를 실행합니다.

1
2
3
4
5
while (GetMessage(&Message, NULL, 00))
{
        TranslateMessage(&Message);
       DispatchMessage(&Message);
}
cs

메세지 루프는 세 개의 함수 호출로 이루어져 있으며 전체 루프틑 while문으로 싸여져 있어 무한히 반복되는 구조를 가지고 있습니다. 메시지 루프는 윈도우즈의 멀티태스킹을 지원하는 굉장히 중요한 역할을 하며 따라서 윈도우즈를 이해하기 위한 핵심 루프입니다. 자세한건 뒤에서 하고 각 함수가 어떤 동작하는지 알아보겠습니다. 여기서는 각 함수가 어떤 동작을 하는지 대충 알아보겠습니다.


BOOL GetMessage(LPMSG, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);


이 함수는 메세지 큐에 메시지를 읽어 들입니다. 메세지 큐(Message Queue)는 시스템이나 사용자로 부터 발생된 메시지가 잠시 대기하는 일종의 메시지 임시 저장 영역입니다. 읽어드린 메세지가 프로그램을 종료하라는 WM_QUIT일 경우 FALSE를 리턴하며 그 외의 메세지이면 TRUE를 리턴합니다. 따라서 WM_QUIT 메시지가 읽혀질 때까지 즉 프로그램이 종료될 떄까지 전체 while 루프가 계속 반복됩니다. 나머지 세 개의 인수는 읽어들일 메세지의 범위를 지정하는데 잘 사용되지 않으므로 우선 무시하겠습니다


BOOL TranslateMessage(CONST MSG *lpMsg);

이 함수는 키보드 입력 메세지를 가공하여 프로그램에서 쉽게 쓸 수 있도록 합니다. 윈도우즈는 키보드의 어떤 키가 눌러지거나 떨어졌을 때 키보드 메시지를 발생시키는데 TranslateMessage 함수는 키보드의 눌림(WM_KEYDOWN) 메세지가 발생할 때 문자가 입력되었다는 메세지(WM_CHAR)를 만드는 역할을 합니다. 예를들어 A를 누르면 A문자가 입력되었다는 메세지를 만들어 냅니다.

LONG DispatchMessage(CONST MSG *lpmsg);

이 함수는 메시지 큐에서 꺼낸 메세지를 윈도우의 메세지 처리 함수(WndProc)로 전달합니다. 이 함수에 의해 메세지가 윈도우로 전달되며 프로그램(정확히는 윈도우 프로시저)에서는 전달된 메세지를 점검하여 다음 동작을 결정합니다. 이 함수가 메시지를 전달하면 다시 루프의 선두로 돌아가 다음 메세지를 기다립니다.

메세지 루프에서 하는 일은 메세지를 꺼내고, 필요한 경우 약간 형태를 바꾼 후 응용 프로그램으로 전달 하는 것 뿐입니다. 이 과정은 WM_QUIT메세지가 전달될 때까지, 즉 프로그램이 종료될 떄까지 반복됩니다. 결국 메세지 루프가 하는 일이란 메세지 큐에서 메세지를 꺼내 메세지 처리 함수로 보내는 것 뿐입니다. 실제 메세지 처리는 별도의 메세지 처리 함수(WndProc)에서 수행합니다. 메세지 루프의 세 함수는 공통적으로 MSG라는 구조체를 사용하는데 이 구조체는 메세지에 대한 정보를 정의합니다. MSG 구조체는 다음과 같이 정의되어있습니다.

1
2
3
4
5
6
7
8
9
typedef struct tagMSG
{
    HWND            hwnd;
    UINT                message;
    WPARAM        wParam;
    LPARAM            lParam;
    DWORD            time;
    POINT                pt;
}MSG;
cs

각 멤버의 의미는 다음표와 같습니다.

 멤버 

 의미

 hwnd

 메세지를 받을 윈도우 핸들입니다.

 message

 어떤 종류의 메세지인가를 나타냅니다. 가장 중요한 값입니다.

 wParam

 전달된 메세지에 대한 부가적인 정보를 가집니다. 어떤 의미를 가지냐는 메세지 병로 다릅니다(32비트 값)

 lParam

 전달된 메세지에 대한 부가적인 정보를 가집니다. 어떤 의미를 가지냐는 메세지 병로 다릅니다(32비트 값)

 time

 메세지가 발생한 시간입니다.

 pt

 메세지가 발생했을 때의 마우스 위치입니다.


message 멤버를 읽음으로써 메세지의 종류를 파악하며 message값에 따라 프로그램의 반응이 달라집니다. wParam, lParam은 메세지에 대한 부가적인 정보를 가지되 메세지별로 의미가 다릅니다. GetMessage함수는 읽은 메세지를 MSG형의 구조체에 대입하여 이 구조체는 DispatchMessage 함수에 의해 응용프로그램의 메세지 처리 함수(WndProc)으로 전달됩니다. 메세지는 실제로 하나의 정수값으로 표현되는데 종류가 무척 많아 메세지 번호를 일일이 암기하여 사용할 수 없으므로 windows.h에 메시지 별로 매크로 상수를 정의해 두었으며  접두어 WM_으로 시작됩니다. 자주 사용되는 메세지 몇개만 표로 알아보겠습니다.



 메세제

 의미

 WM_QUIT

 프로그램을 끝낼 때 발생하는 메세지입니다.

 WM_LBUTTONDOWN

 마우스의 좌측 버튼을 누를 경우 발생합니다.

 WM_KEYDOWN

 키보드의 키를 누를때 발생합니다.

 WM_CHAR

 키보드로부터 문자가 입력될 때 발생합니다.

 WM_PAINT

 화면을 다시 그려야 할 필요가 있을 때 발생합니다.

 WM_CREATE

 윈도우가 처음 만들어질 때 발생합니다.

 WM_DESTORY

 윈도우가 메모리에서 파괴될 때 발생합니다.


메세지 루프가 종료되면 프로그램은 마지막으로 Message.wParam을 리턴하고 종료합니다. 이 값은 WM_QUIT 메세지로부터 전달된 탈출 코드(exit code)이며 이 프로그램을 실행시킨 운영체제로 리턴됩니다. 도스에서 사용하는 탈출 코드와 동일한 의미를 가지며 사용되는 경우가 거의 없습니다.


'Programming > Windows API' 카테고리의 다른 글

WinApi - 출력(2/2)  (0) 2015.08.29
WinApi - 출력(1/2)  (0) 2015.08.27
WinApi - 분석(2/2)  (0) 2015.08.26
WinAPI - 첫번째 시작 - 프로젝트 만들기  (0) 2015.08.25
WinAPI - 시작하기전에 알고 갑시다.  (0) 2015.08.24