• Không có kết quả nào được tìm thấy

Tính không đồng bộ của bộ định thời

Trong tài liệu LẬP TRÌNH WINDOWS (Trang 90-102)

Thông điệp WM_TIMER được đưa vào hàng đợi thông điệp cùng với các thông điệp khác theo thứ tự thời điểm phát sinh thông điệp. Vì vậy nếu ta qui định khoảng cách giữa hai thông điệp là 1000 ms thì không bảo đảm sẽ nhận được thông điệp sau mỗi 1000 ms, hay ngay cả 989 ms.

Nếu ứng dụng còn bận xử lý các thông điệp khác trong một khoảng thời gian dài hơn 1 giây, nó sẽ không nhận được thông điệp WM_TIMER trong khoảng thời gian đó. Cần lưu ý WM_TIMER là thông điệp có độ ưu tiên thấp.

Như là một hệ quả, một ứng dụng để hiển thị thời gian hiện tại như chương trình Clock của

Tham số nIDEvent có ý nghĩa khi ta cần tạo nhiều bộ định thời trong cùng một ứng dụng.

Tham số lpTimerFunc là con trỏ đến thủ tục định thời để xử lý thông điệp. Nếu tham số này bằng NULL, thông điệp WM_TIMER sẽ được gởi đến cho thủ tục cửa sổ.

Ta có thể sử dụng bộ định thời bằng cách gọi hàm SetTimer với thủ tục định thời có giá trị NULL và xử lý thông điệp WM_TIMER trong thủ tục cửa sổ.

Ví dụ 1: Chương trình bounce 1

Chương trình bounce sau đây vẽ một hình tròn nhỏ di chuyển tự động bên trong vùng client.

Ta dùng hàm SetTimer và xử lý thông điệp WM_TIMER. Hình tròn nhỏ tự động thay đổi màu sau một khoảng thời gian qui định trước (1 giây). Tham số TimerProc với giá trị NULL sẽ làm phát sinh thông điệp WM_TIMER. Cách gọi như sau:

SetTimer(hWnd, ID_MOVE_TIMER, 10, NULL);

SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, NULL);

Chương trình gồm các tập tin chương trình nguồn:

Stdafx.h stdafx.cpp Bounce.cpp

Và một số tập tin tài nguyên.

Hình 6-2 Cửa sổ chương trình

Sau đây là trích đoạn chương trình nguồn tập tin bounce.cpp

// bounce.cpp : Defines the entry point for the application.

//

//

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //

// PURPOSE: Processes messages for the main window.

//

// WM_COMMAND - process the application menu // WM_PAINT - Paint the main window

// WM_DESTROY - post a quit message and return //

//

enum {ID_MOVE_TIMER = 1, ID_CHANGECOLOR_TIMER = 2};

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

SetTimer(hWnd, ID_MOVE_TIMER, 10, NULL);

SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, NULL);

break;

case WM_COMMAND:

wmId = LOWORD(wParam);

wmEvent = HIWORD(wParam);

switch (wmId) {

case IDM_ABOUT:

DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);

break;

case IDM_EXIT:

DestroyWindow(hWnd);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

} break;

case WM_TIMER:

switch(wParam) {

case ID_MOVE_TIMER:

RECT rClient, r;

GetClientRect(hWnd, &rClient);

r.left = x - rBall - abs(dx);

r.right = x + rBall + abs(dx);

r.top = y - rBall - abs(dy);

r.bottom = y + rBall + abs(dy);

x += dx;

y += dy;

if (x + rBall > rClient.right && dx > 0) dx = -dx;

if (x - rBall < 0 && dx < 0) dx = -dx;

if (y + rBall > rClient.bottom && dy > 0) dy = -dy;

if (y - rBall < 0 && dy < 0) dy = -dy;

InvalidateRect(hWnd, &r, TRUE);

break;

case ID_CHANGECOLOR_TIMER:

++nCrIdx %= nColors;

break;

}

break;

KillTimer(hWnd, ID_CHANGECOLOR_TIMER);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

Ví dụ 2: Chương trình bounce 2

Chương trình hoạt động tương tự như chương trình của ví dụ trên nhưng ta qui định hàm xử lý timer như sau:

SetTimer(hWnd, ID_MOVE_TIMER, 10, TimerProc);

SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, TimerProc);

Sau đây là trích đoạn chương trình nguồn tập tin bounce.cpp

// bounce.cpp : Defines the entry point for the application.

//

enum {ID_MOVE_TIMER = 1, ID_CHANGECOLOR_TIMER = 2};

VOID CALLBACK TimerProc (HWND hWnd, UINT message, UINT wParam, DWORD lParam) {

static int x = 40,y = 50,rBall=10,dx=5,dy=5, nCrIdx;

static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0), RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};

static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]);

switch(wParam) {

case ID_MOVE_TIMER:

RECT rClient, r;

GetClientRect(hWnd, &rClient);

r.left = x - rBall - abs(dx);

r.right = x + rBall + abs(dx);

r.top = y - rBall - abs(dy);

r.bottom = y + rBall + abs(dy);

if (y - rBall < 0 && dy < 0) dy = -dy;

SelectObject(hdc, hbr);

SelectObject(hdc, GetStockObject(BLACK_PEN));

Ellipse(hdc, x - rBall, y - rBall, x + rBall, y + rBall);

DeleteObject(hbr);

ReleaseDC(hWnd, hdc);

break;

case ID_CHANGECOLOR_TIMER:

++nCrIdx %= nColors;

break;

} } //

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //

// PURPOSE: Processes messages for the main window.

//

// WM_COMMAND - process the application menu // WM_PAINT - Paint the main window

// WM_DESTROY - post a quit message and return //

//

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

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

TCHAR szHello[MAX_LOADSTRING];

LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

switch (message) {

case WM_CREATE:

SetTimer(hWnd, ID_MOVE_TIMER, 10, TimerProc);

SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, TimerProc);

break;

case WM_COMMAND:

wmId = LOWORD(wParam);

wmEvent = HIWORD(wParam);

switch (wmId)

hdc = BeginPaint(hWnd, &ps);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

KillTimer(hWnd, ID_MOVE_TIMER);

KillTimer(hWnd, ID_CHANGECOLOR_TIMER);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

Ví dụ 3: Chương trình bounce 3

Chương trình bounce sau đây cho phép tạo nhiều hình tròn nhỏ di chuyển bên trong vùng client. Người sử dụng có thể thêm hình bằng cách bấm ‘N’ và loại bớt hình bằng cách bấm

‘R’.

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //

//

struct BALL {

int x,y,r,dx,dy;

COLORREF crColor;

};

BALL CreateNewBall() {

BALL b;

b.x = 0, b.y = 0, b.r = 10, b.dx = b.dy = 5;

return b;

};

BALL CreateNewBall(int x, int y, int r, int dx, int dy, COLORREF color) {

BALL b;

b.x = x, b.y = y, b.r = r, b.dx = dx; b.dy = dy;

b.crColor = color;

return b;

};

const int MAX_BALLS = 100;

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

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

TCHAR szHello[MAX_LOADSTRING];

static BALL aBalls[MAX_BALLS];

static int nBalls;

static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0), RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};

static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]);

LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

RECT rt;

switch (message) {

case WM_CREATE:

SendMessage(hWnd, WM_CHAR, 'N', 0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

} break;

case WM_CHAR:

switch(toupper(TCHAR(wParam))) {

case 'N':

GetClientRect(hWnd, &rt);

aBalls[nBalls] = CreateNewBall(rand()%rt.right, rand()%rt.bottom, 10, 5, 5, aColorTab[nBalls%nColors]);

SetTimer(hWnd, nBalls++, 10, NULL);

break;

case 'R':

if (nBalls > 0)

KillTimer(hWnd, --nBalls);

InvalidateRect(hWnd, NULL, TRUE);

break;

} break;

case WM_TIMER:

RECT rClient, r;

BALL *pb;

pb = &aBalls[wParam];

GetClientRect(hWnd, &rClient);

r.left = pb->x - pb->r - abs(pb->dx);

r.right = pb->x + pb->r + abs(pb->dx);

r.top = pb->y - pb->r - abs(pb->dy);

r.bottom = pb->y + pb->r + abs(pb->dy);

pb->x += pb->dx;

pb->y += pb->dy;

if (pb->x + pb->r > rClient.right && pb->dx > 0) pb->dx = -pb->dx;

if (pb->x - pb->r < 0 && pb->dx < 0) pb->dx = -pb->dx;

if (pb->y + pb->r > rClient.bottom && pb->dy > 0) pb->dy = -pb->dy;

if (pb->y - pb->r < 0 && pb->dy < 0) pb->dy = -pb->dy;

InvalidateRect(hWnd, &r, TRUE);

break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

Ví dụ 4: Chương trình QuaDat

Chương trình QuaDat sau đây vẽ mặt trời ở giữa vùng client và vẽ quả đất di chuyển vòng

quanh mặt trời.

VOID VeVongTron(HDC hdc, int tx, int ty, int bk, HBRUSH hbr) {

SelectObject(hdc, hbr);

Ellipse(hdc, tx-bk, ty-bk, tx+bk, ty+bk);

}

//

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //

// PURPOSE: Processes messages for the main window.

//

// WM_COMMAND - process the application menu // WM_PAINT - Paint the main window

// WM_DESTROY - post a quit message and return //

//

const float PI = 3.1415926F;

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

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

TCHAR szHello[MAX_LOADSTRING];

static int nBkQuiDao = 160, nBkMatTroi = 20, nBkQuaDat = 8;

static COLORREF crMatTroi = RGB(255,0,0), crQuaDat = RGB(0,255,0);

static HBRUSH hbrMatTroi, hbrQuaDat;

// static int txQuaDat, tyQuaDat;

static HPEN hpenQuiDao;

static float fGoc = -PI/4, fStep = 0.1f;

LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

switch (message) {

{

case IDM_ABOUT:

DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);

break;

case IDM_EXIT:

DestroyWindow(hWnd);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

} break;

case WM_TIMER:

hdc = GetDC(hWnd);

SetROP2(hdc, R2_NOTXORPEN);

GetClientRect(hWnd, &rt);

VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),

rt.bottom/2 + int(nBkQuiDao*sin(fGoc)), nBkQuaDat, hbrQuaDat);

fGoc -= fStep;

VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),

rt.bottom/2 + int(nBkQuiDao*sin(fGoc)), nBkQuaDat, hbrQuaDat);

ReleaseDC(hWnd, hdc);

break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// TODO: Add any drawing code here...

GetClientRect(hWnd, &rt);

DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);

VeDuongTron(hdc, rt.right/2, rt.bottom/2, nBkQuiDao, hpenQuiDao);

SelectObject(hdc, GetStockObject(BLACK_PEN));

VeVongTron(hdc, rt.right/2, rt.bottom/2, nBkMatTroi, hbrMatTroi);

SetROP2(hdc, R2_NOTXORPEN);

VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),

rt.bottom/2 + int(nBkQuiDao*sin(fGoc)), nBkQuaDat, hbrQuaDat);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

KillTimer(hWnd,0);

PostQuitMessage(0);

break;

default:

Trong tài liệu LẬP TRÌNH WINDOWS (Trang 90-102)