Bộ định thời gian trong lập trình c trên windows
Như chúng ta đã biết Windows cung cấp cho ta các thông tin thông qua dạng thông điệp như là thông điệp bàn phím, thông điệp phát sinh từ thiết bị chuột . Và ngoài ra thì một thông điệp cũng rất hữu dụng là thông điệp thời gian. Khi viết một ...
Như chúng ta đã biết Windows cung cấp cho ta các thông tin thông qua dạng thông điệp như là thông điệp bàn phím, thông điệp phát sinh từ thiết bị chuột . Và ngoài ra thì một thông điệp cũng rất hữu dụng là thông điệp thời gian. Khi viết một chương trình chúng ta có thể yêu cầu hệ điều hành gởi một thông điệp cảnh báo theo từng khoảng thời gian nhất định để chúng ta có thể làm một số xử lý cần thiết. Thông điệp này được gởi từ hệ điều hành đến chương trình thông qua một bộ định thời gian (Timer) và thông điệp được phát sinh là WM_TIMER. Việc dùng chức năng này rất đơn giản, ta khai báo một bộ định thời gian và thiết lập thông số khoảng thời gian để Windows phát sinh thông điệp Timer cho ứng dụng. Khi đó ứng dụng chỉ cần xử lý thông điệp WM_TIMER trong hàm xử lý cửa sổ WinProc.
Bộ định thời gian và vấn đề đồng bộ
Theo lý thuyết thông điệp thời gian do Windows cung cấp là chính xác đến mili giây nhưng thực tế không hoàn toàn như vậy. Sự chính xác còn phụ thuộc vào đồng hồ của hệ thống và các hoạt động hiện thời của chương trình. Nguyên nhân của vấn đề là thông điệp thời gian WM_TIMER có độ ưu tiên thấp, như thông điệp tô vẽ lại màn hình WM_PAINT, thông thường chúng phải chờ cho xử lý xong các thông điệp khác.
Khởi tạo bộ định thời gian
Không như thông điệp xuất phát từ bàn phím và chuột, được Windows gởi tự động vào hàng đợi thông điệp của ứng dụng. Đối với thông điệp thời gian phải được khai báo trước bằng hàm SetTimer, sau khi khai báo hàm này thì Windows sẽ gởi thông điệp WM_TIMER điều đặn vào hàng đợi của ứng dụng.
Hàm SetTimer được khai báo như sau :
UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc );
Trong đó ý nghĩa các tham số được mô tả:
- hWnd : Định danh của cửa sổ khai báo dùng bộ định thời gian.
- nIDEvent : Định danh của bộ định thời gian.
- nElapse : Là khoảng thời gian nghỉ giữa hai lần gởi thông điệp
- lpTimerFunc : Hàm sẽ xử lý khi thông điệp WM_TIMER phát sinh, nếu chúng ta khai báo là NULL thì Windows sẽ gởi thông điệp WM_TIMER vào hàng đợi thông điệp của cửa sổ tương ứng.
Khi không còn dùng bộ định thời gian nữa hay kết thúc ứng dụng ta gọi hàm KillTimer, hàm này được khai báo :
BOOL KillTimer( HWND hWnd, UINT_PTR uIDEvent );
- hWnd : Định danh của cửa sổ dùng bộ định thời gian
- uIDEvent : Định danh của bộ định thời gian.
Ví dụ về bộ định thời gian
Dùng thông điệp WM_TIMER
Đoạn chương trình minh họa việc sử dụng bộ định thời gian trong chương trình. Cứ mỗi 0.5 giây thì chương trình phát sinh tự động ngẫu nhiên một vòng tròn trên màn hình.
#include <time.h>
#include "stdio.h"
#define MAX_POINT 10000
#define IDT_TIMER1 1
LRESULTCALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
static int NumCir = 0;
static POINT point [ MAX_POINT ];
int r = 5, i;
HPEN pen, oldPen;
RECT rc;
TCHAR str [255];
/* Xử lý thông điệp*/
switch ( message )
{
case WM_CREATE:
/* Khai báo dùng bộ định thời gian trong ứng dụng*/
SetTimer(hWnd, IDT_TIMER1, 500, ( TIMERPROC )NULL);
/* Khởi động hàm ngẫu nhiên*/
srand ( (unsigned) time( NULL ) );
break;
case WM_PAINT:
hdc = BeginPaint ( hWnd, &ps );
pen = CreatePen ( PS_SOLID, 2, RGB (255,0,0) );
oldPen = (HPEN) SelectObject ( hdc, pen );
/* Vẽ các vòng tròn với tâm là các điểm lưu trong point và bán kính là r */
for( i=0; i < NumCir; i++ )
Arc ( hdc, point[i].x-r, point[i].y-r, point[i].x+r, point[i].y+r, point[i].x+r, point[i].y,point[i].x+r,point[i].y);
/* Lấy lại bút vẽ trước đó và hũy bút vẽ vừa tạo*/
SelectObject ( hdc, oldPen );
DeleteObject ( pen );
EndPaint ( hWnd, &ps );
break;
case WM_TIMER:
/* Lấy thông tin của vùng làm việc*/
GetClientRect ( hWnd, &rc );
/*Phát sinh ngẫu nhiên một vòng tròn*/
point [NumCir].x = rand( ) % (rc.right - rc.left);
point [NumCir].y = rand( ) % (rc.bottom - rc.top);
NumCir++;
/*Hiển thị số vòng tròn đã sinh ra trên thanh tiêu đề*/
sprintf ( str,"So vong tron : %d", NumCir);
SetWindowText ( hWnd, str );
/* Làm bất hợp lệ vùng làm việc & yêu cầu vẽ lại */
InvalidateRect ( hWnd, &rc, FALSE);
break;
case WM_DESTROY:
/* Hủy bỏ sử dụng bộ định thời gian*/
KillTimer ( hWnd, IDT_TIMER1 );
PostQuitMessage ( 0 );
break;
default:
return DefWindowProc ( hWnd, message, wParam, lParam );
}
return 0;
}
Dùng hàm xử lý
Đoạn chương trình sau cũng khai báo sử dụng một bộ định thời gian, nhưng khai báo trực tiếp, tức là khi hết thời gian chờ thay vì truyền thông điệp WM_TIMER thì Windows gọi hàm TimerProc thực hiện.
Chương trình khi thực thi sẽ xuất ra một dạng đồng hồ điện tử theo dạng : giờ : phút :giây.
#include <time.h>
#include "stdio.h"
#define IDT_TIMER1 1
LRESULTCALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
/* Khai báo biến lưu các giá trị không gian*/
struct tm *newtime;
time_t CurTime;
TCHAR str [255];
RECT rc;
/* Biến LOGFONT để tạo font mới*/
LOGFONT lf;
HFONT oldFont, font;
COLORREF color = RGB (255, 0, 0), oldColor;
switch ( message )
{
case WM_CREATE:
/* khởi tạo bộ định thời gian, và khai báo hàm xử lý Timer*/
SetTimer ( hWnd, IDT_TIMER1, 1000, ( TIMERPROC ) TimerProc );
break;
case WM_PAINT:
hdc = BeginPaint ( hWnd, &ps );
/* Lấy giờ đồng hồ hệ thống*/
time( &CurTime );
newtime = localtime ( &CurTime );
GetClientRect ( hWnd, &rc );
/* Tạo chuỗi xuất ra màn hình*/
sprintf(str,"Gio hien tai : %d gio: %d phut: %d giay", newtime->tm_hour,newtime->tm_min, newtime->tm_sec);
/* Thiết lập màu kí tự xuất*/
oldColor = SetTextColor ( hdc, color );
/* Tạo font riêng để dùng*/
memset ( &lf, 0, sizeof ( LOGFONT ) );
lf.lfHeight = 50;
strcpy ( lf.lfFaceName, "Tahoma" );
font = CreateFontIndirect ( &lf );
oldFont = ( HFONT ) SelectObject ( hdc,font );
/* Xuất ra màn hình*/
DrawText ( hdc, str, strlen(str), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
/* Lấy lại các giá trị mặc định*/
SetTextColor ( hdc,oldColor );
SelectObject ( hdc,oldFont );
DeleteObject ( font );
EndPaint ( hWnd, &ps );
break;
case WM_DESTROY:
PostQuitMessage ( 0 );
break;
default:
returnDefWindowProc ( hWnd, message, wParam, lParam );
}
return 0;
}
/* Hàm xử lý của Timer*/
VOID CALLBACK TimerProc( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
/* Hàm này đơn giản yêu cầu tô lại vùng làm việc*/
RECT rc;
GetClientRect ( hwnd, &rc );
InvalidateRect ( hwnd, &rc, TRUE );
}