Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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
Archives
Today
Total
관리 메뉴

codingfarm

4. 윈도우 관리 메시지 본문

Windows/윈도우즈 API

4. 윈도우 관리 메시지

scarecrow1992 2021. 4. 29. 00:29
  • 우리는 앞선 과정에서 수차례 메시지를 사용했다.
  • 앞서 메시지가 생성되는 조건은 2가지라고 배웠다
    • 사용자의 입력에 의해
    • 시스템이 변화를 감지했을때
  • 메시지는 윈도우에 들어오는 여러가지 상황 변화를 통보하기 위해 쓰여진다.
    • 상황변화 : 명시적인 입력이 될 수도 있고, 윈도우에 관한 기본적인 관리도 포함된다.
  • 윈도우에 들어오는 여러가지 메시지에 대해 알아보겠다.

 


1. 생성 및 파괴

  • WM_CREATE와 WM_DESTROY는 윈도우가 생성 및 파괴 댈때 보내지는 메시지이다.
  • 각 메시지들은 윈도우 생성시 해야할 일회적인 초기 및 종료 처리에 사용할 수 있다.

가령, 윈도우에서 사용하기 위한 1M의 메모리를 동적으로 할당하고자 하면 아래의 방법을 사용할 수 있다.

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
47
48
49
50
51
52
53
54
55
56
57
#include <windows.h>
#include<vector>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("WmCreate");
TCHAR* mem;
 
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)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);
    // mem = (TCHAR *)malloc(1048576);
 
    while (GetMessage(&Message, NULL00)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    // free(mem);
 
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    switch (iMessage) {
    case WM_CREATE:
        mem = (TCHAR*)malloc(1048576);
        return 0;
 
    case WM_DESTROY:
        free(mem);
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

WM_CREATE 메시지를 받을 경우 동적할당을 통해 메모리를 확보하며, WM_DESTROY 메시지를 받으면 동적 메모리를 해제하는 코드이다.

물론 이런 방법 이외에 WinMain 함수 내에서 주석으로 처리해놓은것 처럼 window 함수 외부에서 처리하는것도 가능하다.

하지만, 2개의 방법이 보여주는 결과가 같더라도 그 의미는 명백하게 다르다.

  1. 작업을 처리하는 타이밍이 다르다.
    • 메시지 : 윈도우가 생성중, 파괴중일때 처리
    • WinMain 내부 : 윈도우의 생성 후, 파괴 후에 작업을 하게 된다.
  2. 하나의 프로그램에 여러개의 윈도우가 존재할 수 있는데, 모든 윈도우에 대한 생성, 파괴 처리 작업을 main에서 작성하면 스파게티 코드가 된다.
    • 메시지 : 윈도우에 관련된 초기/종료 처리
    • WinMain 내부 : 프로그램 전역적인 초기/종료 처리

 

주의

윈도우의 경우 WM_DESTROY 메시지에 대해서 반드시 PostQuitMessage 함수를 호출해야한다.

이 함수를 호출해야 WinMain의 메시지 루프가 종료되어 프로그램을 완전히 종료할 수 있게된다.

만약 PostQuitMessage 함수를 제외한다면 창을 종료시키더라도 컴퓨터 내부에서는 여전히 프로그램을 작동중이며, 실제로 작업 관리자를 통해 확인할 수 있다. 그러므로 재컴파일을 하더라도 에러 메시지가 뜨면서 프로그램 실행이 실패하게된다.

다른 메시지는 윈도우창 외부에서 처리하는 일이 있더라도, 최소한 WM_DESTROY 메시지는 꼭 처리해야한다.

 

 


2. 작업영역(Client Area)

  • 윈도우는 크게 2가지 영역으로 구성된다
    • 작업영역(Client Area)
      • 일반적인 프로그래밍 영역
        • 프로그래머에 의해 관리
      • 윈도우 중앙의 흰부분
    • 비작업영역(Non Client Area)
      • 특수한 프로그래밍 영역
        • OS에 의해 관리 : 윈도우의 이동 및 크기 조절...
      • 타이틀바, 경계선, 메뉴 등의 영역

 

일반적으로 윈도우의 좌표 원점은 윈도우의 좌상단 모서리가 아닌, 작업영역에서의 좌상단에서 시작한다.

 

작업영역의 크기를 구하는 함수는 GetClientRect이다.

BOOL GetClientRect( HWND hWnd, LPRECT lpRect );

첫번째 매개변수가 input, 두번째 매개변수가 output이다.

lpRect의 left,top은 항상0으로 고정되며 right와 bottom을 통해 너비와 높이를 구할 수 있다.

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <windows.h>
#include<vector>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Client");
TCHAR* mem;
 
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)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, NULL00)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
 
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    static RECT rt;
 
    switch (iMessage) {
    case WM_CREATE:
        return 0;
    case WM_PAINT:
        GetClientRect(hWnd, &rt);
        hdc = BeginPaint(hWnd, &ps);
        SetTextAlign(hdc, TA_CENTER);
        TextOut(hdc, rt.right / 2, rt.bottom / 2, TEXT("Center String"), 13);
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

아래 그림처럼 윈도우의 창 크기가 변하더라도, Center String 문자열은 항상 중앙에 위치하는것을 볼 수 있다.

 

 


3. WM_SIZE

  • 앞선 과정에서, 우리는 WM_PAINT 메시지가 오면 창의 크기를 통해 중앙좌표를 구하여 해당 위치에 문자열을 출력하게 하였다. 이는 창의 크기가 변하면 화면을 새로 그리기 위해 WM_PAINT 메시지가 보내지는것을 이용한 방법이다.
  • 하지만, 이는 화면을 그리는 기준이 "창의 크기가 변할때" 가 아니라 "그림을 새로 그려야 할 때" 이므로 그 조건이 명확하지 않다.
  • 또한 WM_PAINT가 호출될 때마다 창의 크기가 변한다는 보장이 없는데, 매번 창의 크기를 구하고 출력 위치를 조절함으로써 자원의 손실이 발생한다.

 

WM_SIZE

  • 윈도우의 크기가 변경될 때마다 전송되는 메시지
  • lParam : 하위 워드에 변경된 폭이, 상위 워드에는 높이가 전달
  • wParam : 이 메시지가 발생한 이유를 나타내는 플래그가 전달
플래그
SIZE_MAXHIDE 다른 윈도우가 최대화되어 이 윈도우가 가려졌다.
SIZE_MAXIMIZED 최대화 되었다.
SIZE_MAXSHOW 다른 윈도우가 원래 크기로 복구되어 이 윈도우가 드러났다.
SIZE_MINIMIZED 최소화 되었다.
SIZE_RESTORED 크기가 변경되었다.

 

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
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    static RECT rt;
 
    switch (iMessage) {
    case WM_CREATE:
        return 0;
    case WM_SIZE:
        GetClientRect(hWnd, &rt);
        // rt.right = LOWORD(lParam);
        // rt.bottom = HIWORD(lParam);
        InvalidateRect(hWnd, NULL, TRUE);
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        SetTextAlign(hdc, TA_CENTER);
        TextOut(hdc, rt.right / 2, rt.bottom / 2, TEXT("Center String"), 13);
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

이전과 같은 효과를 볼 수 있다.

GetClientRect 호출부를 주석 처리하고, 아래 2줄의 주석을 풀어도 된다.

WM_SIZE에서는 좌표를 수정만 담당하고, InvalidateRect를 호출하여 WM_PAINT에서 그리기만 담당하게끔 하였다.

 

 


4. WM_MOVE

  • 윈도우의 위치가 변경될때마다 보내진다.
  • lParam의 하위 워드에 윈도우의 새 X좌표, 상위 워드에 윈도우의 새 Y좌표가 전달된다.

 

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <windows.h>
#include<vector>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Client");
 
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)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, NULL00)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
 
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    static RECT rt;
    static int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
 
    switch (iMessage) {
    case WM_CREATE:
        return 0;
    case WM_MOVE:
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        InvalidateRect(hWnd, NULL, TRUE);
    case WM_SIZE:
        GetClientRect(hWnd, &rt);
        // rt.right = LOWORD(lParam);
        // rt.bottom = HIWORD(lParam);
        InvalidateRect(hWnd, NULL, TRUE);
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        SetTextAlign(hdc, TA_CENTER);
       LCHAR str[128];
        wsprintf(str, L"x : %d, y : %d", x, y);
        TextOut(hdc, rt.right / 2, rt.bottom / 2, str, lstrlen(str));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

 화면 중앙에 창의 좌표가 실시간으로 출력된다.

 

참고

윈도우(window) : 화면에 나타나는 사각 영역

윈도우즈(windows) : 운영체제

 

 

 


요약

  • 메시지가 발생하는 조건
    • 사용자의 입력 발생
    • 시스템이 변화를 감지
  • 시스템 변화에 의해 발생하는 메시지의 예와 각각의 발생 조건은 아래와 같다.
    • WM_CREATE
      • 윈도우 생성시 (CreateWindow 함수 호출시)
    • WM_DESTROY
      • 윈도우의 파괴가 감지될 때
      • 이 메시지 전달시 반드시 PostQuitMessage가 호출되어야 한다. 그렇지 않으면 창은 종료되어도 실제 프로그램은 계속 메시지 처리 무한루프에 빠지게 되어 계속 작동됨을 작업관리자에서 확인할 수 있다.
    • WM_SIZE
      • 윈도우 창의 크기가 변경될때
    • WM_MOVE
      • 윈도우의 위치가 바뀔때
    • WM_PAINT
      • 무효영역이 감지되어 새로 그려저야 할때
      • ex : 윈도우를 내리거나 올릴때, 크기가 변할때, 옮길때, 다른 윈도우에 가려질때...

 

 

 

 

 

 

'Windows > 윈도우즈 API' 카테고리의 다른 글

5. 리소스 - 메뉴  (0) 2021.05.01
5. 리소스  (0) 2021.05.01
4. 타이머, 콜백함수  (0) 2021.01.10
4. 입력 - 마우스 입력  (0) 2021.01.10
4. 입력 - 키보드 입력  (0) 2020.11.30
Comments