2.3 DriverEntry和Unload例程
默认情况下,每个驱动程序都有一个叫作DriverEntry
的入口点。用用户模式应用中经典的main函数来打比方,可以把DriverEntry
当作驱动程序的“main”函数。这个函数会被系统线程在IRQL PASSIVE_LEVEL(0)上调用(IRQL会在第8章详细讨论)。
DriverEntry
有预先定义好的原型,如下所示:
_In_
标注是源代码标注语言(Source Annotation Language,SAL)的一部分。这些标注对编译器来说是透明的,但是它们提供了有用的元数据,供人们阅读和静态分析工具使用。我们会尽可能地使用它们来提高代码清晰度。
最小的DriverEntry
例程只返回一个成功状态,像这样:
这段代码现在还无法通过编译。首先,代码需要包含一个头文件,里面有DriverEntry
用到的类型的定义。下面是一个可能的头文件:
现在代码离能成功编译更近一步了,不过依然还会失败。一个原因是编译器被默认设置成将警告作为错误对待,而DriverEntry
函数并没有使用它的两个参数。不建议去掉这个将警告当作错误对待的设置,因为有些警告可能是隐藏的错误。为了消除这些警告,可以去掉参数的名称(或者注释掉),这样做对C++来说完全可行。还有一个传统的方法,就是使用UNREFERENCED_PARAMETER
宏:
观察这个宏的内部实现,它其实是通过给参数重新赋一次原有的值来达到引用的效果,这样就能让编译器停止工作,让变量变成已经被“引用过”的。
此时构建这个项目就能通过编译了,但是会引起一个链接错误。DriverEntry
函数必须具有C语言的链接方式,但是C++编译默认的方式不是C的。下面才是能够成功构建且仅包含一个DriverEntry
函数的驱动程序的最终版本:
有时候驱动程序需要卸载。在那时,DriverEntry
里做过的所有事都必须被恢复原状。无法恢复的部分会产生泄漏,在下一次重启之前,内核无法清除这些泄漏。驱动程序可以拥有一个Unload例程,它会在驱动程序从内存中卸载时自动得到调用。必须使用驱动程序对象的DriverUnload
成员来设置指向这个例程的指针。
Unload例程接受驱动程序对象(与传给DriverEntry
的是同一个)并返回void
。由于我们的sample驱动程序在DriverEntry
里并没有分配任何资源,所以在Unload例程里也就没什么要做的,于是我们就让它保持为一个空函数:
下面是到目前为止完整的驱动程序代码: