链表是驱动开发中经常遇到的一个数据结构,主要是双向循环链表;要使用链表,需要用到一个LIST_ENTRY的结构,其定义如下:
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink; // 指向下一个节点(后继)
struct _LIST_ENTRY *Blink; // 指向前一个节点(前驱)
} LIST_ENTRY, *PLIST_ENTRY;
在实际的编程中,我们需要自己定义链表的节点,并把节点的第一个成员设置为LIST_ENTRY类型的变量(不一定放在第一位,但通常是这样);不过经测试,不放在第一位会导致蓝屏!!
此外,我们还需要一个LIST_ENTRY类型的链表头;其他的就靠下面的函数或者宏来操作了:
InitializeListHead,初始化链表头
IsListEmpty,判断链表是否为空
InsertHeadList,从链表头部插入节点
InsertTailList,从链表尾部插入节点
RemoveHeadList,从链表头部删除节点
RemoveTailList,从链表尾部删除节点
CONTAINING_RECORD,从RemoveHeadList或者RemoveTailList返回的数据获取一个指向删除节点的指针
下面是在头文件中的链表的实现:
//给节点定义一个结构类型,这里定义为结构体类型 typedef struct _strNode{ LIST_ENTRY Entry; //Windows自己提供的 LIST_ENTRY结构双向循环链表 int x; //调用LIST_ENTRY 结构时,一定要把它写在第一位,数据域写在它的下面,不然会导致蓝屏!! int y; }Node, *pNode; //建立链表 pNode SetNode(void){ pNode Head, A; // 定义两个结构体的指针类型变量; Head =(pNode)ExAllocatePool(PagedPool,sizeof(Node)) ; //实例化一个Head 的结构体对象并在分页内存中分配空间 //LIST_ENTRY的结构表头必须要用InitializeListHead初始化 InitializeListHead(&Head->Entry); Head->x = 0; Head->y = 0; //给头节点的两个数据域赋值 //下面开始向链表中插入5个元素(节点)并给对应的节点的数据域赋值 for(int i = 1; i <= 5; i++){ A = (pNode)ExAllocatePool(PagedPool, sizeof(Node)); //实例化节点 A ,在分页内存中分配空间 //给数据域赋值 A->x = i; A->y = i + 1; //由于LIST_ENTRY 结构已是双向循环链表形式,所以这里就不用自己进行循环指向设定了 InsertHeadList(&Head->Entry, &A->Entry); //参数一:被插入的节点,参数二:要插入的节点 } return Head; }
//打印出双向循环链表的数据和节点地址 void GetPrint(pNode head){ pNode temp; temp = head; if (temp != NULL){ do{ DbgPrint("节点 %d = %x,Next = %x, prior = %x, x = %d, y = %d \n",
temp->x, temp, temp->Entry.Flink, temp->Entry.Blink, temp->x, temp->y); temp = (pNode)temp->Entry.Blink; //强制将 "_LIST_ENTRY *" 类型下的成员转换为 "pNode " 指针类型 } while (!(temp == head)); } return; }
下面只需要在驱动入口进行调用就可以打印出来,最好事在驱动加载语句的后边调用,先等驱动加载成功!
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistrPath) { CreateMyDevice(pDriverObject); //调用自定义的 CreateMyDevice 来创建设备对象 //注册派遣函数,与IRP 请求包的数据类型一一对应 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Send_Irp; // 请求发送的I/O管理器和其他操作系统组件、其他内核模式驱动程序,DeviceIoControl函数会产生此IRP pDriverObject->MajorFunction[IRP_MJ_CREATE] = Send_Irp; // 创建设备,CreateFile会产生此IRP,返回一个句柄给用户应用程序 pDriverObject->MajorFunction[IRP_MJ_READ] = Send_Irp; // 读取设备内容,ReadFile会产生此IRP pDriverObject->MajorFunction[IRP_MJ_WRITE] = Send_Irp; // 改写设备内容,WriteFile时会产生此IRP pDriverObject->MajorFunction[IRP_MJ_CLOSE] = Send_Irp; // 关闭保留的通往目标设备对象的句柄,CloseHandle会产生此IRP pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = Send_Irp; // 清除用户模式应用程序的目标设备对象的句柄,CloseHandle会产生此IRP /*MemoryOpe();*/ GetPrint(SetNode()); // 调用双向循环链表函数 pDriverObject->DriverUnload = Unload; //调用卸载函数 KdPrint(("AppDriver 驱动加载成功!")); return STATUS_SUCCESS; }
实例化和初始化的区别是什么?
实例化:
一般是由类创建对象。如有 class A ,而 A a;//这就是实例化,创造了一个名为 a 的 A对象。
初始化:
有很多情况,如函数的初始化,类里成员的初始化等,即给一些变量赋予初始值,那些变量已经存在,只是赋值,
不像实例化那样构造一个实例的时候需要在内存中开辟空间。