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 2020. 11. 30. 22:00

WM_CHAR 메시지

도스(콘솔) 환경에서는 getch, gets, scanf 처럼 명시적으로 입력을 받는 함수들이 따로 있다.

위 입력 함수들은 사용자로부터 입력이 완료될 때까지 프로그램의 실행은 잠시 중단된다.

이는 싱글 태스킹 환경에서는 아무 문제 없지만, 멀티태스킹의 윈도우즈 환경에서는 이런 명시적인 입력 함수가 존재하지 않으며 반드시 메시지를 받아야 한다.

 

윈도우즈는 포커스(focus)를 가진 프로그램에게 키보드 메시지(WM_CHAR, WM_KEYDOWN)를 보내며 프로그램은 이런 메시지를 받아 키보드 입력을 처리한다.

가령 아무 대화상자나 열어 Tab키를 눌러가면서 포커스를 이동해보면 강조표시가 이동하는것을 볼 수 있다.

한번에 하나의 프로그램만 키보드 입력을 받을 수 있는 이유는 간단하다. 시스템에 키보드는 하나뿐이며 키보드를 사용하는 주체인 사용자도 오직 한명 뿐이기 때문이다.

다음은 키보드로부터 입력된 문자들을 화면으로 출력하는 예제이다.

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
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Key");
 
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, 000)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static TCHAR str[256];
    int len;
    switch (iMessage) {
    case WM_CHAR:
        len = wcslen(str);
        
        str[len] = (TCHAR)wParam;
        str[len + 1= 0;
        InvalidateRect(hWnd, NULL, TRUE);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 100100, str, wcslen(str));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

 

키보드에서 키를 누르면 입력한 문자들이 화면 상단에 출력된다.

WndProc을 보면 WM_CHAR내에서 입력값은 wParam을 TCHAR타입으로 str[len]에 저장하고 있으며, 마지막에 InvalidateRect를 호출로 윈도우창을 업데이트 하도록 하여 WM_PAINT내부에 진입되도록 하였다.

문자를 입력받을때마다 출력하는게 아니라 문자열들을 계속 모으는 이유는 윈도우즈 프로그램에서는 모든 출력을 WM_PAINT에서 하도록 되어 있으므로 화면이 가려저서 지워질때를 대비하여 항상 문자열들을 모아둬야한다.

 

lParam은 wParam보다 복잡한 정보가 비트별로 전달된다.

 

 

 

 

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
73
74
75
76
77
78
79
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Key");
 
LPCTSTR clr = 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)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, 000)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static TCHAR str[50];
    int input;
    int mask = 1;
    int ret = 0;
    switch (iMessage) {
    case WM_CHAR:
        
        input = (int)lParam;
        
        for (int i = 0; i < 32; i++) {
            ret = mask & input;
            if (ret == 0)
                str[i] = '0';
            else
                str[i] = '1';
 
            mask = mask << 1;
        }
 
        str[32= 0;
 
        InvalidateRect(hWnd, NULL, FALSE);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 100100, str, 33);
 
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

불완전 하지만 lParam의 값을 비트단위로 확인할 수 있는 코드이다.

 

윈도우즈용 프로그램은 입력받는 부분과 출력하는 부분이 따로 분리되어 있다. 출력에 필요한 모든 정보는 따로 저장해 두고 WM_PAINT 에서 일괄적으로 출력해야 한다. 이런 입력과 출력의 특수성 때문에 도스나 콘솔 환경에 비해 윈도우즈 환경에서 C언어를 배우는것은 어렵다.

 

 


무효영역

앞선예제에서 InvalidRect 함수를 호출하여 강제로 MW_PAINT 메시지를 발생시켜 화면을 새로 그리게끔 하였다.

WM_PAINT 메시지는 윈도우가 다시 그려져야 할 필요가 있을 때마다 호출되는데 다시 그려져야 할 필요가 잇다는 말은 무효영역(Invalid Region)이 있다는 뜻이다. 그렇다면 무효하다는 말이 과연 무슨 의미인지 알아보기 위해 다음 경우를 보자. 계산기 프로그램과 메모장 프로그램이 겹쳐져 있으며 계산기 프로그램의 화면 영역 일부가 메모장에 의해 가려져 있다.

이 상태에서 메모장이 자리를 옮겨 계산기의 가려젔던 부분이 드러나면 이 부분이 무표영역이 된다.

즉 무효하다는 말은 원래 그려져야 할 모습과는 다른 모습을 가지고 있다는 뜻이며 곧 다시 그리리 필요가 있다는 뜻이다.

OS는 윈도우가 무효영역을 가지고 있으면 이 윈도우에게 WM_PAINT 메시지를 보내 다시 그려 전 영역이 유효영역이 되게끔 한다. 

 

하지만 앞선 예제처럼 내부적인 변화에 의해 화면이 다시 그려져야 할 때는 OS가 작업영역을 무효화하지 않는다. 왜냐하면 입력받은 문자를 화면으로 출력할것인지, 내부적인 계산에만 사용할 것인지 네트워크나 DB로 전송하것인지 OS가 판단할 수 없기 때문이다. 그래서 프로그램의 내부에서 윈도우의 모습을 변경 시켰을 때는 변경된 부분을 다시 그리도록 강제로 무효화해야 하며 이때 사용되는 함수가 바로 InvalidateRect이다.

BOOL InvalidateRect( HWND hWnd, const RECT *lpRect, BOOL bErase );

이 함수는 윈도우의 작업 영역을 무효화 하여 운영체제로 하여금 WM_PAINT 메시지를 해당 윈도우로 보내도록한다.

매개변수 기능
hWnd $\bullet$ 무효화의 대상이 되는 윈도우의 핸들
$\bullet$ 자기자신을 무효화 할거면 WndProc으로 전달되는 첫번째 인수 hWnd를 그대로 쓴다.
lpRect $\bullet$ 무효화할 사각영역을 지정하되 이 값이 NULL이면 윈도우의 전 영역이 무효화 된다.
$\bullet$ 무효화할 영역에 따른 성능차이가 존재한다.
bErase 무효화되기 전에 배경을 모두 지운후 다시 그릴것인지 아니면 배경을 지우지 않고 그릴것인지를 지정한다.
$\bullet$ true : 배경을 지운 후 그린다.
$\bullet$ false : 배경을 안지우고 그린다.

 

 

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
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Key");
 
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, 000)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static TCHAR str[256];
    int len;
    switch (iMessage) {
    case WM_CHAR:
 
        if ((TCHAR)wParam == ' ') {
            str[0= 0;
        }
        else {
            len = wcslen(str);
 
            str[len] = (TCHAR)wParam;
            str[len + 1= 0;
        }
        
        InvalidateRect(hWnd, NULL, TRUE);       
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 100100, str, wcslen(str));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

앞의 코드와 달라진점은 space를 눌렀을때 str을 초기화 시키는 기능을 추가한 점에 있다.

InvalidateRect의 3번째 매개변수를 TRUE로 만듦으로써 기존에 작성되었던 내용을통째로 수정하게끔 하였으며 FALSE로 바꾸면 기존에 있던 화면과 새로 시작하는 문자열이 겹처저서 보이는것을 확인할 수 있다.

InvalidateRect함수를 주석처리 할경우에는 어떤 키보드 입력에도 화면에 출려되는 문자열이 변함 없음을 보이지만 화면밖으로창을 뺏다가 다시 되돌려 놓으면 화면에 출력되는 내용이 변한것을 볼 수 있다. 화면이 가려짐으로써 OS가  WM_PAINT메시지를 윈도우에 보낸것이다. (Windows10 환경에서는 다른 윈도우에 의해 가려지는 정도로는 WM_PAINT 메시지가 보내지지 않았음을 확인 하였다.)


WM_KEYDOWN

WM_CHAR 메시지는 문자 이외의 키는 입력받을 수 없다. 가령 커서 이동키나 Ins, Del, PgUp, 펑션키 등의 키는 문자키가 아니므로 WM_CHAR 메시지가 전달되지 않는다.

이때는 WM_KEYDOWN 메시지를 사용해야 한다.

WM_KEYDOWN 메시지는 키보드를 누를 때마다 윈도우로 전달되는데 문자가 아닌 모든 키에 대해서도 발생한다.

단 Alt키와 윈도우키, 한영 전환키 등의 특수 키 몇가지는 제외된다.

이때 wParam 으로는 문자코드가 아닌 가상 키코드(virtual key code) 라는 것이 전달된다.

숫자 및 영문자의 가상키코드는 아스키코드와 같으며 매크로 상수는 정의되어 있지 않으므로문자 상수와 wParam을 바로 비교하면된다.

 

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
73
74
75
76
77
78
79
80
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("KeyDown");
 
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, 000)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    static BOOL toggle = false;
    static TCHAR symbol[2= TEXT("A");
    HDC hdc;
    PAINTSTRUCT ps;
    static int x = 100;
    static int y = 100;
    switch (iMessage) {
    case WM_KEYDOWN:
        switch (wParam) {
        case VK_LEFT:
            x -= 8;
            break;
        case VK_RIGHT:
            x += 8;
            break;
        case VK_UP:
            y -= 13;
            break;
        case VK_DOWN:
            y += 13;
            break;
        case VK_SPACE:
            symbol[0= toggle ? 'A' : '#';
            symbol[1= 0;
            toggle = !toggle;
            break;
        }
        InvalidateRect(hWnd, NULL, TRUE);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, x, y, symbol, 1);
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

위 프로그램을 실행하면 문자 $A$나 #이 방향키에 맞추어 윈도우상에서 움직이는것을 볼 수 있다. 

스페이스를 누름으로써 출력되는 메시지가 토글되게끔 하였다.

만약 InvalidateRect의 매개변수를 FALSE로 설정한다면 A가 길게 꼬리를 그리는듯한 모습을 그리는것을 확인 할 수 있다.

WM_KEYDOWN메시지를 통해 키가 놓아질때마다 메시지가 전달되게끔 할 수 있다.

 


TranslateMessage

 

키보드에서 $A$키를 눌렀다 떼면 이 때 발생하는 메시지는 순서대로 WM_KEYDOWN, WM_CHAR, WM_KEYUP 이 3가지이다. 이 중 WM_CHAR는 사용자에 의해 발생하는 메시지가 아니다. 키보드로부터 전달되는 메시지는 WM_KETDOWN과 WM_KEYUP 2가지 뿐이다. 그럼 WM_CHAR는 어디서 발생할까? 이 메시지는 WM_KETDOWN에 의해 추가로 발생하는 메시지이며, 메시지 루프에서 인위적으로 생성된다.

while (GetMessage(&Message, 0, 0, 0)) {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
}

$GetMessage$는 메시지 큐에서 메시지를 꺼낸 후 이 메시지를 TranslateMessage 함수로 넘긴다. TranslateMessage 함수는 전달딘 메시지가 WM_KEYDOWN인지, 눌러진 키가 문자키 인지 검사하여 조건이 맞을경우 WM_CHAR 메시지를 추가로 발생시킨다. TranslateMessage 함수는 오로지 키보드로부터 문자키 입력 메시지은 WM_CHAR를 만들어내기 위해 존재하며, 경우에 따라서는 생략해도 상관없다.

그렇다면 대체 이게 무슨 의미를 지니는가? 우리가 문자를 받을때 똑같이 a키를 누르더라도 shift 누름 여부, caps lock 및 한/영키 여부에 따라 실질적으로 출력되는 값은 달라진다. 영어의 경우는 대,소문자 의 차이또한 존재할것이다. 이런 여러가지 처리를 내부적으로 처리하여 결과적으로 어떤 값이 입력되었는지를 알아보는데에 편리하게 해주는 역할을 해준다. 즉, WM_KEYDOWN은 'R' 버튼이 눌러젔다를 판단하는 반면 WM_CHAR는 'R'를 누름으로써 나오는 문자가 ㄱ, ㄲ, r, R 중 무엇인지를 자동으로 판단 해주는데에 있다.

 

이외에 WM_SYSKEYDOWN, WM_SYSKEYUP, WM_SYSCHAR등의 시스템 키보드 메시지가 있고, WM_DEADCHAR, WM_SYSDEADCHAR 등 유럽의 악센트 문자를 위한 메시지도 있다.

 

 

 

WM_CHAR와 WM_KEYDOWN

 

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
#include <windows.h>
#include <tchar.h>
 
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Menu");
 
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, 000)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static int wm_keydown = 0, wm_char = 0;
    switch (iMessage) {
    case WM_KEYDOWN:
        wm_keydown++;
        InvalidateRect(hWnd, NULL, FALSE);
        return 0;
    case WM_CHAR:
        wm_char++;
        InvalidateRect(hWnd, NULL, FALSE);
        return 0;
        
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        SetTextAlign(hdc, TA_LEFT);
        TCHAR wm_keydown_str[30];
        _stprintf(wm_keydown_str, _T("WM_KEYDOWN : %d"), wm_keydown);
        TextOut(hdc, 20060, wm_keydown_str, _tcslen(wm_keydown_str));
        TCHAR wm_char_str[30];
        _stprintf(wm_char_str, _T("WM_CHAR : %d"), wm_char);
        TextOut(hdc, 20080, wm_char_str, _tcslen(wm_char_str));
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
cs

화면중앙에서 WM_KEYDOWN 메시지와 WM_CHAR 메시지가 출력된 횟수를 볼 수 있다.

CTRL, ALT, SHIFT function number 키등을 누를때만 wm_keydown이 상승하며, 나머지는 WM_KEYDOWN과 WM_CHAR가 동시에 오르는것을 볼 수 있다.

이 점에서 실제 출력이 발생하지 않는 몇가지 특수키를 제외하면 WM_KEYDOWN과 WM_CHAR 메시지는 입력에 대해 함께 윈도우에 보내지는것을 알 수 있다.

 

 

  요약

  • 윈도우는 포커스(focus)를 가진 프로그램에게 사용자의 입력(키보드, 마우스...)에 의해 발생한 메시지들을 보낸다.
  • WM_PAINT : 윈도우에서 무효영역(invalid region)이 감지되었을때 전송되는 메시지이다.
  • 무효영역 : 윈도우에서 다시 그려저야할 필요가 있는 영역
  • invalidateRect : 윈도우에서 무효영역을 강제로 생성하여 WM_PAINT 메시지의 발생을 유도시키는 함수
  • WM_KEYDOWN
    • TranslateMessage 함수를 통해 전달되는 메시지
    • wParam 을 통해 가상키코드(Virtual Key Code)가 전달됨
  • WM_CHAR
    • DispatchMessage 함수를 통해 전달되는 메시지
    • 키보드 조합을 통해 사용자가 어느 문자를 입력했는지 전달

 

 

 

 

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

4. 타이머, 콜백함수  (0) 2021.01.10
4. 입력 - 마우스 입력  (0) 2021.01.10
3. 메시지 비프  (0) 2020.11.30
3. 메시지 박스 (Message Box)  (0) 2020.11.23
3. 그래픽 출력  (0) 2020.11.19
Comments