C语言settimer函数如何正确使用?

99ANYc3cd6
预计阅读时长 20 分钟
位置: 首页 C语言 正文

核心要点

最重要的一点是:SetTimer 并不是C语言的标准库函数,它是 Windows API (应用程序编程接口) 中的一个函数,专门用于 图形用户界面 程序开发。

  • 适用环境:仅适用于 Windows 操作系统。
  • 所属库user32.dll (在C语言中通常通过 windows.h 头文件来使用)。
  • 功能:在应用程序中创建一个定时器,定时器会周期性地向应用程序发送 WM_TIMER 消息,或者在一个指定的时间间隔后执行一个回调函数。

SetTimer 函数详解

SetTimer 函数主要有两种使用方式,这取决于你传递的参数。

函数原型

UINT_PTR SetTimer(
  HWND     hWnd,       // 窗口句柄
  UINT_PTR nIDEvent,   // 定时器ID
  UINT     uElapse,    // 时间间隔,单位是毫秒
  TIMERPROC lpTimerFunc // 回调函数指针
);

参数说明

  • hWnd (HWND):

    • 一个窗口的句柄,这个窗口将接收定时器发出的 WM_TIMER 消息。
    • 如果你想在窗口的消息循环中处理定时器事件,这个参数就是你的窗口句柄。
    • 如果你想使用回调函数(第四个参数),这个参数可以是 NULL
    • 如果是 NULL 并且第四个参数也是 NULL,系统会为调用线程创建一个 "非窗口定时器" (non-window timer),线程的消息队列会收到 WM_TIMER 消息。
  • nIDEvent (UINT_PTR):

    • 一个定时器的标识符(ID)。
    • 如果这个值是唯一的,SetTimer 就会创建一个新的定时器。
    • 如果这个值已经存在一个定时器,那么该函数会重置那个已有的定时器,使其重新计时。
    • 如果传入 0,系统会自动为你生成一个唯一的ID。
  • uElapse (UINT):

    • 定时器的时间间隔,单位是毫秒
    • 1000 表示1秒,500 表示0.5秒。
  • lpTimerFunc (TIMERPROC):

    • 一个指向回调函数的指针。
    • 回调函数:这是一个由你定义,但由系统(在定时器触发时)调用的函数。
    • 如果这个参数是 NULL,定时器触发时,系统会向 hWnd 指定的窗口发送 WM_TIMER 消息。
    • 如果这个参数是一个有效的函数指针,定时器触发时,系统会直接调用这个函数,不会发送 WM_TIMER 消息

返回值

  • 如果成功,返回新创建的定时器的ID(一个 UINT_PTR 类型的值)。
  • 如果失败,返回 0

两种主要使用方式

通过窗口消息处理(lpTimerFunc = NULL

这是最传统的方式,通常用于基于对话框或窗口的应用程序。

工作流程

  1. 调用 SetTimer,将 lpTimerFunc 设为 NULL
  2. 在你的窗口过程函数(WndProc)中,添加对 WM_TIMER 消息的 case 分支。
  3. 当定时器时间到,系统会自动将 WM_TIMER 消息放入指定窗口的消息队列中。
  4. 你的 WndProc 函数会收到这个消息并执行相应的代码。

示例代码

#include <windows.h>
// 窗口过程函数的声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // 1. 注册窗口类
    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = "MyTimerWindow";
    RegisterClass(&wc);
    // 2. 创建窗口
    HWND hWnd = CreateWindow("MyTimerWindow", "SetTimer 示例", WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
                              NULL, NULL, hInstance, NULL);
    // 3. 显示窗口
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    // 4. 创建定时器 (每1000毫秒触发一次,ID为1)
    SetTimer(hWnd, 1, 1000, NULL); // 注意这里lpTimerFunc是NULL
    // 5. 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case WM_TIMER:
            // 当定时器ID为1的消息到达时执行
            if (wParam == 1) {
                static int counter = 0;
                counter++;
                char buffer[100];
                sprintf_s(buffer, sizeof(buffer), "定时器触发次数: %d", counter);
                SetWindowText(hWnd, buffer); // 在窗口标题上显示次数
            }
            break;
        case WM_DESTROY:
            // 6. 销毁定时器!非常重要!
            KillTimer(hWnd, 1);
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

通过回调函数(lpTimerFunc != NULL

这种方式更灵活,不依赖于窗口消息循环,常用于控制台程序或需要更精细定时控制的场景。

工作流程

  1. 定义一个符合 TIMERPROC 签名的回调函数。
  2. 调用 SetTimer,并将你的回调函数名作为第四个参数传入,hWnd 可以为 NULL
  3. 当定时器时间到,系统会直接调用你的回调函数。

回调函数原型

VOID CALLBACK TimerProc(
  HWND     hwnd,    // 窗口句柄,即SetTimer中的hWnd
  UINT     uMsg,    // 消息,对于定时器总是WM_TIMER
  UINT_PTR idEvent, // 定时器ID
  DWORD    dwTime   // 系统启动以来的毫秒数
);

示例代码

#include <windows.h>
#include <stdio.h>
// 1. 定义回调函数
VOID CALLBACK MyTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
    // 当定时器ID为100触发时执行
    if (idEvent == 100) {
        printf("回调函数被触发!系统运行了 %d 毫秒,\n", dwTime);
    }
}
int main() {
    printf("程序启动,创建一个2秒触发一次的回调定时器,\n");
    // 2. 创建定时器
    // hWnd设为NULL,lpTimerFunc设为我们的回调函数
    UINT_PTR timerID = SetTimer(NULL, 100, 2000, MyTimerProc);
    if (timerID == 0) {
        printf("创建定时器失败!\n");
        return 1;
    }
    printf("定时器创建成功,ID为 %u,\n", timerID);
    printf("程序将运行10秒后退出...\n");
    // 3. 保持程序运行,以便回调函数能被调用
    // 在实际应用中,这里可能是主程序的业务逻辑
    Sleep(10000); // 睡眠10秒
    // 4. 销毁定时器!非常重要!
    KillTimer(NULL, timerID);
    printf("定时器已销毁,程序退出,\n");
    return 0;
}

重要注意事项

  1. 线程关联SetTimer 创建的定时器与调用它的线程关联,回调函数会在该线程的上下文中执行。
  2. 必须销毁定时器:当你不再需要定时器时,必须调用 KillTimer 函数来销毁它,否则会造成资源泄漏KillTimer 的参数与 SetTimer 对应。
    KillTimer(hWnd, nIDEvent); // 销毁指定窗口的指定ID的定时器
    KillTimer(NULL, nIDEvent); // 销毁非窗口定时器
  3. 精度限制SetTimer 的精度受限于系统的定时器精度(通常在10-16毫秒左右),设置一个比系统精度更小的时间间隔(如1毫秒)并不能保证精确的1毫秒触发,只会尽可能接近。
  4. 非阻塞SetTimer 是异步的,调用它后,程序会立即继续执行后续代码,而不会等待定时器触发。
  5. 跨平台性:这是最大的限制。SetTimer 是Windows独有的,如果你需要编写跨平台的C语言程序,应该使用其他库,
    • POSIX 标准<unistd.h> 中的 sleep() (秒级), usleep() (微秒级),以及 <time.h> 中的 nanosleep()
    • 第三方库:如 Boost.Asio (C++),或者 libuv, SDL 等库提供的定时器功能。
特性 说明
函数归属 Windows API,非C标准库
核心功能 创建周期性或一次性定时器
两种模式 消息模式:向窗口发送 WM_TIMER 消息。
2. 回调模式:直接调用用户定义的函数。
关键步骤 SetTimer 创建 -> 业务逻辑处理 -> KillTimer 销毁
适用场景 Windows GUI程序(如MFC, Win32 SDK)
跨平台替代 sleep(), usleep(), nanosleep() (POSIX)

希望这个详细的解释能帮助你完全理解C语言(特指Windows环境下)的 SetTimer 函数!

-- 展开阅读全文 --
头像
如何过滤织梦采集中的a标签?
« 上一篇 05-01
c语言 stdcall
下一篇 » 05-01

相关文章

取消
微信二维码
支付宝二维码

目录[+]