1、Windows 2000驱动程序的设计2003-3-25 12:41:59 VCPROGRAM 谭章熹 阅读次数: 135961. 简介: - Windows 2000原名 Windows NT 5.0是继 Windows NT 4.0的新一代操作系统,它不但继承了 Windows NT 4.0的种种优点,而且在技术上又有了许多的突破,其中一项就是对驱动程序结构的变化,即引入了全新的 WDM (Win32 Driver Model)的驱动程序构架。说是新技术,其实早在 1997年 Microsoft就提出了该项技术并在 Windows 98中得到了充分的应用,换句话说,Windows 98 也
2、支持 WDM。这样 WDM就成为了一个跨平台的驱动程序模型,不仅如此 WDM驱动程序还可以在不修改源代码的情况下经过重新编译后在非 Intel平台上运行,可以不夸张的讲 WDM算得上是 21世纪的驱动程序构架。 2WDM 的工作原理: WDM是在 NT 4.0驱动程序结构上发展起来的,所以它与 NT 4.0的驱动程序极为相似,但是它却有了本质上的提高,比如它支持 USB、IEEE 1394、ACPI 等全新的硬件标准。虽然Windows 98与 Windows 2000都支持 WDM,可是并不意味着 Windows 98下的 VxD可以在Windows 2000下运行,而 NT下的 VDD却可
3、以在 Windows 98下运行。不过原先准备在两个平台上同时运行需要编写两个截然不同的驱动程序,而现在只需要编写一个 WDM驱动程序就可以了。同 NT 4.0驱动程序一样,WDM 驱动程序也是分层的,即不同层上的驱动程序有着不同的优先权,而 Windows 9x下的 VxD则没有此结构。另外,WDM 还引入了功能设备对象 FDO(functional device object)与物理设备对象 PDO(physical device object)两个新概念来描述硬件,一个 PDO代表一个真实硬件,在驱动程序看来则是一个 FDO,见图1。另外值得注意的是,一个硬件只允许有一个 PDO,但却可
4、以拥有多个 FDO,而在驱动程序中我们不是直接操作硬件而是操作相应的 PDO与 FDO。在 Ring-3与 Ring-0通讯方面,操作系统为每一个用户请求打包成一个 IRP(IO Request Packet)结构,将其发送至驱动程序并通过识别 IRP中的 PDO来识别是发送给哪一个设备的。另外,在驱动程序的加载方面 WDM既不靠驱动程序名称也不靠一个具有某种特殊意义的 ID,而是依靠一个 128位的GUID来识别驱动程序(Windows 下许多东西都是靠此进行识别的)。 3具体实现: 同许多应用程序一样,WDM 驱动程序是 PE格式的,但是它却没有 WinMain或 main这样的入口,取而
5、代之的是 DriverEntry: NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, /不同于前面的 PDO IN PUNICODE_STRING RegistryPath) DriverObject- DriverExtension- AddDevice = AddDevice; / DriverExtension 中存放着驱动程序扩展信息,包括设备所需要的硬件资源等。 DriverObject- MajorFunctionIRP_MJ_CREATE = RequestCreate; DriverObject- MajorFunctio
6、nIRP_MJ_CLOSE = RequestClose; DriverObject- MajorFunctionIRP_MJ_DEVICE_CONTROL = RequestControl; DriverObject- MajorFunctionIRP_MJ_PNP = RequestPnp; return STATUS_SUCCESS; - 在 DriverEntry驱动程序要向操作系统登记并注册一些消息处理器,而且还要指明是否对驱动程序输入输出的数据进行缓冲,另外还要我们提供一个 AddDevice例程来把驱动程序添加到驱动程序堆栈中。其中,IRP_MJ_XXXXX 为驱动程序所收到的系
7、统消息,RequestXXXXX为相应的消息处理函数。在客户端程序中,我们一般要采用DeviceIoControl通过自定义的控制码与驱动程序通信(在 VxD中大多也采用这种方式)。看看驱动程序所收到的系统消息,我们不难发现当用户调用 DeviceIoControl时操作系统就会向驱动程序发出一条 IRP_MJ_DEVICE_CONTROL消息,以触发 RequestControl消息处理函数。 NTSTATUS RequestControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) PIO_STACK_LOCATION IrpStack; UL
8、ONG ControlCode; ULONG InputLength,OutputLength; NTSTATUS status; IrpStack=IoGetCurrentIrpStackLocation(Irp); /获取当前 IRP所在的 I/O堆栈 ControlCode=IrpStack- Parameters.DeviceIoControl. IoControlCode; /取得控制码 InputLength=IrpStack- Parameters.DeviceIoControl. InputBufferLength; /取输入缓冲区大小 OutputLength=IrpStac
9、k- Parameters.DeviceIoControl. OutputBufferLength;/取输出缓冲区大小 switch(ControlCode) case HELLOWDM_IOCTL_HELLO: DbgPrint (“Hello from WDM.n“);/向调试器输出字符串 status=STATUS_SUCCESS; /置返回值 break; default: status=STATUS_INVALID_DEVICE_REQUEST; /输入的控制码不支持 return CompleteRequest(Irp, status, 0); /调用 CompleteReques
10、t通知操作系统完成 IRP操作 - 在客户端方面,先调用 Setupapi.dll中的 SetupDiGetClassDevs 并用上面提到的128位 GUID 建立 Ring-0与 Ring-3接口: -HDEVINFO info=SetupDiGetClassDevs (LPGUID) 然后使用SetupDiEnumDeviceInterfaces 对所获得的接口进行枚举以获得接口数据,接着连续两次调用 SetupDiGetDeviceInterfaceDetail 获得接口详细信息,其中包括调用 CreateFil e所需的一个型为.0000000000000004# 3d93c5c0-
11、0085-11d1-821e-0080c88327ab 的字符串,最后调用方法和 VxD的调用大体相同这里就不赘述了。不过由于使用了Setupapi.dll中的 API所以还需要使用 SetupDiDestroyDeviceInfoList 来释放所申请的资源。 - 4几点说明: - 由于 WDM是跨平台和跨操作系统的的驱动程序模型,所以在编写时一定不要使用汇编。另外,在编写时还应注意对 IRP_MJ_PNP消息的响应以及其他系统消息的传递,这里的传递是向其它在驱动程序堆栈中的驱动程序而不是向客户端程序,详细的信息请参考本文所提供的例程。最后,由于笔者写此文章时 Windows 2000尚未正式发布,一切的编写工作都是在 Windows 98上用 98DDK与 VC6.0完成的,并且用 Numega SoftIce 4.0调试通过。 您编译后有什么意见欢迎来信告知: E-mail: 清华大学电子工程系 谭章熹