일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 그래프
- Trie
- DP
- SQL
- union find
- binary search
- two pointer
- 스토어드 프로시저
- MYSQL
- Brute Force
- Hash
- Two Points
- 다익스트라
- Stored Procedure
- 이진탐색
- String
- Dijkstra
- Today
- Total
codingfarm
4. 윈도우 관리 메시지 본문
- 우리는 앞선 과정에서 수차례 메시지를 사용했다.
- 앞서 메시지가 생성되는 조건은 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, NULL, 0, 0)) {
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개의 방법이 보여주는 결과가 같더라도 그 의미는 명백하게 다르다.
- 작업을 처리하는 타이밍이 다르다.
- 메시지 : 윈도우가 생성중, 파괴중일때 처리
- WinMain 내부 : 윈도우의 생성 후, 파괴 후에 작업을 하게 된다.
- 하나의 프로그램에 여러개의 윈도우가 존재할 수 있는데, 모든 윈도우에 대한 생성, 파괴 처리 작업을 main에서 작성하면 스파게티 코드가 된다.
- 메시지 : 윈도우에 관련된 초기/종료 처리
- WinMain 내부 : 프로그램 전역적인 초기/종료 처리
주의
윈도우의 경우 WM_DESTROY 메시지에 대해서 반드시 PostQuitMessage 함수를 호출해야한다.
이 함수를 호출해야 WinMain의 메시지 루프가 종료되어 프로그램을 완전히 종료할 수 있게된다.
만약 PostQuitMessage 함수를 제외한다면 창을 종료시키더라도 컴퓨터 내부에서는 여전히 프로그램을 작동중이며, 실제로 작업 관리자를 통해 확인할 수 있다. 그러므로 재컴파일을 하더라도 에러 메시지가 뜨면서 프로그램 실행이 실패하게된다.
다른 메시지는 윈도우창 외부에서 처리하는 일이 있더라도, 최소한 WM_DESTROY 메시지는 꼭 처리해야한다.
2. 작업영역(Client Area)
- 윈도우는 크게 2가지 영역으로 구성된다
- 작업영역(Client Area)
- 일반적인 프로그래밍 영역
- 프로그래머에 의해 관리
- 윈도우 중앙의 흰부분
- 일반적인 프로그래밍 영역
- 비작업영역(Non Client Area)
- 특수한 프로그래밍 영역
- OS에 의해 관리 : 윈도우의 이동 및 크기 조절...
- 타이틀바, 경계선, 메뉴 등의 영역
- 특수한 프로그래밍 영역
- 작업영역(Client Area)
일반적으로 윈도우의 좌표 원점은 윈도우의 좌상단 모서리가 아닌, 작업영역에서의 좌상단에서 시작한다.
작업영역의 크기를 구하는 함수는 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, NULL, 0, 0)) {
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가 호출될 때마다 창의 크기가 변한다는 보장이 없는데, 매번 창의 크기를 구하고 출력 위치를 조절함으로써 자원의 손실이 발생한다.
- 윈도우의 크기가 변경될 때마다 전송되는 메시지
- 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, NULL, 0, 0)) {
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 : 윈도우를 내리거나 올릴때, 크기가 변할때, 옮길때, 다른 윈도우에 가려질때...
- WM_CREATE
'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 |