我写了一段代码来学习SetTimer和KillTimer,首先在WM_CREATE中设置一个定时器,然后在定时器的回调函数中写上KillTimer,这样定时器只会被调用一次。
我在定时器回调函数中放置了一个 MessageBox,这样当定时器被调用时,MessageBox 会弹出来提示。MessageBox 预计只会弹出一次,因为定时器会在 MessageBox 调用后立即被 KillTimer 关闭。
但是,程序并没有按照预期运行。看来 KillTimer 并没有按照指示停止计时器。相反,程序不断弹出消息框。
以下是代码:
#include <stdio.h>
#include <windows.h>
VOID CALLBACK timer_proc(HWND hwnd, UINT msg, UINT timer_id, DWORD time)
{
char buf[1024];
sprintf(buf, "TIMER PROC CALLED AT %d, ID %d", time, timer_id);
MessageBoxA(hwnd, buf, "MSGBOX", MB_ICONINFORMATION);
KillTimer(hwnd, timer_id);
}
void wm_paint(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
TextOutA(hdc, 20, 20, "Hi?", 3);
EndPaint(hwnd, &ps);
}
LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_CREATE:
SetTimer(hwnd, 1, 500, timer_proc);
return 0;
case WM_PAINT: wm_paint(hwnd); return 0;
case WM_SIZE: return 0;
case WM_DESTROY: PostQuitMessage(0); return 0;
default: return DefWindowProc(hwnd, msg, wp, lp);
}
}
void init_wndclass(WNDCLASS *w, HINSTANCE hinstance, char *class_name)
{
w->style = CS_HREDRAW | CS_VREDRAW;
w->lpfnWndProc = wndproc;
w->cbClsExtra = w->cbWndExtra = 0;
w->hInstance = hinstance;
w->hIcon = LoadIcon(NULL, IDI_APPLICATION);
w->hCursor = LoadCursor(NULL, IDC_ARROW);
w->hbrBackground = GetStockObject(WHITE_BRUSH);
w->lpszMenuName = NULL;
w->lpszClassName = class_name;
}
HWND create_window(HINSTANCE hinstance)
{
WNDCLASS wndclass;
char *class_name = "main window class";
init_wndclass(&wndclass, hinstance, class_name);
RegisterClass(&wndclass);
return CreateWindowA(class_name, "TIMER TEST", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, // position
CW_USEDEFAULT, CW_USEDEFAULT, // window size
NULL, /* owner window */ NULL, /* menu */
hinstance, NULL /* window-creation data */);
}
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prev, PSTR cmdline,
int cmdshow)
{
HWND hwnd = create_window(hinstance);
ShowWindow(hwnd, cmdshow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
直到消息框对话框关闭后,您才会终止计时器。
MessageBox()
阻止调用线程,因此消息循环在消息框对话框打开时不会运行。为了保持对话框响应,MessageBox()
运行其自己的内部消息循环。直到您关闭对话框,它将继续接收和分派调用线程的窗口消息,包括计时器消息。要解决此问题,您需要执行以下操作之一:
在显示消息框之前终止计时器。
否则,请使用其他不涉及处理窗口消息的方法向用户报告计时器事件。例如,在 UI 上显示更新后的文本,或用于
OutputDebugString()
记录到调试器或DebugView应用等。