使用定时器回调函数出现问题

时间:2022-10-09 23:14:19
用一个窗口类创建了两个窗口,然后在消息处理程序中的 WM_CREATE 中创建了两个定时器,两个定时器间隔均 3  秒,两个 TimerProc 的功能是将各自相关的窗口颜色由红变蓝,再由蓝变红,如些反复。不同的是要求一个窗口为蓝时另一个为红。

问题:基本上总是一个窗口固定为蓝色,而另一个窗口固定为红色,没有出现“明显变换”现象(变换是有的,但都是眨眼即过,按理说均有三秒的停留时间,不明白这是为什么)

// 定时器#2(使用定时器回调函数)

#include <windows.h>

UINT_PTR     ID1,ID2; 
HWND         hwnd1,hwnd2 ;

// 定时器回调函数
VOID  CALLBACK TimerProc1 (HWND, UINT, UINT,   DWORD ) ;
VOID  CALLBACK TimerProc2 (HWND, UINT, UINT,   DWORD ) ;
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("Beeper2") ;
    
    MSG          msg ;
    WNDCLASS     wndclass ;
     
    wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
    wndclass.lpfnWndProc   = WndProc ;
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance     = hInstance ;
    wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wndclass.lpszMenuName  = NULL ;
    wndclass.lpszClassName = szAppName ;

    if (!RegisterClass (&wndclass))
    {
        MessageBox (NULL, TEXT ("程序需要 Windows NT!"), 
                    szAppName, MB_ICONERROR) ;
        return 0 ;
    }
     
hwnd1 = CreateWindow (szAppName, TEXT ("定时器演示#2-1"),
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;
     
    ShowWindow (hwnd1, iCmdShow) ;
    UpdateWindow (hwnd1) ;

hwnd2 = CreateWindow (szAppName, TEXT ("定时器演示#2-2"),
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;
     
    ShowWindow (hwnd2, iCmdShow) ;
    UpdateWindow (hwnd2) ;
     
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }
    return msg.wParam ;
}


LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 
    switch (message)
    {
    case   WM_CREATE: // 设置定时器
           ID1 = SetTimer (NULL, 0, 3000, TimerProc1) ;
   ID2 = SetTimer (NULL, 0, 3000, TimerProc2);
           return 0 ;


    case   WM_DESTROY : // 停止定时器
           KillTimer (hwnd1, ID1) ;
   KillTimer (hwnd2, ID2) ;
           PostQuitMessage (0) ;

           return 0 ;
    }

    return DefWindowProc (hwnd, message, wParam, lParam) ;
}

// 定时器回调函数
VOID CALLBACK TimerProc1 (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
    static BOOL fFlipFlop = FALSE ;
        
    HBRUSH  hBrush ;
    HDC     hdc ;
    RECT    rc ;

    MessageBeep (-1) ;
    fFlipFlop = !fFlipFlop ;   
    GetClientRect (hwnd1, &rc) ;

    hdc = GetDC (hwnd1) ;
    hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;

    FillRect (hdc, &rc, hBrush) ;
    ReleaseDC (hwnd1, hdc) ;
    DeleteObject (hBrush) ;
}

VOID CALLBACK TimerProc2 (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
    static BOOL TWO = TRUE ;
        
    HBRUSH  hBrush ;
    HDC     hdc ;
    RECT    rc ;

    MessageBeep (-1) ;
    TWO = !TWO ;   
    GetClientRect (hwnd2, &rc) ;

    hdc = GetDC (hwnd2) ;
    hBrush = CreateSolidBrush (TWO ? RGB(255,0,0) : RGB(0,0,255)) ;

    FillRect (hdc, &rc, hBrush) ;
    ReleaseDC (hwnd2, hdc) ;
    DeleteObject (hBrush) ;
}


运行截图:

使用定时器回调函数出现问题


应该是每过三秒上面的窗口和下面的窗口颜色就对换一次,可是现在上面的窗口始终是红色,下面的窗口始终是蓝色,(有变换,但都是眨一下即过,程序原意是每个状态要保持三秒,三秒一次切换,不应该这么快呀)

8 个解决方案

#1


你的程序每个窗口都创建了两个定时器!

#2


两个窗口同时用了一个注册类和同一个窗口函数,

#3


引用 1 楼 Mackz 的回复:
你的程序每个窗口都创建了两个定时器!


引用 2 楼 zqckzqck 的回复:
两个窗口同时用了一个注册类和同一个窗口函数,


多谢二位。

#4


引用 1 楼 Mackz 的回复:
你的程序每个窗口都创建了两个定时器!


请问,为什么我这么改写了函数,情况还是和原来一样?

// 定时器#2(使用定时器回调函数)

#include <windows.h>

UINT_PTR     ID; 
HWND         hwnd1,hwnd2 ;

// 定时器回调函数
VOID  CALLBACK TimerProc (HWND, UINT, UINT,   DWORD ) ;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("Beeper2") ;
    
    MSG          msg ;
    WNDCLASS     wndclass ;
     
    wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
    wndclass.lpfnWndProc   = WndProc ;
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance     = hInstance ;
    wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wndclass.lpszMenuName  = NULL ;
    wndclass.lpszClassName = szAppName ;

    if (!RegisterClass (&wndclass))
    {
        MessageBox (NULL, TEXT ("程序需要 Windows NT!"), 
                    szAppName, MB_ICONERROR) ;
        return 0 ;
    }
     
hwnd1 = CreateWindow (szAppName, TEXT ("定时器演示#2-1"),
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;
     
    ShowWindow (hwnd1, iCmdShow) ;
    UpdateWindow (hwnd1) ;

hwnd2 = CreateWindow (szAppName, TEXT ("定时器演示#2-2"),
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;
     
    ShowWindow (hwnd2, iCmdShow) ;
    UpdateWindow (hwnd2) ;
     
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }
    return msg.wParam ;
}


LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 
    switch (message)
    {
    case   WM_CREATE: // 设置定时器
           ID = SetTimer (NULL, 0, 3000, TimerProc) ;
           return 0 ;


    case   WM_DESTROY : // 停止定时器
           KillTimer (hwnd1, ID) ;
           PostQuitMessage (0) ;

           return 0 ;
    }

    return DefWindowProc (hwnd, message, wParam, lParam) ;
}

// 定时器回调函数
VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
    static BOOL fFlipFlop = FALSE ;
        
    HBRUSH  hBrush ;
    HDC     hdc ;
    RECT    rc ;

    MessageBeep (-1) ;
    fFlipFlop = !fFlipFlop ;  

    GetClientRect (hwnd1, &rc) ;
hdc = GetDC (hwnd1) ;
    hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;
    FillRect (hdc, &rc, hBrush) ;
    ReleaseDC (hwnd1, hdc) ;


GetClientRect (hwnd2, &rc);
hdc = GetDC (hwnd2) ;
    hBrush = CreateSolidBrush ( fFlipFlop ? RGB(0,0,255) : RGB(255,0,0)) ;
    FillRect (hdc, &rc, hBrush) ;
    ReleaseDC (hwnd2, hdc) ;

    
    DeleteObject (hBrush) ;
}

#5


不明白还有什么一样,如果你说过渡效果你本来就没有设计,如果说变色应该是没有问题的。但是,你这种绘制方式有问题,应该是在定时器中修改变量,刷新窗口,窗口WM_PAINT消息中根据控制变量重绘。

话说已经好几年没搞Win32了,但是你的代码还有几处问题,是不是关闭一个窗口另外一个也关了?回调函数中你创建了两次画刷却只销毁一次,存在GDI资源泄漏。

#6


我知道原因了,你只需要一个定时器就行了,现在两个窗口各自创建一个定时器,还是在互相修改。

#7


正好吃完饭没事干:
// 全局变量:
BOOL bFlip = FALSE;
int nTimerId = 1;
HWND hWnd1 = NULL;
HWND hWnd2 = NULL;
HBRUSH brush[2];

void CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
bFlip = !bFlip;
if (hWnd1){
InvalidateRect(hWnd1, NULL, TRUE);
}
if (hWnd2){
InvalidateRect(hWnd2, NULL, TRUE);
}
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 处理主窗口的消息。
//
//  WM_COMMAND - 处理应用程序菜单
//  WM_PAINT - 绘制主窗口
//  WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
if (hWnd == hWnd1){
FillRect(ps.hdc, &ps.rcPaint, brush[bFlip]);
}
else
{
FillRect(ps.hdc, &ps.rcPaint, brush[!bFlip]);
}
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
   _In_opt_ HINSTANCE hPrevInstance,
   _In_ LPTSTR    lpCmdLine,
   _In_ int       nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

TCHAR szWindowClass[] = _T("MyWindowClass"); // 主窗口类名

//  目的: 注册窗口类。
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT1));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassEx(&wcex);

hWnd1 = CreateWindow(szWindowClass, _T("窗口一"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

hWnd2 = CreateWindow(szWindowClass, _T("窗口二"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

brush[0] = CreateSolidBrush(RGB(255, 0, 0));
brush[1] = CreateSolidBrush(RGB(0, 0, 255));

if (!hWnd1 || !hWnd2 || !brush[0] || !brush[1])
{
return FALSE;
}

ShowWindow(hWnd1, nCmdShow);
UpdateWindow(hWnd1);

ShowWindow(hWnd2, nCmdShow);
UpdateWindow(hWnd2);

SetTimer(NULL, nTimerId, 3000, TimerProc);

// 主消息循环:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
hWnd1 = NULL;
hWnd2 = NULL;

DeleteObject(brush[0]);
brush[0] = NULL;
DeleteObject(brush[1]);
brush[1] = NULL;

return (int) msg.wParam;
}

#8


学习了,版主......

#1


你的程序每个窗口都创建了两个定时器!

#2


两个窗口同时用了一个注册类和同一个窗口函数,

#3


引用 1 楼 Mackz 的回复:
你的程序每个窗口都创建了两个定时器!


引用 2 楼 zqckzqck 的回复:
两个窗口同时用了一个注册类和同一个窗口函数,


多谢二位。

#4


引用 1 楼 Mackz 的回复:
你的程序每个窗口都创建了两个定时器!


请问,为什么我这么改写了函数,情况还是和原来一样?

// 定时器#2(使用定时器回调函数)

#include <windows.h>

UINT_PTR     ID; 
HWND         hwnd1,hwnd2 ;

// 定时器回调函数
VOID  CALLBACK TimerProc (HWND, UINT, UINT,   DWORD ) ;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("Beeper2") ;
    
    MSG          msg ;
    WNDCLASS     wndclass ;
     
    wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
    wndclass.lpfnWndProc   = WndProc ;
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance     = hInstance ;
    wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wndclass.lpszMenuName  = NULL ;
    wndclass.lpszClassName = szAppName ;

    if (!RegisterClass (&wndclass))
    {
        MessageBox (NULL, TEXT ("程序需要 Windows NT!"), 
                    szAppName, MB_ICONERROR) ;
        return 0 ;
    }
     
hwnd1 = CreateWindow (szAppName, TEXT ("定时器演示#2-1"),
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;
     
    ShowWindow (hwnd1, iCmdShow) ;
    UpdateWindow (hwnd1) ;

hwnd2 = CreateWindow (szAppName, TEXT ("定时器演示#2-2"),
                         WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         CW_USEDEFAULT, CW_USEDEFAULT,
                         NULL, NULL, hInstance, NULL) ;
     
    ShowWindow (hwnd2, iCmdShow) ;
    UpdateWindow (hwnd2) ;
     
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }
    return msg.wParam ;
}


LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 
    switch (message)
    {
    case   WM_CREATE: // 设置定时器
           ID = SetTimer (NULL, 0, 3000, TimerProc) ;
           return 0 ;


    case   WM_DESTROY : // 停止定时器
           KillTimer (hwnd1, ID) ;
           PostQuitMessage (0) ;

           return 0 ;
    }

    return DefWindowProc (hwnd, message, wParam, lParam) ;
}

// 定时器回调函数
VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
    static BOOL fFlipFlop = FALSE ;
        
    HBRUSH  hBrush ;
    HDC     hdc ;
    RECT    rc ;

    MessageBeep (-1) ;
    fFlipFlop = !fFlipFlop ;  

    GetClientRect (hwnd1, &rc) ;
hdc = GetDC (hwnd1) ;
    hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ;
    FillRect (hdc, &rc, hBrush) ;
    ReleaseDC (hwnd1, hdc) ;


GetClientRect (hwnd2, &rc);
hdc = GetDC (hwnd2) ;
    hBrush = CreateSolidBrush ( fFlipFlop ? RGB(0,0,255) : RGB(255,0,0)) ;
    FillRect (hdc, &rc, hBrush) ;
    ReleaseDC (hwnd2, hdc) ;

    
    DeleteObject (hBrush) ;
}

#5


不明白还有什么一样,如果你说过渡效果你本来就没有设计,如果说变色应该是没有问题的。但是,你这种绘制方式有问题,应该是在定时器中修改变量,刷新窗口,窗口WM_PAINT消息中根据控制变量重绘。

话说已经好几年没搞Win32了,但是你的代码还有几处问题,是不是关闭一个窗口另外一个也关了?回调函数中你创建了两次画刷却只销毁一次,存在GDI资源泄漏。

#6


我知道原因了,你只需要一个定时器就行了,现在两个窗口各自创建一个定时器,还是在互相修改。

#7


正好吃完饭没事干:
// 全局变量:
BOOL bFlip = FALSE;
int nTimerId = 1;
HWND hWnd1 = NULL;
HWND hWnd2 = NULL;
HBRUSH brush[2];

void CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
bFlip = !bFlip;
if (hWnd1){
InvalidateRect(hWnd1, NULL, TRUE);
}
if (hWnd2){
InvalidateRect(hWnd2, NULL, TRUE);
}
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 处理主窗口的消息。
//
//  WM_COMMAND - 处理应用程序菜单
//  WM_PAINT - 绘制主窗口
//  WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
if (hWnd == hWnd1){
FillRect(ps.hdc, &ps.rcPaint, brush[bFlip]);
}
else
{
FillRect(ps.hdc, &ps.rcPaint, brush[!bFlip]);
}
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
   _In_opt_ HINSTANCE hPrevInstance,
   _In_ LPTSTR    lpCmdLine,
   _In_ int       nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

TCHAR szWindowClass[] = _T("MyWindowClass"); // 主窗口类名

//  目的: 注册窗口类。
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT1));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassEx(&wcex);

hWnd1 = CreateWindow(szWindowClass, _T("窗口一"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

hWnd2 = CreateWindow(szWindowClass, _T("窗口二"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

brush[0] = CreateSolidBrush(RGB(255, 0, 0));
brush[1] = CreateSolidBrush(RGB(0, 0, 255));

if (!hWnd1 || !hWnd2 || !brush[0] || !brush[1])
{
return FALSE;
}

ShowWindow(hWnd1, nCmdShow);
UpdateWindow(hWnd1);

ShowWindow(hWnd2, nCmdShow);
UpdateWindow(hWnd2);

SetTimer(NULL, nTimerId, 3000, TimerProc);

// 主消息循环:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
hWnd1 = NULL;
hWnd2 = NULL;

DeleteObject(brush[0]);
brush[0] = NULL;
DeleteObject(brush[1]);
brush[1] = NULL;

return (int) msg.wParam;
}

#8


学习了,版主......