◉4-1. 입력
●4-1-라. TranslateMessage
- 키보드에서 A키를 눌렀다가 뗐다고 가정하자.
- WM_KEYDOWN, WH_CHAR, WM_KEYUP이 순서대로 발생한다.
- WM_CHAR은 WM_KETDOWN에 의해 추가로 발생하는 메시지이다.
![](https://t1.daumcdn.net/cfile/tistory/26231D4F564958EF35)
- GetMessage는 메시지 큐에서 메시지를 꺼내와서 TranslateMessage로 넘겨준다.
- TranslateMessage는 WM_KEYDOWN인지와 눌려진 키가 문자키인지 검사한다.
- 조건이 맞을 경우 WM_CHAR 메시지를 만든다.
- 맞지 않거나 문자입력이 아닐 경우 아무것도 하지 않는다.
- DispatchMessage에 의해 WndProc으로 보내진다.
◉4-2. 마우스 입력
●4-2-가. Mouse
- 키보드에서 wParam과 lParam을 통해 정보를 받아오듯이 마우스도 정보를 받을 수 있다.
- lParam을 통해 좌표를 확인할 수 있다.
- 창을 기준으로 처리된다.
- 절대 좌표가 아니다.
- wParam에서 눌러져 있는 키를 확인할 수 있다.
![](https://t1.daumcdn.net/cfile/tistory/264C1E4F564958EB0E)
- 소스
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_DESTROY: PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd, iMessage, wParam, lParam)); } |
- 결과
![](https://t1.daumcdn.net/cfile/tistory/21507A4F564958EC0A)
- 가로, 세로의 한계가 존재한다.
- X의 좌표는 0000 FFFF와 비트 연산한다.
- Y의 좌표는 >>16 쉬프트한다.
- BOOL이 실제로 존재하지는 않는다.
- define되어 있다.
- window.h를 include하면 사용 가능하다.
●마우스의 좌표 출력하기
- 마우스의 좌표 출력하기
- 유니코드 인식 함수들은 표준 함수와 이름이 비슷하되 앞에 l자가 하나 더 붙는다는 것만 다르며, 운영체제가 제공하는 API함수이므로 별도의 용량을 차지하지 않는다는 장점이 있다.
- 소스
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; static int x; static int y; static WCHAR wcBuffer[100];
switch (iMessage) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); SetTextAlign(hdc, TA_CENTER); TextOut(hdc, 100, 100, wcBuffer, lstrlen(wcBuffer)); EndPaint(hWnd, &ps); return 0; case WM_MOUSEMOVE: x = LOWORD(lParam); y = HIWORD(lParam); wsprintf(wcBuffer, L"x는 %d, y는 %d이다.", x, y); InvalidateRect(hWnd, NULL, TRUE); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd, iMessage, wParam, lParam)); } |
- 결과
![](https://t1.daumcdn.net/cfile/tistory/2749354F564958EE10)
●4-2-나. 더블클릭
- 윈도우는 더블클릭에 대한 메시지를 지원하지 않는다.
- 더블클릭용 Flag를 추가해준다.
- WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
- 더블클릭을 검출하는 데는 실행시간의 감소가 요구된다.
- 소스
case WM_LBUTTONDBLCLK: wcBuffer[0] = 0; //wcBuffer을 지운다. InvalidateRect(hWnd, NULL, TRUE); return 0; |
- 결과
◉4-3. 타이머
●4-3-가. 시계
- WM_CREATE
- 창이 만들어질 때 한번만 동작한다.
- SetTimer
- 1. 윈도우 핸들러
- 2. 타이머 번호
- 여러개를 만들 수 있다.
- 3. 시간(ms)
- 1000 : 1초마다 동작
- 4. 함수주소
- 적지 않으면, WM_TIMER라는 메시지만 생성된다.
- WM_TIMER
- time
- 현재 시간을 초로 가지고 있다.
- ctime
- 문자열로 바꿔준다.
- WM_DESTROY
- KillTimer
- 타이머 번호를 입력해서, 닫아주어야 한다.
- 일치해야 닫아진다.
- 소스
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; 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일 %d:%d:%d입니다.") , st.wYear , st.wMonth , st.wDay , 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)); }
|
- 결과
![](https://t1.daumcdn.net/cfile/tistory/254F644F564958F00B)
●4-3-나. SendMessage
- 실행시킨 직후에 출력이 되지 않는다.
- 문제는 WM_CREATE
- SetTimer에서 주기를 1초로 하여 실행 후에 1초가 지나야 출력이 된다.
- 강제로 메시지를 발생시킨다.
- WM_TIMER
- WM_TIMER 안에서 새로운 스위치 문을 만든다.
- 소스
static RECT rt = { 100, 100, 400, 120 };
switch (iMessage) { case WM_CREATE: SetTimer(hWnd, 1, 1000, NULL); SendMessage(hWnd, WM_TIMER, 1, 0); return 0; case WM_TIMER: InvalidateRect(hWnd, &rt, TRUE); //부분 무효화
|
- 결과
-
●4-3-다. 두 개의 타이머
- 타이머는 한꺼번에 여러 개를 설치하여 사용할 수도 있다.
- 타이머를 2개 사용한 예제
- 1번 Timer는 현재 시간을 계속 출력한다.
- 2번 Timer는 5초 간격으로 메시지 박스를 출력한다.
- 소스<1>
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; SYSTEMTIME st; static TCHAR sTime[128]; static RECT rt = { 100, 100, 500, 120 };
switch (iMessage) { case WM_CREATE: SetTimer(hWnd, 1, 1000, NULL); SetTimer(hWnd, 2, 5000, 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); switch (wParam) { case 1: InvalidateRect(hWnd, &rt, TRUE); break; case 2: MessageBox(hWnd, L"5초가 지났습니다.", L"Double Timer", MB_OK); break; } 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); KillTimer(hWnd, 2); PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd, iMessage, wParam, lParam)); }
|
- 소스<2>
long FAR PASCAL WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; time_t mytime; static HANDLE hTimer, hTimer2; static char *str; static RECT rt={100,100,400,120}; switch(iMessage) { case WM_CREATE: hTimer=(HANDLE)SetTimer(hWnd,1,1000,NULL); hTimer2=(HANDLE)SetTimer(hWnd,2,5000,NULL); str=""; SendMessage(hWnd, WM_TIMER, 1, 0); return 0; case WM_TIMER: switch (wParam) { case 1: time(&mytime); str=ctime(&mytime); InvalidateRect(hWnd,&rt,TRUE); break; case 2: MessageBox(hWnd, L"5초가 지났습니다.", L"Double Timer", MB_OK); break; } return 0; case WM_PAINT: hdc=BeginPaint(hWnd,&ps); TextOut(hdc,100,100,str,strlen(str)-1); EndPaint(hWnd,&ps); return 0; case WM_DESTROY: KillTimer(hWnd,1); KillTimer(hWnd,2); PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd,iMessage,wParam,lParam)); } |
- 결과
●4-3-라. 콜백함수
- 운영체제에 의해 호출되는 프로그램 내부의 함수.
- 오직 운영체제만이 호출할 수 있다.
- SetTimer의 4번째 인자에 함수가 인자로 들어있다면, 정해진 시간마다 함수를 실행한다.
- 앞에서 언급 했듯이 NULL을 입력하면 WM_TIMER 메시지가 호출된다.
- 타이머 메시지를 받을 때 마다 조금씩 나누어 하는 것이 좋다.
- 타이머 메시지 보다 정확한 시간에 호출된다.
- 소스
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()%100, rand()%100, 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)); } |
- 결과
![](https://t1.daumcdn.net/cfile/tistory/2409743856497B5004)
- 예제에서는 WM_PAINT가 없으므로 저장이 되지 않아 지워진다.![](https://t1.daumcdn.net/cfile/tistory/2557173856497B5230)
◉4-4. 윈도우 관리 메시지
●4-4-가. 생성 및 파괴
- 윈도우와 관련된 메시지중 가장 간단한 메시지는 윈도우가 생성될 대 보내지는 WM_CREATE와 윈도우가 파괴될 때 보내지는 WM_DESTROY 2가지가 있다.
- 위의 메시지 대신 사용할 수 있는 방법은 WinMain에서 직접 초기화와 종료 처리를 해주는 방법이 있다.
●4-4-나. 작업영역
- GetClientRect
- 소스
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; static RECT rt; static int x; static int y; static WCHAR wcBuffer[100]; 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, TEXT("Center String"), 13); TextOut(hdc, 100, 100, wcBuffer, lstrlen(wcBuffer)); EndPaint(hWnd, &ps); return 0; case WM_MOUSEMOVE: x = LOWORD(lParam); y = HIWORD(lParam); wsprintf(wcBuffer, TEXT("x는 %d, y는 %d이다."), x, y); InvalidateRect(hWnd, NULL, TRUE); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd, iMessage, wParam, lParam)); } |
- 결과
![](https://t1.daumcdn.net/cfile/tistory/2560FD3856497B5327)
●4-4-다. WM_SIZE
- 윈도우의 창 크기를 변경 할 때마다 호출된다.
- 6교시만 하고 가게되어 정리는 여기까지 입니다 ㅠ.