DDK中定义了LIST_ENTRY双向链表结构,这样把数据和链表分开的定义方法,降低了耦合度。
typedef struct LIST ENTRY {
PLIST ENTRY Flink;
PLIST_ ENTRY Blink;
) LIST ENTRY *PLIST_ ENTRY;
链表的使用
在自己定义的结构体中包含已经定义好的LIST_ENTRY链表.
typedef struct _MYDATASTRUCT
{
// List Entry需要作为MYDATASTRUCT结构体的一部分
LIST_ENTRY ListEntry;
//下面是自定义的数据
ULONG number;
}MYDATASTRUCT,*PMYDATASTRUCT;
链表的初始化
InitializeListHead(PLIST_ ENTRY pListHead)
参数为链表头的地址,该函数的作用就是把Flink和Blink都设置为链表头的地址。
判断链表是否为空
IsListEntry(PLIST_ ENTRY pListHead)
该函数的原理就是判断链表的Flink和Blink的值一样,如果不一样 说明链表不为空。
链表的插入
void InsertHeadtist (PLIST_ ENTRY pListHead,PLIST_ ENTRY Entry);
参数1:双向链表头部的指针
参数2:要插入数据的链表节点指针
尾部插入
void InsertTailtist (PLIST_ ENTRY pListHead,PLIST_ ENTRY Entry);
参数1:双向链表头部的指针
参数2:要插入数据的链表节点指针
链表的删除
可以使用MiProcessLoadEntry来进行链表的删除(第三个参数为FALSE),更加的安全.
RemoveHeadList(&pListHead);
参数:链表头的指针
该函数返回值为用户自定义数据的指针,在LIST_ENTRY是自定义结构第一个元素的情况下。
如果不是,那么就需要地址减去到结构头的大小。
DDK定义CONTAINING_RECORD宏,实现了无论LIST_ENTRY是否是自定义结构的第一个元素都能返回自定义数据开头的指针。
参数1:自定义数据的LIST_ENTRY地址
参数2:结构体的名字
参数3:结构体中的LIST_ENTRY的名字
#include<ntddk.h>
typedef struct _MYDATASTRUCT
{
//下面是自定义的数据
ULONG x;
ULONG y;
// 这里把链表定义在结构体非头部位置,事实上windows内核里面的很多结构都是这样定义的
LIST_ENTRY ListEntry;
}MYDATASTRUCT, *PMYDATASTRUCT;
void DriverUnload(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("unload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pReg)
{
//定义链表头
LIST_ENTRY ListHead;
ULONG i = 0;
//自定义的结构指针,用来指向分配后的内存
PMYDATASTRUCT pMyData;
pDriverObject->DriverUnload = DriverUnload;
//初始化链表
InitializeListHead(&ListHead);
for (i; i<10; i++)
{
pMyData = (PMYDATASTRUCT)ExAllocatePool(PagedPool, sizeof(MYDATASTRUCT));
if (!pMyData)
{
DbgPrint("申请内存失败\n");
return STATUS_MEMORY_NOT_ALLOCATED;
}
pMyData->x = i;
pMyData->y = i;
//使用头部插入方式
InsertHeadList(&ListHead, &pMyData->ListEntry);
}
while (!IsListEmpty(&ListHead))
{
//如果链表头有数据,就移除链表头并获得头部的数据指针,使用了CONTAINING_RECORD宏。
PMYDATASTRUCT pList = (PMYDATASTRUCT)CONTAINING_RECORD((PMYDATASTRUCT)RemoveHeadList(&ListHead), MYDATASTRUCT, ListEntry);
DbgPrint("list in struct=%x\n",pList->y);
ExFreePool(pList);
}
return STATUS_SUCCESS;
}
通过结构体中的链表地址获得结构体开始地址
一般来说直接减去结构体中链表的偏移即可,DDK提供了一个宏CONTAINING_RECORD来实现这个操作。
该宏一共有3个参数:
参数1:结构体中这个链表的地址
参数2:结构体名字
参数3:结构体中包含的链表字段
例如:
//结构体
typedef struct _IRP_ENTRY
{
PIRP pIRP;
LIST_ENTRY ListEntry;
}IRP_ENTRY, *PIRP_ENTRY;
CONTAINING_RECORD( pList,链表地址
IRP_ENTRY, //结构体名
ListEntry); //链表字段名