HelloWDM

2008-08-14 13:47
人生真是会开玩笑啊,作为自由软件的信徒,居然要研究 Windows 的驱动了。不过对比一下还是挺好的,
和 Linux 下面的驱动比起来,还是有许多相似的地方。不过这种函数缩进形式也太难看了,一个参数一行,
看了两天都没反应过来,头晕。


WDM 驱动程序是一种很新的东西,相信很多人都跟我一样,对它很感兴趣,但是又找不到学习的切入 点。究其原因,还是因为 WDM 是一种非常“死板板”的程 序,它一运行就是工作在系统的底层 RING 0 处,提供各种接口给应用程序调用。也正因为如此,它不像普通的应用程序一样,可以很快地上手——更多的时候,你是在阅读它的技术资料和各种接口信息,你 还要非常地熟悉系统底层的工作原理,否则一个不小心,就“蓝屏”了,呵呵——话说回来,写驱动程序的时候,死机是家常便饭。

因 此很多人都对 WDM 望而生畏了。回想一下,我刚开始学 WDM 的情形还历历在目——看书看了整整 3 天,但是看完之后好像跟没看也差不了多少,还是不知道怎么入门,甚至连怎么写一个“Hello World”都不知道——后来才知道其实 WDM 是没有所谓的“Hello World”程序的,唉,真是痛苦啊,这主要还是因为网络上的 WDM 资料太少造成的。为了不让大家重蹈我的覆辙并对 WDM 有个感性的认识,在此我给出一个 最简单的完整的 WDM 框架,并附有注释,姑且可以算是一个入门的“Hello World”吧。

废话少说,让我们马上开始研究,要求读者已安装 DDK 2000。

/***************************************************************
程序名称:Hello World for WDM
文件名称:HelloWDM.cpp
作者:罗聪
日期:2002-8-16
***************************************************************/

// 一定要的头文件,声明了函数模块和变量:
#include "HelloWDM.h"

/***************************************************************
函数名称:DriverEntry()
功能描述:WDM 程序入口
***************************************************************/
//extern "C" 是必须的,表示“用 C 链接”。如果你的文件名是 HelloWDM.c 的话,这句可以省略。
extern "C"
NTSTATUS DriverEntry(     IN PDRIVER_OBJECT DriverObject,
                         IN PUNICODE_STRING RegistryPath)
{// 指定“添加设备”消息由函数“HelloWDMAddDevice()”来处理:
     DriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
    // 指定“即插即用”消息由函数“HelloWDMPnp()”来处理:
     DriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;

    // 返回一个 NTSTATUS 值 STATUS_SUCCESS。几乎所有的驱动程序例程都必须返回一个 NTSTATUS 值,这些值在 NTSTATUS.H DDK 头文件中有详细的定义。
    return STATUS_SUCCESS;
}

/***************************************************************
函数名称:HelloWDMAddDevice()
功能描述:处理“添加设备”消息
***************************************************************/
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject)
{
    // 定义一个 NTSTATUS 类型的返回值:
     NTSTATUS status;
    // 定义一个功能设备对象(Functional Device Object):
     PDEVICE_OBJECT fdo;

    // 创建我们的功能设备对象,并储存到 fdo 中:
     status = IoCreateDevice(
         DriverObject,                // 驱动程序对象
        sizeof(DEVICE_EXTENSION),    // 要求的设备扩展的大小
         NULL,                        // 设备名称,这里为 NULL
         FILE_DEVICE_UNKNOWN,        // 设备的类型,在标准头文件 WDM.H 或 NTDDK.H 中列出的 FILE_DEVICE_xxx 值之一
        0,                            // 各种常量用 OR 组合在一起,指示可删除介质、只读等。
         FALSE,                        // 如果一次只有一个线程可以访问该设备,为 TRUE,否则为 FALSE
        &fdo);                        // 返回的设备对象

    //NT_SUCCESS 宏用于测试 IoCreateDevice 内核是否成功完成。不要忘记检查对内核的所有调用是否成功。NT_ERROR 宏不等同于!NT_SUCCESS,最好使用!NT_SUCCESS,因为除了错误外,它还截获警告信息。
    if(!NT_SUCCESS(status))
        return status;

    // 创建一个设备扩展对象 dx,用于存储指向 fdo 的指针:
     PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
     dx->fdo = fdo;

    // 用 IoAttachDeviceToDeviceStack 函数把 HelloWDM 设备挂接到设备栈:
     dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);

    // 设置 fdo 的 flags。有两个“位”是必须改变的,一个是必须清除 DO_DEVICE_INITIALIZING 标志,如果在 DriverEntry 例 程中调用 IoCreateDevice(),就不需要清除这个标志位。还有一个是必须设置 DO_BUFFER_IO 标志位:
     fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
     fdo->Flags &= ~DO_DEVICE_INITIALIZING;

    // 返回值:
    return STATUS_SUCCESS;
}

/***************************************************************
函数名称:HelloWDMPnp()
功能描述:处理“即插即用”消息
***************************************************************/
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
    // 创建一个设备扩展对象 dx,用于存储指向 fdo 的指针:
     PDEVICE_EXTENSION dx=(PDEVICE_EXTENSION)fdo->DeviceExtension;

    // 首先要通过函数 IoGetCurrentIrpStackLocation() 得到当前的 IRP,并由此得到 Minor Function:
     PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
     ULONG MinorFunction = IrpStack->MinorFunction;

    // 然后把这个 Minor Function 传递给下一个设备栈:
     IoSkipCurrentIrpStackLocation(Irp);
     NTSTATUS status = IoCallDriver(dx->NextStackDevice, Irp);

    // 处理“即插即用”次功能代码:
    // 当 Minor Function 等于 IRP_MN_REMOVE_DEVICE 时,说明有设备被拔出或卸下,这时要取消资源分配并删除设备:
    if(MinorFunction==IRP_MN_REMOVE_DEVICE)
    {
        // 取消设备接口:
         IoSetDeviceInterfaceState(&dx->ifSymLinkName, FALSE);
         RtlFreeUnicodeString(&dx->ifSymLinkName);

        // 调用 IoDetachDevice() 把 fdo 从设备栈中脱开:
        if (dx->NextStackDevice)
             IoDetachDevice(dx->NextStackDevice);
        // 删除 fdo:
         IoDeleteDevice(fdo);
    }

    // 返回值:
    return status;
}

/***************************************************************
程序名称:Hello World for WDM
文件名称:HelloWDM.h
作者:罗聪
日期:2002-8-16
***************************************************************/

// 头文件,只是声明一些函数和变量,比较简单就不多说了,请读者自行研究:

#ifdef __cplusplus

extern "C"
{
#endif

#include "ntddk.h"

#ifdef __cplusplus
}
#endif

typedef struct _DEVICE_EXTENSION
{
     PDEVICE_OBJECT     fdo;
     PDEVICE_OBJECT     NextStackDevice;
     UNICODE_STRING     ifSymLinkName;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject);

NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
                         IN PIRP Irp);