I wrote a code to learn about SetTimer and KillTimer. I firstly set a timer in WM_CREATE, then write KillTimer in the timer callback function so that the timer would be called only once.
I put a MessageBox in the timer callback function, so that a MessageBox would pop up to indicate that the timer is called. The MessageBox is expected to pop up only once, since the timer would be shut down by KillTimer right after the MessageBox call.
However, the program doesn't run as intended. It seems that KillTimer didn't stop the timer as instructed. Instead, the program keeps popping out MessageBoxes.
The following is the code:
#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);
}
}
You are not killing the timer until after the message box dialog is closed.
MessageBox()
blocks the calling thread, so your message loop will not be running while the message box dialog is open. To keep the dialog responsive, MessageBox()
runs its own internal message loop. Until you close the dialog, it will continue to receive and dispatch window messages for the calling thread, including timer messages.
To fix this, you need to either:
kill the timer before you display the message box.
Otherwise, use a different method of reporting your timer events to the user that doesn't involve processing window messages. Such as displaying updated text on your UI, or using OutputDebugString()
to log to your debugger or DebugView app, etc.