이번에는 입력에 의한 제어가 아닌

 

일정 시간 간격으로 메세지를 보내는 타이머 메세지이다.

 

1
2
3
4
5
6
UINT_PTR SetTimer(
    HWND hWnd,
    UINT_PTR nIDEvent,
    UINT uElapse,
    TIMERPROC lpTimerFunc
    );
cs

 

SetTimer() 함수를 이용한다.

 

nIDEvent : 타이머의 ID로 정수값 (여러개의 타이머를 쓸 경우 구분하기위함)

uElapse : 시간 간격으로 단위는 밀리초 ex) 1000 이 1초

lpTimerFunc : 타이머가 보내는 WM_TIMER 메세지를 받을 함수 이름 NULL 이면 WndProc() 가 WM_TIMER 메세지를 받는다.

 

 

 

 

타이머의 동작을 중간에 정지시킬 떄는 KillTimer() 함수를 사용한다. KillTimer() 함수로 정지시킨 타이머는 SetTimer() 로 다시 동작시킬 수 있다.

 

 

 

1
2
3
4
BOOL KiLLTimer(
    HWND hWnd,
    UINT_PTR nIDEvent,
    );
cs

 

nIDEvent : 동작 중인 타이머의 ID

 

 

 

이제 이를 이용하여 타이머를 설정하고 

 

타이머를 이용하여 원이 자동으로 움직이게 만들어보면

 

 

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
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    PAINTSTRUCT ps;
    static int x,y;
    static RECT rectView;
    
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            x=20; y=20;
            GetClientRect(hwnd,&rectView);
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            
            Ellipse(hdc,x-20,y-20,x+20,y+20);
            EndPaint(hwnd,&ps);
            break;
        case WM_KEYDOWN:
            if(wParam == VK_RIGHT)
            {
                SetTimer(hwnd,1,70,NULL);
                
            }
            
            break;
        case WM_TIMER:
            x+=40;
            if(x+20>rectView.right)
            {
                x-=40;
            }
            InvalidateRgn(hwnd,NULL,TRUE);    
            
            break;
        case WM_DESTROY: {
            KillTimer(hwnd,1);
            PostQuitMessage(0);
            break;
        }
    
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
cs

 

이렇게 만들어서 실행해보면 오른쪽 방향키를 누르는 순간 원이 자동으로 오른쪽으로 이동한다.

 

하지만 오른쪽방향키를 꾹 누르고 있는 경우 공이 멈춰버리는데 

 

정확한것은 아니지만

 

내 예상으로는 SetTimer가 설정되고 그 타이머가 WM_TIMER 메세지를 보내기전에 또다시  SetTimer가 만들어져서(오른쪽 방향키를 계속 누르고있으니 WM_KEYDOWN 메세지가 엄청빨리 보내짐) WM_TIMER가 메세지가 발생하지 않았을 것이라 생각된다(SetTimer가 덮어써지는 현상일듯?)

 

 

출처 :

 

핵심 API로 배우는 윈도우 프로그래밍,   강경우 지음   출판사 : 한빛아카데미

이번에는 원을 그리고 키보드로 원을 움직이는 것을 해보겠다

 

먼저 원을 그려야 하는데 

 

원을 그려주는 Ellipse 함수를 이용한다.

 

좌표 2개를 입력으로 받는다 (X1,Y1) , (X2, Y2) 이 두 좌표를 이용하여 사각형을 만들었을때 

 

그 안을 내접하는 원을 만들어준다.

 

예를들어 (0,0)    (40,40) 이라면

 

중심이 (20,20)인 원

 

 

 

1
2
3
4
5
6
7
Bool Ellipse(
    HDC hdc,
    int X1,
    int Y1,
    int X2,
    int Y2
    );
cs

 

ex)

 

 

 

 

1
2
hdc = BeginPaint(hwnd,&ps);
Ellipse(hdc,x-20,y-20,x+20,y+20);
cs

 

 

 

이제 이를 이용하여

 

 

dd

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
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    PAINTSTRUCT ps;
    static int x,y;
    static RECT rectView;
    
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            x=20; y=20;
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            Ellipse(hdc,x-20,y-20,x+20,y+20);
            EndPaint(hwnd,&ps);
            break;
        case WM_KEYDOWN:
            if(wParam == VK_RIGHT)
            {
    
                x+=40;
            
            }
            InvalidateRgn(hwnd,NULL,TRUE);    
            break;
        case WM_KEYUP:
            
            InvalidateRgn(hwnd,NULL,TRUE);
            break;
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
    
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
cs

 

 

이 코드의 경우

 

오른쪽 방향키를 누르면 x가 40만큼 증가하여 원이 오른쪽으로 이동하는 것 처럼 보이게 만들 수 있다.

 

하지만 계속 누르는 경우 윈도우 밖으로 나가버린다.

 

이를 막기 위해 윈도우 크기를 알아내어 해당 윈도우 크기만큼만 이동시킬 수 있는데 

 

GetClientRect()함수로 윈도우 크기를 알아낼 수 있다.

 

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
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    PAINTSTRUCT ps;
    static int x,y;
    static RECT rectView;
    
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            x=20; y=20;
            GetClientRect(hwnd,&rectView);
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            
            Ellipse(hdc,x-20,y-20,x+20,y+20);
            EndPaint(hwnd,&ps);
            break;
        case WM_KEYDOWN:
            if(wParam == VK_RIGHT)
            {
                
                x+=40;
                if(x+20>rectView.right)
                {
                    x-=40;
                }
            }
            InvalidateRgn(hwnd,NULL,TRUE);    
            break;
        case WM_KEYUP:
            
            InvalidateRgn(hwnd,NULL,TRUE);
            break;
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
    
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
cs

 

 

 

 

 

 

 

실행 하면 현재 윈도우 크기 만큼만 원이 이동함을 알 수 있다.

 

하지만 예상대로 윈도우를 최대화로 늘려도 그 이상 이동되진 않았다.

 

이경우는 윈도우의 크기가 변하면 발생하는 WM_SIZE 메세지를 이용한다.

 

 

 

출처 :

 

핵심 API로 배우는 윈도우 프로그래밍,   강경우 지음   출판사 : 한빛아카데미

키보드로 이벤트를 발생시키면 WndProc()에 전달되는데

 

전달변수는

 

1. Msg : 키보드 메세지 ex) WM_KEYDOWN, WM_CHAR 등

 

2. wParam : 가상키값 ex ) A, B, C, 1, 2, 3, VK_BACK,VK_RETURN ...

 

가상키값은 우리가 알고있는 문자,특수문자는 아스키값으로 전달되며

 

방향키,Ctrl,Shift등의 값은 VK_LEFT,VK_CONTROL과 값은 값으로 전달된다. 자세한것은 

 

https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

 

Virtual-Key Codes (Winuser.h) - Win32 apps

The following table shows the symbolic constant names, hexadecimal values, and mouse or keyboard equivalents for the virtual-key codes used by the system. The codes are listed in numeric order.

docs.microsoft.com

여기서 확인 할수 있다.

 

 

 

3. lParam : 부가정보 (키 반복횟수같은 내용이 담아있는듯 한데 중요하진 않은 것 같다.)

 

ex) 키를 눌렀을때 반응하는 코드

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
81
82
#include <windows.h>
#include <TCHAR.H> 
/* This is where all the input to the window goes to */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
            
        
        case WM_CHAR:
            hdc = GetDC(hwnd);
            TextOut(hdc,0,0,_T("키를 눌렀을때 발생 "),21);
            ReleaseDC(hwnd,hdc);
            break;
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
 
/* The 'main' function of Win32 GUI programs: this is where execution starts */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASSEX wc; /* A properties struct of our window */
    HWND hwnd; /* A 'HANDLE', hence the H, or a pointer to our window */
    MSG msg; /* A temporary location for all messages */
 
    /* zero out the struct and set the stuff we want to modify */
    memset(&wc,0,sizeof(wc));
    wc.cbSize         = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc     = WndProc; /* This is where we will send messages to */
    wc.hInstance     = hInstance;
    wc.hCursor         = LoadCursor(NULL, IDC_IBEAM);
    
    /* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it */
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "Window Class Name";
    
    wc.hIcon         = LoadIcon(NULL, IDI_QUESTION); /* Load a standard icon */
    wc.hIconSm         = LoadIcon(NULL, IDI_QUESTION); /* use the name "A" to use the project icon */
 
    if(!RegisterClassEx(&wc)) {
        MessageBox(NULL"Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"Window Class Name","홍길동의 첫번째 윈도우 ",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, /* x */
        CW_USEDEFAULT, /* y */
        CW_USEDEFAULT, /* width */
        CW_USEDEFAULT, /* height */
        NULL,NULL,hInstance,NULL);
 
    if(hwnd == NULL) {
        MessageBox(NULL"Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    /*
        This is the heart of our program where all input is processed and 
        sent to WndProc. Note that GetMessage blocks code flow until it receives something, so
        this loop will not produce unreasonably high CPU usage
    */
    while(GetMessage(&msg, NULL00> 0) { /* If no error is received... */
        TranslateMessage(&msg); /* Translate key codes to chars if present */
        DispatchMessage(&msg); /* Send it to WndProc */
    }
    return msg.wParam;
}
cs

 

실행 결과

 

 

 

 

 

키를 눌렀을때 어느 키를 눌렀는지 확인하는 코드

 

 

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
81
82
83
84
#include <windows.h>
#include <TCHAR.H> 
/* This is where all the input to the window goes to */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    static TCHAR str[2]; 
 
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
            
        
        case WM_CHAR:
            hdc = GetDC(hwnd);
            str[0]= wParam;
            str[1]= NULL;
            TextOut(hdc,0,0,str,_tcslen(str));
            ReleaseDC(hwnd,hdc);
            break;
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
 
/* The 'main' function of Win32 GUI programs: this is where execution starts */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASSEX wc; /* A properties struct of our window */
    HWND hwnd; /* A 'HANDLE', hence the H, or a pointer to our window */
    MSG msg; /* A temporary location for all messages */
 
    /* zero out the struct and set the stuff we want to modify */
    memset(&wc,0,sizeof(wc));
    wc.cbSize         = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc     = WndProc; /* This is where we will send messages to */
    wc.hInstance     = hInstance;
    wc.hCursor         = LoadCursor(NULL, IDC_IBEAM);
    
    /* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it */
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "Window Class Name";
    
    wc.hIcon         = LoadIcon(NULL, IDI_QUESTION); /* Load a standard icon */
    wc.hIconSm         = LoadIcon(NULL, IDI_QUESTION); /* use the name "A" to use the project icon */
 
    if(!RegisterClassEx(&wc)) {
        MessageBox(NULL"Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"Window Class Name","홍길동의 첫번째 윈도우 ",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, /* x */
        CW_USEDEFAULT, /* y */
        CW_USEDEFAULT, /* width */
        CW_USEDEFAULT, /* height */
        NULL,NULL,hInstance,NULL);
 
    if(hwnd == NULL) {
        MessageBox(NULL"Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    /*
        This is the heart of our program where all input is processed and 
        sent to WndProc. Note that GetMessage blocks code flow until it receives something, so
        this loop will not produce unreasonably high CPU usage
    */
    while(GetMessage(&msg, NULL00> 0) { /* If no error is received... */
        TranslateMessage(&msg); /* Translate key codes to chars if present */
        DispatchMessage(&msg); /* Send it to WndProc */
    }
    return msg.wParam;
}
cs

 

 

 

하지만 위와 같은 코드에서는 창을 최소화하고 다시 켰을 때 입력한 값이 지워지는 현상을 볼 수있는데

 

원인 :

 

창을 최소화 하고 다시키면

 

화면을 깨끗이 지우고 WM_PAINT 메세지를 발생시키기 때문

 

 

해결 방법 : 

1. 먼저 출력할 변수를 지워지지 않게 전역변수나 static변수에 넣어둔다.

 

2. 그 이후 WM_PAINT에서 저장된 변수를 다시 그려버리면 깔끔하게 해결

 

 

 

 

ex)

 

 

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
81
82
83
84
85
86
87
88
#include <windows.h>
#include <TCHAR.H> 
/* This is where all the input to the window goes to */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    static TCHAR str[2]; 
    PAINTSTRUCT ps;
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            TextOut(hdc,0,0,str,_tcslen(str));
            EndPaint(hwnd,&ps);
            break;
        case WM_CHAR:
            hdc = GetDC(hwnd);
            str[0]= wParam;
            str[1]= NULL;
            TextOut(hdc,0,0,str,_tcslen(str));
            ReleaseDC(hwnd,hdc);
            break;
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
 
/* The 'main' function of Win32 GUI programs: this is where execution starts */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASSEX wc; /* A properties struct of our window */
    HWND hwnd; /* A 'HANDLE', hence the H, or a pointer to our window */
    MSG msg; /* A temporary location for all messages */
 
    /* zero out the struct and set the stuff we want to modify */
    memset(&wc,0,sizeof(wc));
    wc.cbSize         = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc     = WndProc; /* This is where we will send messages to */
    wc.hInstance     = hInstance;
    wc.hCursor         = LoadCursor(NULL, IDC_IBEAM);
    
    /* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it */
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "Window Class Name";
    
    wc.hIcon         = LoadIcon(NULL, IDI_QUESTION); /* Load a standard icon */
    wc.hIconSm         = LoadIcon(NULL, IDI_QUESTION); /* use the name "A" to use the project icon */
 
    if(!RegisterClassEx(&wc)) {
        MessageBox(NULL"Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"Window Class Name","홍길동의 첫번째 윈도우 ",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, /* x */
        CW_USEDEFAULT, /* y */
        CW_USEDEFAULT, /* width */
        CW_USEDEFAULT, /* height */
        NULL,NULL,hInstance,NULL);
 
    if(hwnd == NULL) {
        MessageBox(NULL"Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    /*
        This is the heart of our program where all input is processed and 
        sent to WndProc. Note that GetMessage blocks code flow until it receives something, so
        this loop will not produce unreasonably high CPU usage
    */
    while(GetMessage(&msg, NULL00> 0) { /* If no error is received... */
        TranslateMessage(&msg); /* Translate key codes to chars if present */
        DispatchMessage(&msg); /* Send it to WndProc */
    }
    return msg.wParam;
}
cs

 

위 코드를 실행해보면 최소화후 다시 켜도 해당 문자가 저장되어 있음을 확인 할 수 있다.

 

하지만 이 코드도 다시한번 생각해보면 이런 현상을 막기 위해서 계속해서 WM_PAINT,WM_CHAR 에 모두 출력코드를 계속해서 적어야 한다.

 

이 문제를 해결하기 위해 InvalidateRgn() 함수를 사용하는데 이 함수의 역할은 윈도우를 최소화 시키지 않아도

강제로 WM_PAINT 메세지를 발생시킬 수 있다. 

즉, WM_PAINT에서만 출력코드를 사용하고 WM_CHAR 에서는 InvalidateRgn()함수만 호출하면 된다.

 

InvalidateRgn()함수는

 

BOOL InvalidateRgn(

    HWND hWnd,

    HRGN hRgn,

    BOOL bErase

);

 

 

hRgn : 수정 영역에 대한 핸들값 주로 NULL값을 쓴다 (클라이언트 영역 전체를 수정)

bErase : true면 모두 삭제하고 다시 그림(이경우 출력량이 많으면 화면이 깜빡꺼린다고 한다), false면 수정부분만 다시그림

 

ex)

 

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
#include <windows.h>
#include <TCHAR.H> 
/* This is where all the input to the window goes to */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    static TCHAR str[2]; 
    PAINTSTRUCT ps;
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            TextOut(hdc,0,0,str,_tcslen(str));
            EndPaint(hwnd,&ps);
            break;
        case WM_CHAR:
            hdc = GetDC(hwnd);
            str[0]= wParam;
            str[1]= NULL;
            InvalidateRgn(hwnd,NULL,TRUE);
            ReleaseDC(hwnd,hdc);
            break;
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
 
cs

 

 

 

출처 :

 

핵심 API로 배우는 윈도우 프로그래밍,   강경우 지음   출판사 : 한빛아카데미

 

출력을 위해 얻어온 화면 영역을 DC(device context) 라고 한다. 

 

DC는 변수에 저장해여 하는데 이때 변수 타입이 HDC이다. 

 

HDC 타입은 메모리 영역을 관리하며 메모리 영역에는 얻어온 화면 영역에 대한 속성 값을 저장 할 수 있다.

 

즉, HDC타입의 변수를 hdc로 선언한 뒤 hdc에 DC를 얻어와 저장하고, 화면 얻어오기 함수를 이용에 얻은 화면을 hdc에

 

저장하면 된다.

 

DC를 얻는 방법 2가지

 

1. BeginPaint()

-> WM_PAINT 메세지가 발생했을 때만 사용 (윈도우가 화면에 나타나는 시점에 DC를 얻는다.)

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
HDC BeginPaint(
    HWND hwnd,                    //생성한 윈도우 핸들 
    PAINTSTRUCT *lpPaint        //출력 영역에 대한 정보를 저장할 PAINTSTRUCT 구조체 주소      
 
 
 
BOOL EndPaint(
    HWND hwnd,                    //생성한 윈도우 핸들 
    PAINTSTRUCT *lpPaint        //출력 영역에 대한 정보를 저장할 PAINTSTRUCT 구조체 주소  
)
 
 
typedef struct tagPAINTSTRUCT{
    HDC hdc;
    BOOL fErase;                    //배경 삭제 여부를 가르키는 값 true면 응용 프로그램이 직접 배경을 삭제해야하는데 그렇게 하려면 hbrBackground 멤버를 NULL로 해야함  
    RECT rcPaint;                     //RECT 구조체, 출력 영역의 왼쪽 상단 꼭짓점과 오른쪽 하단 꼭짓점의 좌표 저장  
    BOOL fREstore;                    //시스템용 
    BOOL fIncUpdate;                //시스템용 
    BYTE rgbReserved[32];            //시스템용  
}; PAINTSTRUCT, *PPAINTSTRUCT;
 
 
cs

 

 

ex)

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
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    PAINTSTRUCT ps;
    
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            //이곳에서 출력이 이뤄짐
            
            EndPaint(hwnd,&ps);
            break;    
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
 
cs

 

 

 

 

2. GetDC()

-> WM_PAINT 메세지를 제외한 다른메세지 발생했을 때 사용

 

 

1
2
3
4
5
6
7
8
9
10
11
HDC GetDC(
    HWND hwnd
);
 
int ReleaseDC(
    HWND hwnd,
    HDC hdc        //반환하는 DC 핸들값  
)
 
 
 
cs

 

텍스트 출력 함수 

 

1. TextOut()

 

BOOL TextOUT(HDC hdc, int x, int y, LPCTSTR lpString, int nLength);

 

hdc : BeginPaint나 GetDC 함수로 얻어온 화면 영역

 

int x, int y : 텍스트를 출력할 위치

 

LPCTSTR lpString : 출력할 텍스트 문자열

 

int nLength : 출력할 텍스트 길이

 

 

ex)

 

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
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    PAINTSTRUCT ps;
    
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            //이곳에서 출력이 이뤄짐
            TextOut(hdc,100,100,_T("Hello 안녕"), _tcslen(_T("Hello 안녕")));
            EndPaint(hwnd,&ps);
            break;    
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
cs

 

실행 결과

 

 

2. DrawText()

-> TextOut()처럼 한점의 좌표를 이용해 출력하는것이 아니라 박스 영역을 지정하고 그안에 출력

 

int DrawText(HDC hdc, LPCTSTR lpString, int nLength, LPRECT lpRect, UINT Flags);

 

LPRECT lpRect : LPRECT는 RECT * 와 같은 것으로 문자열을 출력할 박스 영역의 좌표가 저장된 RECT 타입 변수의 주소

 

UINT Flags : 영역의 어느위치에 어떻게 출력 할지를 알려주는 플래그값 (미리 정의되어 있음 아래 출처 참조)

 

 

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
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
            
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            //이곳에서 출력이 이뤄짐
            
            
            rect.left =50;
            rect.top = 40;
            rect.right = 200;
            rect.bottom = 120;
            
            DrawText(hdc,_T("HelloWorld"),10,&rect,
            DT_SINGLELINE | DT_CENTER | DT_VCENTER);
            EndPaint(hwnd,&ps);
            break;    
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
cs

 

실행 결과

 

 

 

출처 :

 

핵심 API로 배우는 윈도우 프로그래밍,   강경우 지음   출판사 : 한빛아카데미

 

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawtext

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>
 
/* This is where all the input to the window goes to */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
 
/* The 'main' function of Win32 GUI programs: this is where execution starts */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASSEX wc; /* A properties struct of our window */
    HWND hwnd; /* A 'HANDLE', hence the H, or a pointer to our window */
    MSG msg; /* A temporary location for all messages */
 
    /* zero out the struct and set the stuff we want to modify */
    memset(&wc,0,sizeof(wc));
    wc.cbSize         = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc     = WndProc; /* This is where we will send messages to */
    wc.hInstance     = hInstance;
    wc.hCursor         = LoadCursor(NULL, IDC_IBEAM);
    
    /* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it */
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "Window Class Name";
    
    wc.hIcon         = LoadIcon(NULL, IDI_QUESTION); /* Load a standard icon */
    wc.hIconSm         = LoadIcon(NULL, IDI_QUESTION); /* use the name "A" to use the project icon */
 
    if(!RegisterClassEx(&wc)) {
        MessageBox(NULL"Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"Window Class Name","홍길동의 첫번째 윈도우 ",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT0, /* x */
        CW_USEDEFAULT, /* y */
        CW_USEDEFAULT, /* width */
        CW_USEDEFAULT, /* height */
        NULL,NULL,hInstance,NULL);
 
    if(hwnd == NULL) {
        MessageBox(NULL"Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }
 
    /*
        This is the heart of our program where all input is processed and 
        sent to WndProc. Note that GetMessage blocks code flow until it receives something, so
        this loop will not produce unreasonably high CPU usage
    */
    while(GetMessage(&msg, NULL00> 0) { /* If no error is received... */
        TranslateMessage(&msg); /* Translate key codes to chars if present */
        DispatchMessage(&msg); /* Send it to WndProc */
    }
    return msg.wParam;
}
cs

----------------------------------------------------------------- 기본 코드 --------------------------------------------------

 

 

메세지 처리 과정

 

1.  키보드 마우스 등의 이벤트 발생

 

2. 이벤트를 "윈도우" 시스템이 감지 -> 메세지 큐에 쌓음

 

3. WinMain()함수에서 쌓인 메세지큐에서 메세지를 꺼냄

 

4. 꺼낸 메세지를 해석하여 메세지 처리 함수에 보낸다 (여기서 메세지처리 함수 : WndProc)

 

5. 메세지 처리함수가 메세지를 받아서 반응

 

 

 

 

 

각 코드 해석

 

1. WinMain() 함수

 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

 

WINAPI : 윈도우 프로그래밍을 의미 (콘솔 응용프로그램이 아님)

 

hInstance : 운영체제의 커널이 응용 프로그램에 부여한 ID로 메모리에서의 위치

               (프로그램에 커널이 부여한 ID, 메모리에 적재된 많은 응용프로그램을 구분하기 위함)

 

 

lpCmdLine : 명령 라인에서 프로그램 구동시 전달할 문자열 (C에서 main함수에 argv 넣는거랑 같음)

 

iCmdShow : 정숫값으로 윈도우가 화면에 출력될때 형태 정의(아직 안써봐서 모름)

 

 

 

 

2. WNDCLASSEX 구조체

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _WNDCLASSEX{
    UINT    cbSize;                //구조체 크기 
    UINT    style;                //출력 형태 
    WNDPROC    lpfnWndProc;        //프로시저 함수 
    int    cbClsExtra;                //클래스 여분 메모리 
    int    cbWndExtra;                //윈도우 여분 메모리 
    HANDLE    hInstance;            //윈도우 인스턴스 
    HICON    hIcon;                //아이콘 
    HCURSOR    hCursor;            //커서 
    HBRUSH    hbrBackground;        //배경 색 
    LPCTSTR    lpszMenuName;        //메뉴 이름 
    LPCTSTR    lpszClassName;        //클래스 이름 
    HICON    hIconSm;            //작은 아이콘  
};    WNDCLASSEX;
cs

 

lpfnWndProc : 메세지 처리에 사용될 함수 (여기선  wc.lpfnWndProc     = WndProc;)

hCursor : 마우스를 가져다 댔을때 모양 설정

 

(ex : wc.hCursor  = LoadCursor(NULL, IDC_IBEAM);)

 

 

 

 

3. 윈도우 생성

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
HWND CreateWindow(
    LPCTSTR lpClassName,        //윈도우 클래스 이름 
    LPCTSTR lpWindowName,        //윈도우 타이틀 이름
    DWORD dwStyle,                //윈도우 스타일
    int x,                        //윈도우 위치 x 좌표 
    int y,                        //윈도우 위치 y 좌표 
    int nWidth,                    //윈도우 가로 크기 
    int nHeight,                //윈도우 세로 크기 
    HWND hWndParent,            //부모 윈도우 핸들 
    HMENU hMenu,                //메뉴 핸들 
    HINSTANCE hInstance,        //응용 프로그램 인스턴스 
    LPVOID lpParam                 //생성 윈도우 정보  
); 
cs

 

 

ex)

hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"Window Class Name","윈도우 타이틀 이름 ",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT0, /* x */
CW_USEDEFAULT, /* y */
CW_USEDEFAULT, /* width */
CW_USEDEFAULT, /* height */
NULL,NULL,hInstance,NULL);

 

 

먼저 내 dev c++에서는 CreateWindow가 아닌 CreateWindowEx를 자동으로 만들어 주는데

CreateWindow의 확장 버전으로 가장 맨앞에 매개변수를 하나 더 받는다 기능이 추가된듯 한데 중요한것 같지 않으니 그냥 넘어가자

 

알아야할 것

 

내 화면에 띄울 윈도우 이름이 lpWindowName이다 

 

윈도우스타일(dwStyle)중에 

WS_OVERLAPPEDWINDOW 옵션은 타이틀바에 최소화 , 최대화, 닫기 버튼이 나타나고 마우스 오른쪽클릭시 나타나는 시스템 메뉴도 지원해주는 형태의 윈도우이다. 유용할 것 같으니 알아두자

(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXMIZEBOX 와 같다)

 

x,y,width,height값은 숫자로 입력해도 되지만 CW_USEDEFAULT  옵션을 이용해 커널에게 맡기는 방법이 있다.

 

 

 

4. 메세지 처리 함수 : WndProc()

1
2
3
4
5
6
7
LRESULT WndProc(
    HWND hWnd,
    UINT iMsg,
    WPARAM wParam,
    LPARAM lParam
 
);
cs

 

 

함수 반환타입인 LRESULT는 32비트의 값을 알려주는 반환타입이라고 한다.

 

hWND : 생성된 윈도우 핸들 CreateWindow() 함수를 이용해 생성

 

iMsg : UINT 타입은 unsigned int 줄인말이다.

메세지 번호를 의미한다 

 

wParam,lParam : 32비트 값으로 키보드에서 눌린 문자의 코드값이 들어있을수 있고 마우스의 위치가 들어 있을 수도 있다. (상황에 따라 다름)

 

 

ex)

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
    switch(Message) {
        
        /* Upon destruction, tell the main thread to stop */
        case WM_CREATE:
            break
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        
        /* All other messages (a lot of them) are processed using default procedures */
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}
cs

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

cf)

책에서는 클래스 name을 지정할때

    wc.lpszClassName = "Window Class Name";가 아닌

    wc.lpszClassName = _T("Window Class Name")을 사용하였는데 프로젝트에서 사용하는 문자집합이 멀티바이트든 유니코드는 상관없이 처리하기 위함이라 한다. 나중에 안되는게 나오면 _T를 쓰자. 

 

( _T 함수는 #include <TCHAR.H> 헤더에 있다.)

 

 

 

 

 

 

 

 

 

출처 : 

 

핵심 API로 배우는 윈도우 프로그래밍,   강경우 지음    출판사 : 한빛아카데미

 

http://soen.kr/lecture/win32api/reference/Function/LoadCursor.htm

 

Win32 API Reference

설명 표준 커서 또는 응용 프로그램의 리소스에 정의되어 있는 커서를 읽어온다. 단, 이미 커서가 로드되어 있을 때는 다시 읽지 않고 읽어 놓은 커서의 핸들을 구해 준다. 만약 lpCursorName 인수가 가리키는 리소스가 커서가 아닌 다른 리소스이더라도 이 함수는 NULL을 리턴하지 않으므로 반드시 커서 리소스만 지정하도록 주의해야 한다. 하나의 커서 리소스에 여러 가지 색상의 커서가 있을 경우 현재 화면 설정에 가장 적합한 커서를 읽어준다.

soen.kr

 

 

+ Recent posts