本文选自《竹林蹊径:深入浅出Windows驱动开发》一书
如果纯粹是为了尝鲜,在驱动中加入一个类,内部却只是一团硬板,那就完全多此一举了。所以本节笔者将带领大家在内核中实现类的多态。以CY001 USB设备驱动为例进行讲解,代码请参考本书工程UsbBaseClass和CY001UsbClass,前者以基类实现设备驱动,后者以子类实现设备驱动。
笔者对基类的要求是能够实现USB设备的最基本要素,使得设备能够在系统中显现,能够正常运行和移除。所以设备栈一定要成功建立,基本的Pnp/Power接口也必须要提供,但用户层接口可以暂不考虑。最终的结果是PnpAdd函数实现得非常完整,因为必须要将设备栈建立起来;EvtDevicePrepareHardware和EvtDeviceReleaseHardware函数也得以完整实现,这样设备能够正确运行和移除,但细节方面的设置如休眠等则以接口留出。
子类必须实现更完善的功能,如休眠、唤醒设置。下面的例子分别对应着基类和子类的实现。
// 配置设备驱动的电源管理功能
NTSTATUS DrvClass::InitPowerManagement()
{
return STATUS_SUCCESS;
}
这是基类的实现,空空如也,子类则要复杂许多倍。
// 配置设备驱动的电源管理功能
NTSTATUS CY001Drv::InitPowerManagement()
{
NTSTATUS status = STATUS_SUCCESS;
WDF_USB_DEVICE_INFORMATION usbInfo;
KDBG(DPFLTR_INFO_LEVEL, "[InitPowerManagement]");
// 获取设备信息
WDF_USB_DEVICE_INFORMATION_INIT(&usbInfo);
WdfUsbTargetDeviceRetrieveInformation(m_hUsbDevice, &usbInfo);
// 设置设备的休眠和远程唤醒功能
// …… 详见代码
return status;
}
怎么能够实现多态呢?当前,动态对象是在入口函数中创建的,而按照现有逻辑,入口函数是不允许修改的。笔者要提供一个机会,让库使用者可以创建动态对象。为此笔者特地有一个规定,所有库使用者必须定义一个宏,以注册自己的驱动类。
REGISTER_DRV_CLASS(DriverName)
如果不使用子类,则需要定义下面的宏而直接使用基类。
REGISTER_DRV_CLASS_NULL()
那么这两个宏到底有什么作用呢?要看宏定义了:
// 注册子类
#define REGISTER_DRV_CLASS(DriverName) \
DrvClass* GetDrvClass(){\
return (DrvClass*)new(NonPagedPool, '10YC') DriverName();\
}
// 注册基类
#define REGISTER_DRV_CLASS_NULL()\
DrvClass* GetDrvClass(){\
return new(NonPagedPool, 'ESAB') UsbBaseClass();\
}
两个宏都是为了定义一个名称为GetDrvClass的函数。前者注册驱动类的子类,并在GetDrvClass的实现中动态创建子类对象,在返回时将子类对象的指针转换为基类对象指针;后者则声明直接使用基类,并在GetDrvClass的实现中动态创建一个基类对象,并返回其指针。
调用者不必关心被创建的对象到底是来自基类还是子类,他只要使用在基类中定义的接口就可以了,而借助虚拟函数的运行时绑定策略,即可实现多态。
需要注意的是宏REGISTER_DRV_CLASS(DrvClass),其作用和REGISTER_DRV_ CLASS_NULL()是一样的,都将定义一个GetDrvClass函数。
好戏还要看DriverEntry()函数中的实现,重新修改后的函数代码如下:
NTSTATUS Driver(DRIVER_OBJECT Driver,
UNICODE_STRING Register)
{
DrvClass* pDriver = GetDrvClass();
return pDriver->DriverEntry(Driver, Register);
}
真是无与伦比的简洁,它通过GetDrvClass函数实现了多态,并立刻将驱动的实现交付到了pDriver对象的手中,而pDriver可以是基类,也可以是任意一个从基类继承的子类。
实现多态的核心是两个类注册宏,以及在入口函数中对GetDrvClass函数的调用。需要注意的是,如果用户同时定义了两个宏,那么系统就会因为发现两个完全一样的GetDrvClass函数而使编译失败;反之,如果上述两个宏一个都没有定义,那么在链接时,将因为无法找到函数定义而链接失败。
驱动工程UsbBaseClass使用驱动基类直接驱动CY001 USB设备,从SOURCE文件中可以看到,它含有的编译文件为DrvClass.cpp和GetDrvClass.cpp两个文件,前者是基类的定义文件,后者只有一行代码,即REGISTER_DRV_CLASS_NULL()。这是最简单的驱动工程。
驱动工程CY001USBClass使用驱动子类CY001DrvClass驱动CY001 USB设备,从SOURCE文件中可以看到,它依旧包含了DrvClass.cpp文件,此外还包含了若干个子类的实现文件。
所以,读者只要在DrvClass甚至CY001DrvClass类的基础上实现子类化,并注册新的子类,就能够实现功能扩展。
如图6-6所示是本节所讲的多态实现原理图。
图6-6 多态关系图
编译UsbBaseClass工程的代码,用得到的CY001.sys文件替代system32\drivers目录下的同名文件,以驱动CY001 USB设备。尝试使用本书中的UsbKitApp程序,会发现能够正确枚举到USB设备,但软件的具体功能如获取描述符等,无法正常使用。
以同样的方法测试编译CY001UsbClass工程后得到的CY001.sys文件,并运行UsbKitApp程序以测试,会发现和WDFCY001工程的测试结果完全一样。
本文选自《竹林蹊径:深入浅出Windows驱动开发》一书