본문 바로가기
코스웨어/15년 스마트컨트롤러

2015.11.16_개인업무일지_[Win32API #3]_이량경

by 알 수 없는 사용자 2015. 11. 16.
728x90
반응형
<Win32API>✔ 
================================================================

✔ 4-1-라. TranslateMessage
- GetMessage는 메시지 큐에서 메시지를 꺼내온후 이 메시지를 함수 TransleateMessage()로 넘겨준다. 
- WM_KEYDOWN이 전달되려면 TransleateMessage()함수와DispatchMessage()함수까지 통과되어야 한다.

- DispatchMessage()함수에 의해 wndProc에 전달 되므로 
* TranslateMessage 함수를 빼 버리면 WM_CHAR 메시지는 절대로 WndProc으로 전달되지 않을 것이다.

---------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )
{
        HDC hdc;
        static int x;
        static int y;
        static BOOL bnowDraw = FALSE;
        switch ( iMessage)
       {
        case WM_LBUTTONDOWN:
              x = LOWORD( lParam);
              y = HIWORD( lParam);
              bnowDraw = TRUE;
               return 0;
        case WM_MOUSEMOVE:
               if (bnowDraw == TRUE)
              {
                     hdc = GetDC( hWnd);
                     MoveToEx(hdc, x, y, NULL);
                     x = LOWORD( lParam);
                     y = HIWORD( lParam);
                     LineTo(hdc, x, y);
                     ReleaseDC( hWnd, hdc);
              }
               return 0;
        case WM_LBUTTONUP :
              bnowDraw = FALSE;
               return 0;
        case WM_LBUTTONDBLCLK:
              InvalidateRect( hWnd, NULL, TRUE);
               return 0;
        case WM_DESTROY:
              PostQuitMessage(0);
               return 0;
       }
        return(DefWindowProcA( hWnd, iMessage, wParam, lParam));

}

---------------------------------------------------

✔ 4-2-가. Mouse


- WParam(word)     lParam(long)
  옛날엔 2,          4
  지금은 4,          4

버튼누름놓음더블클릭
좌측WM_LBUTTONDOWNWM_LBUTTONUPWM_LBUTTONDBLCLK
우측WM_RBUTTONDOWNWM_RBUTTONUPWM_RBUTTONDBLCLK
중앙WM_MBUTTONDOWNWM_MBUTTONUPWM_MBUTTONDBLCLK
- 버튼 세개에 각각 누름, 놓음, 더블 클릭 의 9가지 메시지가 있다.


* lParam의 상위 워드에 마우스 버튼이 눌러진 y좌표 ,하위 x좌표 를 가진다.
- 반으로 잘르면 x,y좌표를 얻는다.
- 절대 좌표가 아니고 (바탕화면 기준아니고) 창 안에서의 기준이다.

* 좌표값 검출을 위해 HIWORD, LOWORD 의 매크로 함수사용.



* 조합키 상태는 다음표의 값들과 비트연산

설명
MK_CONTROLCtrl 키가 눌러져 있다.
MK_LBUTTON마우스 왼쪽 버튼이 눌러져 있다.
MK_RBUTTON마우스 오른쪽 버튼이 눌러져 있다.
MK_MBUTTON마우스 중간 버튼이 눌러져 있다.
MK_SHIFTShift 키가 눌러져 있다.


- 왼쪽 마우스 버튼이 눌려지면 "WM_LBUTTONDOWN 메시지 발생"
  -> 눌려진 위치좌표를 x,y 에 대입

  -> 마우스가 움직이면 시작 지점~ 움직이는 지점까지 선을 긋는다.

- 그리는 것처럼 보이지만 실제 선을 긋고 있다. 버튼 때면 UP호출/누리면 DOWN

WParam에는 마우스 버튼의 상태(누른상태/안누른상태 체크)와 키보드 조합키의 상태(Shift, Ctrl)가 전달된다.

- lParam 에 마우스 커서의 위치가 전달되고, wParam에 조합키 상태가 전달된다.


- 마우스 버튼이 놓아지면 bnowDraw를 FALSE로 변경하여 마우스가 움직여도 선이 안그려진다.

- 화면 그림을 그리기만 하고 복구 능력이없다.

* 이 문제의 해결은
- 화면 전체를 비트맵으로 저장

- 연결리스트를 사용하여 마우스의 움직임을 일일이 보관.


* 우리가 만든창은 가로가 65000정도가 한계다. 가로가 3000개 넘어가기 힘들다. 
윈도우의 한계다.

- 하지만 이후에 long를 8로 올리면 가로 40억개 되니까 해결방법이 된다.


* C++아니라서 BOOL형이 존재 하지 않음. 용량이 실제 그렇게 작지 않다(typedef로 만들었기 때문에) 모르는 형이 나오면 존재하는것이 아니고 define해서 사용한것.


*lsprintf : 메모리에 출력하는 것(메모리 주소)

- 윈도우에서는 2바이트를 프린터 할수있는애 유니코드지원.

---------------------------------------------------

LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )
{
        HDC hdc;
        PAINTSTRUCT ps;
        static int x;
        static int y;
        static WCHAR wcbuf[100];
        static WCHAR str[256]; //WCHAR 2바이트 / TCHAR은 4바이트
        int len;
        switch ( iMessage)
       {
        case WM_DESTROY:
              PostQuitMessage(0);
               return 0;
        case WM_MOUSEMOVE:
              x = LOWORD( lParam);
              y = HIWORD( lParam);
               wsprintf(wcbuf, L "x = %d, y = %d", x, y);
              InvalidateRect( hWnd, NULL, TRUE);
               return 0;
        case WM_PAINT:
              hdc = BeginPaint( hWnd, &ps);
               TextOut(hdc, 50, 50, wcbuf, lstrlen(wcbuf)); //아스키1바이트만 지원 그래서 L
              EndPaint( hWnd, &ps);
               return 0;
       }
        return( DefWindowProc(hWnd , iMessage , wParam , lParam ));

}



---------------------------------------------------


✔ 4-2-나. 더블클릭
- InvalidateRect 함수 호출로 작업영역 전체 무효화

* 더블클릭 메시지를 발생하기 위해 메인을 드물게 수정한다.

- CS_DBLCLKS 플레그 추가


WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;


* WM_Paint가 출력문자가 있으면 계속 다시 그려 안지워짐.

* buf의 0번째 자리를 초기화 해서 아무것도 출력되지 않도록 처리.
---------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )
{
        HDC hdc;
        PAINTSTRUCT ps;

        static int x;
        static int y;
        static WCHAR wcbuf[100];
        static WCHAR str[256]; //WCHAR 2바이트 / TCHAR은 4바이트
       
        int len;
       
        switch ( iMessage)
       {
        case WM_MOUSEMOVE:
              x = LOWORD( lParam);
              y = HIWORD( lParam);
               wsprintf(wcbuf, L "x = %d, y = %d", x, y);
              InvalidateRect( hWnd, NULL, TRUE);
               return 0;
        case WM_PAINT:
              hdc = BeginPaint( hWnd, &ps);
               TextOut(hdc, 50, 50, wcbuf, lstrlen(wcbuf)); //아스키1바이트만 지원 그래서 L
              EndPaint( hWnd, &ps);
               return 0;
        case WM_LBUTTONDBLCLK:
              wcbuf[0] = 0;
              InvalidateRect( hWnd, NULL, TRUE);
               return 0;


        case WM_DESTROY:
              PostQuitMessage(0);
               return 0;
       }
        return( DefWindowProc(hWnd , iMessage , wParam , lParam ));

}


---------------------------------------------------


✔ 4-3-가. 타이머

- CREATE : 창이 생성될때 딱한번 호출 된다.
- setTimer: 타이머를 만드는 함수(hWnd(윈도우), 1(타이머 번호), 시간간격, 함수주소)
* 타이머 번호 우리가 알고 있어야 한다.
- 함수 주소 안넣으면 :WTimer이라는 메시지만 만들어진다. (주소를 적으면 시간간격으로 호출한다.)
- 현재 시간을 알아내는 mytime 현재시간을 초로 가져온다.

- 글자 형태의 시간. 을 계속 뿌린다. InvalidateRect 
- killTimer (, 1): 타이머 번호와 일치해야 한다.
- 내부적으로 동적할당 받는 것 같아 닫아줘야 한다. 만들때 번호 받을때 번호 일치!

***게임에서 
블럭이 떨어지는 속도

미사일의 속도***

---------------------------------------------------

LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )

{
        HDC hdc;
        PAINTSTRUCT ps;
       
        //time_t mytime;
        //static HANDLE hTimer;
        SYSTEMTIME st;
        static TCHAR sTime[128];
       
        switch ( iMessage)
       {
        case WM_CREATE:
              SetTimer( hWnd, 1, 1000, NULL);
               //SendMessage(hWnd, WM_TIMER, 1, 0);
               return 0;
        case WM_TIMER:
              GetLocalTime(&st);
               wsprintf
                     (
                           sTime
                           , TEXT( "지금 시간 : %d: %d: %d 입니다." )
                           , st.wHour
                           , st.wMinute
                           , st.wSecond
                     );
              InvalidateRect( hWnd, NULL, TRUE);
               return 0;
        case WM_PAINT:
              hdc = BeginPaint( hWnd, &ps);
               TextOut(hdc, 100, 100, sTime, lstrlen(sTime));
              EndPaint( hWnd, &ps);
               return 0;
        case WM_DESTROY:
              KillTimer( hWnd, 1);
              PostQuitMessage(0);
               return 0;
       }
        return( DefWindowProc(hWnd , iMessage , wParam , lParam ));

}


---------------------------------------------------

✔ 4-3-나. SendMessage

* 시계 프로그램의 문제1 : 1초 정도 경과후 시간 출력
- WM_TIMER 메시지는 최초로 호출 되는 시점이 프로그램 시작 1초후다. 
- WM_CREATE 에서 타이머 설치 할때 주기를 1초로 주었다. 
- WM_TIMER메시지를 강제로 발생시켜주기 위해 SendMessage 함수를 사용한다(hWnd 윈도우로 Msg메시지를 보냄)


- 타이머가 여러개면 switch 안에 switch 

     - 오락의 총알이 다르게 날라온다. 즉 타이머가 다르게 동작한다.


* 메시지 기반 운영체제인 윈도우즈의 SendMessage 함수는 빈번히 사용된다.


* 시계 프로그램의 문제2 : 몽땅지워지고 다시 출력되어 깜빡거린다. 그래서 무효화 영역을 최소화 한다. 

---------------------------------------------------

LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )
{
        HDC hdc;
        PAINTSTRUCT ps;

        SYSTEMTIME st;
        static RECT rt = { 100, 100, 400, 120 };
        static TCHAR sTime[128];


        switch ( iMessage)
       {
        case WM_CREATE:
              SetTimer( hWnd, 1, 1000, NULL);
               SendMessage(hWnd , WM_TIMER , 1, 0);
               return 0;

        case WM_TIMER:
              GetLocalTime(&st);
               wsprintf
                     (
                           sTime
                           , TEXT( "지금 시간 : %d: %d: %d 입니다." )
                           , st.wHour
                           , st.wMinute
                           , st.wSecond
                     );
              InvalidateRect(hWnd, &rt, TRUE);
               return 0;

        case WM_PAINT:
              hdc = BeginPaint( hWnd, &ps);
               TextOut(hdc, 100, 100, sTime, lstrlen(sTime));
              EndPaint( hWnd, &ps);
               return 0;
        case WM_DESTROY:
              KillTimer( hWnd, 1);
              PostQuitMessage(0);
               return 0;
       }
        return( DefWindowProc(hWnd , iMessage , wParam , lParam ));

}


✔ 4-3-다 . 두개의 타이머

* 타이머는 한꺼번에 여러개 설치 가능 .
- 16비트 윈도우즈에서 타이머는 최대 16개까지 설치할수 있다.

* 필요한 만큼 SetTimer를 호출 

- 1초간격 타이머

- 5초간격 타이머



4-3-라. 콜백 함수


프로그램이 실행되는 동안 지속적으로 수행해야 할 작업이 있다고 해 보자. 

예를 들어 로고 애니메이션이나 백그라운드 음악 연주 등을 들 수 있는데 도스에서라면 다음과 같이 코드를 작성할 것이다.
for(;;) {
        지속적인 작업
        기타 작업
}
무한 루프가 전체 프로그램 코드를 감싸고 있고 이 루프 안에서 지속적으로 해야할 작업과 그외 작업을 수행하고 있다. 
도스에서는 이런식으로 프로그램을 작성하는 것이 가능하며 실제로 이렇게 한다. 
그러나 윈도우즈와 같은 멀티 태스킹 환경에서는 이런 방식을 사용해서는 안된다. 

왜냐하면 한 프로그램이 제어권을 독점하고 있어서는 안되며 다른 프로그램도 실행시간을 가져야 하기 때문이다. 
사용자는 수시로 작업 전환을 할 수 있어야 하는데 한 프로그램이 CPU를 독차지하고 있으면 안된다. 
그래서 CPU를 독점하는 이런 무한루프를 작성해서는 안되며 반드시 메시지가 전달되었을 때에 한해 필요한 작업을 하도록 해야 한다. 

이럴 때 사용하는 메시지가 바로 타이머 메시지이다.


✔ SetTimer 
- 4번째 인수 : 타이머 프로시저 함수의 포인터를 가리킨다.
- NULL일 경우 첫번째 인수 hWnd로 WM_TIMER메시지가 전달된다.

함수를 만들어 네번째 인수에 함수명을 적어줌. 함수가 지정되면 시간마다 메시지 대신 함수를 호출

VOID CALLBACK TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ); 
(hwnd   : 타이머를 소유한 윈도우 핸들, , , dwTime : 윈도우즈가 실행된 후의 경과시간)

* 콜백함수와 WM_TIMER메시지 의 차이점
- WM_TIMER메시지는 다른 메시지가 있을 경우 실행 순서에 밀려 늦게 호출 되는 경우가 있음.
- 콜백함수를 사용하면 정확한 시간에 호출된다.

* 정확도를 필요로 하면 타이머 메시지보다 콜백 함수를 사용하는 것이 더 좋다.
* 지속적인 작업(for문에서 폴링방식)

✔ 콜백(CALLBACK Func)
일반적으로 API 함수들은 운영체제가 제공하며 프로그램에서는 이 함수들을 호출해서 운영체제의 서비스를 받는다.  
예를 들어 도스의 시스템 콜 함수를 호출하여 디스크 입출력을 받는다든가 
윈도우즈의 TextOut 함수를 호출하여 문자열을 출력하도록 하는 경우가 이에 해당한다. 

반면 콜백 함수는 응용 프로그램이 제공하며 운영체제가 필요할 때 호출하는 함수로 호출되는 방향이 거꾸로 되어 콜백이라고 부르는 것이다.
"운영체제에 의해 호출되는 프로그램 내부의 함수"라고 할 수 있다

* 타이머의 콜백함수가 대표적이다. - 열거 함수 , 그래픽 함수등의 콜백함수도 사용한다.

* 콜백 함수의 예로 메시지 처리함수 WndProc이 있다.
메시지가 발생할때마다 윈도우즈가 호출해주며 응용프로그램 내부에 있지만 응용프로그램에서 직접 함수를 호출하지 않는다. 오직 운영체제만이 함수를 호출한다.

<0.1초마다 점을찍는다. 100개씩>
---------------------------------------------------
void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
        HDC hdc;
        int i;
       hdc = GetDC( hWnd);
        for (i = 0; i < 100; i++)
       {
              SetPixel(hdc, rand() % 500, rand() % 400,
                      RGB(rand() % 256, rand() % 256, rand() % 256, ));
              ReleaseDC( hWnd, hdc);
       }

}

LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )
{
        switch ( iMessage)
       {
        case WM_CREATE:
              SetTimer( hWnd, 1, 100, ( TIMERPROC)TimerProc);
               return 0;

        case WM_DESTROY:
              KillTimer( hWnd, 1);
              PostQuitMessage(0);
               return 0;
       }
        return( DefWindowProc(hWnd , iMessage , wParam , lParam ));
}



---------------------------------------------------

✔ 4-4- 가. 생성및 파괴
- 윈도우가 생성될때 보내지는 WM_CREATE  : 각종 초기화하기에 적합한 장소
- 윈도우가 파괴될때 보내지는 WM_DESTROY : 종료처리하기에 적합

✔ 4-4- 나. 작업영역
- 우리가 생성한 getclientrect 우리가 만든 클라이언트 정보를 rt에 넣어준다.
---------------------------------------------------
<생성할때 크기를 알아내고, 정중앙을 계산하고 글자를 출력.>
LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )
{
        HDC hdc;
        PAINTSTRUCT ps;
        static RECT rt;
        switch ( iMessage)
       {
        case WM_CREATE:
              GetClientRect( hWnd, &rt);
               return 0;
        case WM_PAINT:
              hdc = BeginPaint( hWnd, &ps);
              SetTextAlign(hdc, TA_CENTER);
               TextOut(hdc, rt.right / 2, rt.bottom / 2, L "Center String", 13);
              EndPaint( hWnd, &ps);
               return 0;

        case WM_DESTROY:
              PostQuitMessage(0);
               return 0;
       }
        return ( DefWindowProc(hWnd , iMessage , wParam , lParam ));
}

- 메뉴(바)는 원점(0,0)이 아니다.

---------------------------------------------------

✔ 4-4- 다. WM_SIZE
- 좌표를 기준으로 중앙에 문자를 출력 
  -> 출력된 윈도우 크기를 변경하면 작업 영역의 크기가 달라져 중앙이 아니게 된다.
  -> 계속 중앙 : 창 크기가 변경되면 다시 출력.

플레그
SIZE_MAXHIDE다른 윈도우가 최대화되어 이 윈도우가 가려졌다.
SIZE_MAXIMIZED최대화되었다.
SIZE_MAXSHOW다른 윈도우가 원래 크기로 복구되어 이 윈도우가 드러났다.
SIZE_MINIMIZED최소화되었다.
SIZE_RESTORED크기가 변경되었다.
---------------------------------------------------

LRESULT CALLBACK WndProc(HWND hWnd , UINT iMessage , WPARAM wParam , LPARAM lParam )
{
        HDC hdc;
        PAINTSTRUCT ps;
        static RECT rt;
        switch ( iMessage)
       {
        /*case WM_CREATE:
              GetClientRect(hWnd, &rt);
              return 0;*/
        case WM_PAINT:
              hdc = BeginPaint( hWnd, &ps);
              SetTextAlign(hdc, TA_CENTER);
               TextOut(hdc, rt.right / 2, rt.bottom / 2, L "Center String", 13);
              EndPaint( hWnd, &ps);
               return 0;
        case WM_SIZE:
              GetClientRect(hWnd, &rt);   // 창크기를 알아냄
              InvalidateRect(hWnd, NULL, TRUE);

              또는
              /*rt.right = LOWORD(lParam);
              rt.bottom = HIWORD( lParam);
              InvalidateRect(hWnd, NULL, TRUE);*/
              return 0;
        case WM_DESTROY:
              PostQuitMessage(0);
               return 0;
       }
        return ( DefWindowProc(hWnd , iMessage , wParam , lParam ));
}

---------------------------------------------------

✔ 4-4- 라. WM_MOVE
- 창을 이동 시키면 호출한다.

✔ 5-1- 가
- 리소스의 분리
- 리소스 편집기>> 메뉴

✔ 5-2-가. 리소스 작성
- 프로젝트 만들고, 소스를 붙인다.
- 디테일 한것은 프로퍼티즈에서 설정한다.(버전이 바뀌면서 그래픽 요소가 텍스트화 됨.)

* 메뉴를 만들어서 properties를 보면 캡션에 - 아이디를 자동으로 부여한다.



예> VS이 자동으로 추가함.(숫자 알아서 넣는다.) 메뉴를 번호로 관리 하므로 고유번호를 알아서 메김 알아서 디파인 이름 정하고, 우리가 적은 메뉴이름과비슷하게 만들어 40001, 40002 .. 식으로 번호 매김. 편함..(반면 바보가 되는 개발자)

* 메뉴리소스는 고유 번호는 101번이다.
- 하부 메뉴는 40001 이고, 이 번호는 줄어들지 않는다. 시작 번호를 기준으로 무조건 증가한다. 완전한 고유 번호를 인지 하기 위해 삭제했던 번호는 다시 사용안한다.
* 메뉴 선택 = 해당 메뉴의 함수호출 의미이므로 반드시 고유해야 한다.

* 어떤 메뉴를 선택해도 WM_COMMAND가 호출
- wParam에 메뉴번호가 들어온다 메뉴를 건드리는게 w커멘드다. 그래서 고유한 번호를 부여한다.

<메뉴 만들기>
1. #include "resource.h"  추가
2. 메뉴를 만든다.
3. 윈메인을 추가한다(등록한다)

4. 자동으로 발급된 아이디에 따라서 배치하면 끝난다. 



✔ 5-2-나. WM_COMMAND

설명
lParam통지 메시지를 발생시킨 컨트롤의 윈도우 핸들
LOWORD(wParam)메뉴나 액셀러레이터, 컨트롤의 ID
HIWORD(wParam)컨트롤이 보내주는 통지 메시지, 메뉴가 선택된 경우는 0이 되며 액셀러레이터가 선택된 경우는 1이 된다.

✔ 5-2-다.메뉴 편집기의 사용법 읽어보기

✔ 5-3 아이콘, 커서




728x90