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

 

먼저 원을 그려야 하는데 

 

원을 그려주는 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로 배우는 윈도우 프로그래밍,   강경우 지음   출판사 : 한빛아카데미

 

 

망겜을 주로 하다보니 이번 플레이영상은 

 

스타유즈맵 장르인 폭탄피하기 중 puma bound 2라는 맵이다.

 

폭피 유저라면 거의 다 아는 맵이다

 

예전에는 이맵을 깨면 열손가락에 드는 초고수였는데

 

런쳐가 나온이후 저글링의 반응속도가 빨라지면서 지금은 깨는 사람이 많아졌다.

 

나같은 경우만 해도 런쳐가 나오기전 반응으로는 5탄도 깨본적이 없었지만

 

지금은 간단하게 클리어 할 수 있게 되었다.

키보드로 이벤트를 발생시키면 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

 

 

이미 노트북이 있지만



요즘 들어 테블릿이 있다면 정말 편하겠다라는 생각이 들었다.



특히 수업들을때 수업의 강의노트를 매번 복사했었는데



이런 복사가 필요 없다는 것은 엄청난 메리트가 아닐 수 없다.





아이패드 프로와 갤텝s3를 두고 무엇을 살지 정말 많은 고민을 했다...(3일정도?)


결국 백화점가서 둘다 써본후 갤텝을 사기로 결정했다( 음...  성능은 아이패드 프로가 더 좋다는데 사실 거의 수업용이라 필기감만 보고 결정함 ㅎ)






일단 개봉 전 한장 찍고









구성품은 정말 간단하다 딱 저게 끝이다 



바로


별도로 산 키보드 케이스와 시력방지 액정필름을 붙였다.








lectureNote를 바로 받아서 필기감 테스트를 해봤는데


역시 생각대로 필기감이 너무 좋다 ㅋㅋ.


앞으로 노트보단 테블릿을 더 많이 쓸거같다 


캬캬캬

용어 : visual editor의 줄인 말이라고 하는데 전혀 visual하지 않다는 것이 특징이다.(옛날기준으로는 visual 했을지도?)


장점 : 편집 속도가 빠르다, 간편하다, 기본설치 프로그램이다.(리눅스 환경에 기본적으로 설치되어있다.)




텍스트 형식이라 아직은 불편하지만 숙달된다면 편집속도가 빠르다고 하니 조금씩 익혀두자.





우선 구조를 잠깐 보면






이렇게 터미널에서 vi에디터를 실행하면 명령모드로 진입하고



<명령모드 진입상태>


위 사진처럼 된다.





이 명령모드에서 두가지 모드로 갈 수 있는데


      >>>> 1. 

A(또는 a)나 I(또는 i)를 입력하여 입력모드 (문서를 작성하는 실제 작업)






<사진은 명령모드에서 A버튼을 클릭하여 입력모드로 진입한 상태(좌측 하단에 INSERT표시가 되어있다)


다시 명령모드로 돌아가려면 ESC키를 눌러주면 된다.









>>>> 2.

:를 이용한 ex모드 (라인 명령모드)



                             <사진은 :를 입력하여 ex모드에 진입한 상태이다 커서가 좌측하단에 자동으로 가게 된다>






1. 입력모드


메모장을 이용하듯이 내용을 수정 삭제 할 수 있다.





2. ex모드(라인명령모드)


저장, 종료, 취소등을 수행하는 모드



 :w

 입력모드에서 수정한것을 저장 후 명령모드로 돌아간다

 :wq

 입력모드에서 수정한것을 저장 후 터미널로 돌아간다.

 :q!

 수정한 내용을 저장하지 않고 vi 에디터를 닫는다.

 

 

  




3. 명령모드


글자 삭제

dd

현재 위치한 행 삭제

숫자 dd

현재 위치한 행에서 숫자만큼의 줄 행 삭제

yy

현재 위치한 행 복사


 

 

 

 

 

 

 

 

 



표의 빈칸은 나중에 채우도록 하자







예를 보자 



1. sort (quick sort로 구현)




위에 보이는 것과 같이 크기 순서대로 정렬이 되어진다.


sort의 3번째 매개변수 값을 정하지 않으면 


default 값으로 오름차순 정렬이 된다.


내림차순으로 정렬을 원한다면


#include<functional>을 정의하고


sort(a,a+10,greater<int>());


와 같이 3번째 매개변수 값으로 greater<int>()를 추가해주면 된다.





2.stable_sort  (merge sort로 구현)


stable_sort의 사용법은 sort와 완전히 같다



보이는 것과 같이 sort와 똑같이 사용해주면 된다.


하지만 sort와 다른점은 정렬하는 원소값이 같은 경우이다.


다음을 보자





다음 코드는 vector에 


한쌍의 순서쌍을 6개 원소로 넣었다


sort로 정렬하였는데 


정렬기준은 내가 지정한 cmp함수를 따른다


cmp는 순서쌍의 "첫번째 원소"가 작다면


앞쪽에 오도록 하였다(오름차순)



결과를 보면


12 32 42 44 52 72


순서로 잘 정렬이 되어있는데


42 와 44와 같이 


첫번째 원소가 같은경우 어떻게 정렬이 될까??


이경우 sort로 정렬한다면 


누가 앞에 올지 예측 할수 없다.


하지만 stable_sort로 정렬한다면 컨테이너의 앞의 원소(여기선 vector의 앞 원소)


순서대로 정렬이 된다


즉 42와 44는 42가 앞에 있기때문에 


42 44 순서로 정렬이 된다.!







이를 응용하는 문제는


https://www.acmicpc.net/problem/10814


를 풀어보자

공부한 것들을 정리하여 


블로그에 정리해두기





나중에 내가 보기도 편할 것이고


그것을 다른사람과 공유하기 



+일상 등등

+ Recent posts