很少有专题讲内核中的C++编程,中文资料恐怕更是罕见。由于C++的普及性、与C的亲密关系,以及大部分情况下程序员都使用C++编译器编译C程序的事实,当初学者听说内核中“不容易”(笔者也听说过“无法”二字)用C++进行编程时,会大吃一惊。不管是说者无意,还是听者有心,Windows内核的现状,决定了C语言是内核编程的首选。其实内核驱动中也能使用C++,也能使用类,但和用户程序中的用法有一些区别,一些特殊的地方需要特别注意。从笔者的经验来看,WDK给出的AVStream小端口驱动示例工程,就都是C++代码,这是由于AVStream的模块性非常强,在实现较大功能模块时,非得用类封装,否则难以表述清楚。本章专门讲述如何在内核中编写C++驱动程序。笔者先写一个简单的例子,显示类的一些基本特性,并由此交代出几项关键点;然后改造《WDFUSB设备驱动开发》一章中的WDFCY001驱动的例子,将它全部改造成一个驱动类,并最终实现C++的最大优点:多态。6.1.1一个简单的例子首先我们尝试把用户程序中最简单的类拷贝到内核中,编译链接,看看行不行。下面就是笔者定义的整数类,它封装一个整数,对象能够被当成整数使用。1.classclsInt{2.Public:3.clsInt(){m_nValue=0;}4.clsInt(intnValue){m_nValue=nValue;}5.voidprint(){KdPrint((“m_nValue:%d”,m_nValue));}6.operatorint(){returnm_nValue;}7.8.private:9.intm_nValue;10.};上例是一个非常简单的类定义,我们将在DriverEntry函数中使用它,分别定义一个局部变量和动态创建一个对象。我们通过Debug信息来观察对象行踪,希望能够得到正确的输出。入口函数中的定义如下:1.extern"C"NTSTATUSDriverEntry(2.INPDRIVER_OBJECTDriverObject,3.INPUNICODE_STRINGRegistryPath4.)5.{6.//创建两个对象,一个是局部变量,一个是动态创建的7.clsIntobj1(1);8.clsInt*obj2=new(NonPagedPool,'abcd')clsInt(2);9.10.//打印Log信息11.obj1.print();12.obj2->print();13.deleteobj2;14.15.//让模块加载失败16.returnSTATUS_UNSUCCESSFUL;17.}上面代码中先后创建了两个clsInt对象,一个是在栈中创建的,初始变量为1;一个是动态创建的,初始变量为2。后者由于是动态创建的,必须手动调用delete函数释放内存,所以其析构函数比前者先调用。我们必须从Log信息中得到类似的脉络,以证明其正确性。代码请参看simClass工程。图6-1是Log信息的截图,我们如愿以偿地得到了想要的结果。图6-1对象Log信息6.1.2new/delete查看上面的代码,会发现一个不同于以往的new操作符。这是怎么回事呢?我们这一节就讲讲它。在用户程序中,创建和释放一个对象使用new/delete方法,其底层乃是调用HeapAllocate/HeapFree堆API从线程堆栈中申请空间。但问题是,内核CRT没有提供new/delete操作符,所以需要自己定义。自定义的new/delete操作符,自然也是能够从堆栈中分配内存的,内核中有RtlAllocateHeap/RtlFreeHeap堆栈服务函数。但在内核中,我们一般使用内存池来获取内存,实际上内存池和堆栈使用了同一套实现机制。使用ExAllocatePool/ExFreePool函数对从内存池申请/释放内存,下面是一个例子。1.__forceinline2.void*__cdecloperatornew(size_tsize,3.POOL_TYPEpool_type,4.ULONGpool_tag)5.{6.ASSERT((pool_type(size),15.pool_tag);16.}上面的函数定义有几个细节的地方应当注意一下。首先注意new操作符重载,它的第一个参数一定是size_t,用来表示将分配缓冲区的长度;其次注意分页内存和非分页内存的区别,即pool_type所表示者,在DISPATCH_LEVEL及以上的级别是不能分配分页内存的。下面是使用new进行内存申请的一个例子。1.//定义一个32位的TAG值2.#defineTAG'abcd'3.//外部已经定义了一个clsName类4.externclassclsName;5.6.//为clsName申请对象空间7.clsName*objName=NULL;8.objName=new(NonPagedPool,T...