定时器操作是应用编程中非常常见的操作,同样的在Windows内核驱动编程中也有对应的API。在Windows应用编程中使用的是SetTimer进行定时器编程,在Windows驱动开发中也有对应的API。主要用的下面3个API。
// 1.初始化定时器
VOID
KeInitializeTimer(
// 指向要初始化的定时器,调用者分配空间
IN PKTIMER Timer
);
// 2.初始化dpc结构体对应,可以指定定时器回调函数
VOID
KeInitializeDpc(
// 指向KDPC结构体指针,调用者必须自己分配持久的内存空间
IN PRKDPC Dpc,
// 定时器回调函数,函数原型 CustomDpc
IN PKDEFERRED_ROUTINE DeferredRoutine,
// 回调函数上下文参数,会在调用定时器回调函数时作为参数2传入
IN PVOID DeferredContext
);
// 3.启动定时器
BOOLEAN
KeSetTimer(
// 已经初始化好的定时器
IN PKTIMER Timer,
// 延时执行回调函数的相对时间 或者 绝对时间,相对时间使用负数(单位为100ns),正数会被视为绝对时间 该时间依赖操作系统时间
IN LARGE_INTEGER DueTime,
// 指向dpc结构体的指针,主要用于和定时器回调函数关联的
IN PKDPC Dpc OPTIONAL
);
// CustomDpc原型
KDEFERRED_ROUTINE CustomDpc;
VOID
CustomDpc(
__in struct _KDPC *Dpc,
__in_opt PVOID DeferredContext,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2
)
{...}
按照参数要求 可以写个简单版的定时器,需要分配空间的 都用全局变量 提前分配好。代码中有详细注释。
MyTimer.c
#include
#include
#pragma ("Ntstrsafe.lib",lib)
VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("02-DriverUnload enter\r\n");
}
PWCHAR MyCurTimeStr()
{
LARGE_INTEGER snow,now;
TIME_FIELDS now_fields;
static WCHAR time_str[32] = { 0 };
// 获得标准时间
KeQuerySystemTime(&snow);
// 转换为当地时间
ExSystemTimeToLocalTime(&snow,&now);
// 转换为人类可以理解的时间要素
RtlTimeToTimeFields(&now,&now_fields);
// 打印到字符串中
RtlStringCchPrintfW(
time_str,
32*2,
L"%4d-%d-%2d %2d:%2d:%d",
now_fields.Year,now_fields.Month,now_fields.Day,
now_fields.Hour,now_fields.Minute,now_fields.Second);
return time_str;
}
// 这些都可以封装在一个构体中
KTIMER my_timer;
KDPC my_dpc;
unsigned int timer_count = 5; // 定时器回调函数执行次数
unsigned int timer_interval = 5000 ; // 定时器执行间隔时间ms
VOID MyCustomDpc(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PKTIMER pTimer = (PKTIMER)DeferredContext;
LARGE_INTEGER time;
static int i = 1;
time.QuadPart = -10000;
time.QuadPart *= timer_interval;
if( --timer_count > 0)
{
// 继续启动定时器
KeSetTimer(pTimer,time,&my_dpc);
}
DbgPrint("%ws MyCustomDpc enter %d \r\n",MyCurTimeStr(),i++);
}
// DriverEntry,入口函数。
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
// 设置断点
#if DBG
// _asm int 3
#endif
// 注意所有的变量要声明在函数的最开头
LARGE_INTEGER time;
DbgPrint("02-DriverEntry enter\r\n");
// 1.初始化定时器
KeInitializeTimer(&my_timer);
// 2.初始化Dpc,也就是初始化 延时执行的回调函数结构
// 将my_timer作为定时回调MyCustomDpc的context,方便执行完后继续下一次定时。
// 所以这可以继续封装,可以将需要的封装成一个结构体往里面传,然后在回调函数中可以取到
KeInitializeDpc(&my_dpc,MyCustomDpc,&my_timer);
// 延时单位为100ns,1ms = 1000us = 10000 00ns
// 此时延时1ms,且必须为负数,负数才认为是相对时间否则认为是绝对时间
time.QuadPart = -10000;
time.QuadPart *= timer_interval;
// 3.启动定时器
KeSetTimer(&my_timer,time,&my_dpc);
driver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
自己简单封装一下,代码中有详细注释。MyTimer.c
#include
#include
#pragma ("Ntstrsafe.lib",lib)
VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("03—DriverUnload enter\r\n");
}
PWCHAR MyCurTimeStr()
{
LARGE_INTEGER snow,now;
TIME_FIELDS now_fields;
static WCHAR time_str[32] = { 0 };
// 获得标准时间
KeQuerySystemTime(&snow);
// 转换为当地时间
ExSystemTimeToLocalTime(&snow,&now);
// 转换为人类可以理解的时间要素
RtlTimeToTimeFields(&now,&now_fields);
// 打印到字符串中
RtlStringCchPrintfW(
time_str,
32*2,
L"%4d-%d-%2d %2d:%2d:%d",
now_fields.Year,now_fields.Month,now_fields.Day,
now_fields.Hour,now_fields.Minute,now_fields.Second);
return time_str;
}
// 定义自己封装的定时器timer
typedef struct MyTimer
{
KTIMER m_timer; // timer结构
KDPC m_dpc; // dpc结构
PKDEFERRED_ROUTINE m_func; // 定时触发的回调函数
unsigned int m_count; // 执行次数
unsigned int m_interval; // 间隔时间,单位ms
}MyTimer,*PMyTimer;
// 1.定时器相关内容初始化
VOID MyTimerInit(
PMyTimer timer, // 初始化的定时结构体
PKDEFERRED_ROUTINE func,// 定时器回调函数
unsigned int count, // 定时器执行次数
unsigned int interval // 执行间隔,单位ms
)
{
timer->m_func = func;
timer->m_count = count;
timer->m_interval = interval;
KeInitializeTimer(&timer->m_timer);
KeInitializeDpc(&timer->m_dpc,timer->m_func,timer);
}
// 2.启动定时器
VOID MyTimerSet(PMyTimer timer)
{
LARGE_INTEGER time;
time.QuadPart = -10000;
time.QuadPart *= timer->m_interval;
KeSetTimer(&timer->m_timer,time,&timer->m_dpc);
}
// 定时器回调函数
VOID MyCustomDpc(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PMyTimer pTimer = (PMyTimer)DeferredContext;
static int i = 1;
if( --pTimer->m_count > 0)
{
// 继续启动定时器
MyTimerSet(pTimer);
}
DbgPrint("%ws MyCustomDpc enter %d \r\n",MyCurTimeStr(),i++);
}
MyTimer g_timer;
// DriverEntry,入口函数。
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
// 设置断点
#if DBG
// _asm int 3
#endif
// 1.定时器相关初始化
MyTimerInit(&g_timer,MyCustomDpc,5,5000);
// 2.启动定时器
MyTimerSet(&g_timer);
DbgPrint("03-DriverEntry enter\r\n");
driver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}