일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 강의
- 졸업 작품
- 충남 천안
- 산책하기 좋은 곳
- 언제나 휴일
- 알고리즘
- 표준 입출력
- 프로젝트
- 클래스 다이어그램
- 캡슐화
- 안드로이드 앱 개발
- C++
- Windows Forms
- 동영상 강의
- 졸업 작품 소재
- 무료 동영상 강의
- 언제나휴일
- 실습
- c#
- 유튜브 동영상 강의
- 추천
- 실습으로 다지는 c#
- 소켓 통신
- 표준 라이브러리 함수
- 소스 코드
- 동영상
- 파이썬
- 네트워크 프로그래밍
- c언어
- 원격 제어 프로그램
- Today
- Total
프로그래밍 언어 및 기술 [언제나휴일]
6. 타이머 입력 [Windows API] 본문
안녕하세요. 언제나휴일입니다.
Windows API에서 주기적인 작업을 할 수 있게 타이머를 제공하고 있어요.
타이머를 생성할 때는 SetTimer 함수를 이용하고 해제할 때는 KillTimer를 사용합니다.
UINT_PTR WINAPI SetTimer(HWND hWnd,UINT_PTR nIDEvent,UINT uElapse, TIMERPROC lpTimerFunc);
hWnd: 타이머 메시지를 처리할 윈도우
nIDEvent: 타이머 일련 번호(개발자가 정의)
uElapse: 타이머 메시지를 발생할 주기(1/1000초 단위)
lpTimerFunc: 타이머 메시지를 수행할 함수(NULL 전달하면 hWnd의 콜백 프로시저에 WM_TIMER 메시지 발생
typedef VOID (CALLBACK* TIMERPROC)(HWND, UINT, UINT_PTR, DWORD);
타이머 메시지는 SetTimer를 호출하였을 때 바로 발생하지 않습니다. 세 번째 인자로 전달한 시간이 흘렀을 때 처음으로 타이머 메시지가 발생합니다.
SetTimer 함수를 호출할 때 4번째 인자인 타이머 콜백 프로시저를 전달하지 않고 NULL을 전달할 수도 있습니다.
타이머 콜백 프로시저를 전달하면 주기적으로 타이머 콜백 프로시저가 동작합니다.
NULL을 전달하면 hWnd의 윈도우 콜백 프로시저에 WM_TIMER 메시지가 전달됩니다.
BOOL WINAPI KillTimer(HWND hWnd,UINT_PTR uIDEvent);
hWnd: SetTimer에서 설정한 hWnd
nIDEvent: 타이머 일련 번호, SetTimer에서 전달한 값
두 개의 타이머를 이용하는 간단한 코드를 작성해 봅시다.
첫 번째 타이머는 1초 주기로 발생하고 타이머 콜백 프로시저에서는 현재 시각을 출력하게 합시다.
두 번째 타이머는 1/100초 주기로 발생하고 프로그램 시작한 후 얼마나 흘렀는지 틱(특정 목적으로 계측하기 위한 단위로 여기에서는 1/100초를 1틱) 수를 출력하게 합시다.
먼저 윈도우 생성 메시지 처리에서 두 개의 타이머를 생성합니다. 첫 번째 타이머는 타이머 콜백 프로시저로 전달하고 두 번째 타이머는 NULL을 전달할게요. (둘 다 타이머 콜백 프로시저를 전달하거나 NULL을 전달해도 문제 없어요. 여기에서는 학습 목적으로 두 가지 방법을 모두 사용하는 것입니다.)
#define IDT_CLOCK 1
#define IDT_TICK 2
VOID CALLBACK OnClock(HWND, UINT, UINT_PTR, DWORD);
void OnTick(HWND hWnd);
void OnCreate(HWND hWnd)
{
SetTimer(hWnd, IDT_CLOCK, 1000, OnClock);
SetTimer(hWnd, IDT_TICK, 1 / 10, NULL);
}
void OnTimer(HWND hWnd, DWORD tid)
{
switch (tid)
{
case IDT_TICK: OnTick(hWnd); return;
}
}
void OnDestroy(HWND hWnd)
{
KillTimer(hWnd, IDT_CLOCK);
KillTimer(hWnd, IDT_TICK);
PostQuitMessage(0);//WM_QUIT 메시지를 발급, 메시지 루프 종료 위함
}
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch (iMessage)
{
case WM_CREATE: OnCreate(hWnd); return 0;
case WM_PAINT: OnPaint(hWnd); return 0;
case WM_TIMER: OnTimer(hWnd, wParam); return 0;
case WM_DESTROY: OnDestroy(hWnd); return 0;
}
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
1초 주기의 타이머 콜백 프로시저에서는 현재 시각을 얻어옵니다. 출력 내용을 바꾸기 위해 무효화 영역을 발생시키세요.
OnTick 프로시저에서는 ticks를 1 증가 시킵니다. 출력 내용을 바꾸기 위해 무효화 영역을 발생시키세요.
SYSTEMTIME st;
int ticks;
VOID CALLBACK OnClock(HWND hWnd, UINT iMessage, UINT_PTR tid, DWORD cnt)
{
GetLocalTime(&st);
InvalidateRect(hWnd, 0, 0);
}
void OnTick(HWND hWnd)
{
ticks++;
InvalidateRect(hWnd, 0, 0);
}
다음은 전체 소스 코드입니다.
//Windows API
//두 개의 타이머를 사용하는 실습
//첫 번째 타이머: 1초 주기, 시각 출력
//두 번째 타이머: 1/100초 주기, 수행한 틱 수 출력
#include <Windows.h>
#define MY_DEF_STYLE CS_HREDRAW | CS_VREDRAW|CS_DBLCLKS
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
void RegMyWndClass(LPCWSTR cname);
void MakeWindow(LPCWSTR cname);
void MsgLoop();
INT APIENTRY WinMain(HINSTANCE hIns, HINSTANCE hPrev, LPSTR cmd, INT nShow)
{
RegMyWndClass(TEXT("MyWindow"));
MakeWindow(TEXT("MyWindow"));
MsgLoop();
}
void RegMyWndClass(LPCWSTR cname)
{
//윈도우 클래스 속성 설정
WNDCLASS wndclass = { 0 };
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hInstance = GetModuleHandle(0);
wndclass.hIcon = LoadIcon(0, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(0, IDC_ARROW);
wndclass.lpfnWndProc = MyWndProc;
wndclass.lpszMenuName = 0;
wndclass.lpszClassName = cname;
wndclass.style = MY_DEF_STYLE;
//윈도우 클래스 등록
RegisterClass(&wndclass);
}
void MakeWindow(LPCWSTR cname)
{
//윈도우 개체 생성
HWND hWnd = CreateWindow(cname,//클래스 이름
TEXT("두 개의 타이머 "),//타이틀 명
WS_OVERLAPPEDWINDOW,//윈도우 스타일
100, 30, 700, 600,//Left, Top, Width, Height
0, //부모 윈도우 핸들
0,//메뉴
GetModuleHandle(0),//모듈 핸들
0 //WM_CREATE에 전달할 인자
);
//윈도우 개체 시각화
ShowWindow(hWnd, SW_SHOW);
}
void MsgLoop()
{
MSG Message;
while (GetMessage(&Message, 0, 0, 0))//응용 큐에서 메시지를 꺼내오기
{
DispatchMessage(&Message);//메시지 수행(콜백 가동)
}
}
SYSTEMTIME st;
int ticks;
VOID CALLBACK OnClock(HWND hWnd, UINT iMessage, UINT_PTR tid, DWORD cnt)
{
GetLocalTime(&st);
InvalidateRect(hWnd, 0, 0);
}
void OnTick(HWND hWnd)
{
ticks++;
InvalidateRect(hWnd, 0, 0);
}
void OnDraw(HDC hdc)
{
WCHAR buf[256];
wsprintf(buf, TEXT("현재 시간 : %d:%d:%d"), st.wHour, st.wMinute, st.wSecond);
TextOut(hdc, 10, 10, buf, lstrlen(buf));
wsprintf(buf, TEXT("%8d"), ticks);
TextOut(hdc, 10, 40, buf, lstrlen(buf));
}
void OnPaint(HWND hWnd)
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
OnDraw(ps.hdc);
EndPaint(hWnd, &ps);
}
#define IDT_CLOCK 1
#define IDT_TICK 2
void OnCreate(HWND hWnd)
{
SetTimer(hWnd, IDT_CLOCK, 1000, OnClock);
SetTimer(hWnd, IDT_TICK, 1 / 10, NULL);
}
void OnTimer(HWND hWnd, DWORD tid)
{
switch (tid)
{
case IDT_TICK: OnTick(hWnd); return;
}
}
void OnDestroy(HWND hWnd)
{
KillTimer(hWnd, IDT_CLOCK);
KillTimer(hWnd, IDT_TICK);
PostQuitMessage(0);//WM_QUIT 메시지를 발급, 메시지 루프 종료 위함
}
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch (iMessage)
{
case WM_CREATE: OnCreate(hWnd); return 0;
case WM_PAINT: OnPaint(hWnd); return 0;
case WM_TIMER: OnTimer(hWnd, wParam); return 0;
case WM_DESTROY: OnDestroy(hWnd); return 0;
}
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
이 프로그램을 실행시키면 시작 시점에는 현재 시각을 0:0:0으로 출력합니다.
시작하자 마자 현재 시각을 정상적으로 출력하기 위해서는 OnCreate에서 현재 시각을 얻어오는 작업을 수행하여야 합니다.
PostMessage 혹은 SendMessage로 WM_TIMER 메시지를 보내면 윈도우 콜백 프로시저에서 WM_TIMER를 처리합니다.
현재 시각을 출력하기 위한 목적의 타이머는 타이머 콜백 프로시저를 직접 등록하였기 때문에 PostMessage나 SendMessage로 WM_TIMER 메시지를 보내는 것은 현명한 방법이 아닙니다.
오히려 여기에서는 OnCreate 함수에서 GetLocalTime을 호출하는 것이 나은 방법일 수 있어요.
//Windows API
//두 개의 타이머를 사용하는 실습
//첫 번째 타이머: 1초 주기, 시각 출력
//두 번째 타이머: 1/100초 주기, 수행한 틱 수 출력
#include <Windows.h>
#define MY_DEF_STYLE CS_HREDRAW | CS_VREDRAW|CS_DBLCLKS
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
void RegMyWndClass(LPCWSTR cname);
void MakeWindow(LPCWSTR cname);
void MsgLoop();
INT APIENTRY WinMain(HINSTANCE hIns, HINSTANCE hPrev, LPSTR cmd, INT nShow)
{
RegMyWndClass(TEXT("MyWindow"));
MakeWindow(TEXT("MyWindow"));
MsgLoop();
}
void RegMyWndClass(LPCWSTR cname)
{
//윈도우 클래스 속성 설정
WNDCLASS wndclass = { 0 };
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hInstance = GetModuleHandle(0);
wndclass.hIcon = LoadIcon(0, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(0, IDC_ARROW);
wndclass.lpfnWndProc = MyWndProc;
wndclass.lpszMenuName = 0;
wndclass.lpszClassName = cname;
wndclass.style = MY_DEF_STYLE;
//윈도우 클래스 등록
RegisterClass(&wndclass);
}
void MakeWindow(LPCWSTR cname)
{
//윈도우 개체 생성
HWND hWnd = CreateWindow(cname,//클래스 이름
TEXT("두 개의 타이머 "),//타이틀 명
WS_OVERLAPPEDWINDOW,//윈도우 스타일
100, 30, 700, 600,//Left, Top, Width, Height
0, //부모 윈도우 핸들
0,//메뉴
GetModuleHandle(0),//모듈 핸들
0 //WM_CREATE에 전달할 인자
);
//윈도우 개체 시각화
ShowWindow(hWnd, SW_SHOW);
}
void MsgLoop()
{
MSG Message;
while (GetMessage(&Message, 0, 0, 0))//응용 큐에서 메시지를 꺼내오기
{
DispatchMessage(&Message);//메시지 수행(콜백 가동)
}
}
SYSTEMTIME st;
int ticks;
VOID CALLBACK OnClock(HWND hWnd, UINT iMessage, UINT_PTR tid, DWORD cnt)
{
GetLocalTime(&st);
InvalidateRect(hWnd, 0, 0);
}
void OnTick(HWND hWnd)
{
ticks++;
InvalidateRect(hWnd, 0, 0);
}
void OnDraw(HDC hdc)
{
WCHAR buf[256];
wsprintf(buf, TEXT("현재 시간 : %d:%d:%d"), st.wHour, st.wMinute, st.wSecond);
TextOut(hdc, 10, 10, buf, lstrlen(buf));
wsprintf(buf, TEXT("%8d"), ticks);
TextOut(hdc, 10, 40, buf, lstrlen(buf));
}
void OnPaint(HWND hWnd)
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
OnDraw(ps.hdc);
EndPaint(hWnd, &ps);
}
#define IDT_CLOCK 1
#define IDT_TICK 2
void OnCreate(HWND hWnd)
{
SetTimer(hWnd, IDT_CLOCK, 1000, OnClock);
SetTimer(hWnd, IDT_TICK, 1 / 10, NULL);
GetLocalTime(&st);
}
void OnTimer(HWND hWnd, DWORD tid)
{
switch (tid)
{
case IDT_TICK: OnTick(hWnd); return;
}
}
void OnDestroy(HWND hWnd)
{
KillTimer(hWnd, IDT_CLOCK);
KillTimer(hWnd, IDT_TICK);
PostQuitMessage(0);//WM_QUIT 메시지를 발급, 메시지 루프 종료 위함
}
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch (iMessage)
{
case WM_CREATE: OnCreate(hWnd); return 0;
case WM_PAINT: OnPaint(hWnd); return 0;
case WM_TIMER: OnTimer(hWnd, wParam); return 0;
case WM_DESTROY: OnDestroy(hWnd); return 0;
}
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
'C & C++ > Windows API 예제' 카테고리의 다른 글
7. 첫 번째 실습 – 도형 이동 시키기 [Windows API] (1) | 2024.01.26 |
---|---|
5. 키보드 입력 [Windows API] (1) | 2024.01.25 |
4. 마우스 입력 (0) | 2024.01.25 |
3. 기본 그리기 [Windows API] (1) | 2024.01.24 |
2. 윈도우 클래스 등록 및 윈도우 개체 생성 (0) | 2024.01.24 |
1. Windows API 소개 (1) | 2023.12.26 |
테트리스 처음부터 끝까지 [C++, Windows API] (0) | 2023.12.25 |