IO定时器是DDK提供的一种定时器。使用这种定时器时,每间隔1s系统会调用一次IO定时器例程。
程序员稍加改进,就可以将定时间隔修改为多秒。
如果需要的时间间隔小于1S,那么IO定时器就没办法做到了,就需要用DPC定时器了。
IO定时器的使用之前需要对其进行初始化,初始化的时候使用内核函数IoInitializeTimer。
NTSTATUS
IoInitializeTimer(
IN PDEVICE_OBJECT DeviceObject,//关联设备对象指针
IN PIO_TIMER_ROUTINE TimerRoutine,//关联定时器例程,回调函数
IN PVOID Context//这个是传入的参数
);
其中回调函数的格式如下:
需要说明的是,IO定时器例程运行在DISPATCH_LEVEL级别,因此例程中不能使用分页内存。否则会引起系统崩溃。
另外,IO定时器是运行在任意线程的,不一定是IRP发起的线程中。
下面是演示程序。
1.初始化
2.在设备扩展中加入一个计数变量,这个计数变量负责记录间隔的秒数。
3.定义两个IOCTL码,分别是启动定时器和停止定时器的请求。
在DDK中使用 IoStartTimer 和IoStopTimer 函数用来控制定时器的启动和停止。
VOID
IoStartTimer(
IN PDEVICE_OBJECT DeviceObject
);
VOID
IoStopTimer(
IN PDEVICE_OBJECT DeviceObject
);
4.如下为内核层的定时器控制演示程序
5.编写定时器例程,下面是定时器例程的演示程序
#pragma LOCKEDCODE
VOID OnTimer(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
DeviceObject->DeviceExtension;
KdPrint(("Enter OnTimer!\n"));
//将计数器自锁减一
InterlockedDecrement(&pDevExt->lTimerCount);
//如果计数器减到0,重新编程TIMER_OUT,整个过程是互锁运算
LONG previousCount = InterlockedCompareExchange(&pDevExt->lTimerCount,TIMER_OUT,0);
//每隔三秒,计数器一个循环,输出以下log
if (previousCount==0)
{
KdPrint(("%d seconds time out!\n",TIMER_OUT));
}
//证明该线程运行在任意线程上下文的
PEPROCESS pEProcess = IoGetCurrentProcess();
PTSTR ProcessName = (PTSTR)((ULONG)pEProcess + 0x174);//即可得到用户进程
KdPrint(("The current process is %s\n",ProcessName));
}
6.编写应用层程序,用于控制定时器的启动和停止
int main()
{
HANDLE hDevice =
CreateFile("\\\\.\\HelloDDK",
GENERIC_READ | GENERIC_WRITE,
0, // share mode none
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ); // no template
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("Failed to obtain file handle to device: "
"%s with Win32 error code: %d\n",
"MyWDMDevice", GetLastError() );
return 1;
}
DWORD dwOutput;
DeviceIoControl(hDevice, IOCTL_START_TIMER, NULL, 0, NULL, 0, &dwOutput, NULL);
Sleep(10000);
DeviceIoControl(hDevice, IOCTL_STOP, NULL, 0, NULL, 0, &dwOutput, NULL);
CloseHandle(hDevice);
return 0;
}