友链
hellowdm.h
// 保证头文件只被编译一次
// 因为在实际项目中,一个头文件可能会被另一个头文件包含
// 比如b.h中包含了a.h
// 然后在c.c中有如下代码:
/*
#include<a.h>
#include<b.h>
*/
// 这样一来,a.h就被包含了两次
// 而#pragma once可以保证a.h只被编译一次
// 从而提高编译效率
#pragma once// 下面的这段条件编译在C++项目中是非常常见的
// 它使得我们可以在C++项目中使用C中的头文件
#ifdef __cplusplus
extern "C"
{
#endif
#include <wdm.h>
#ifdef __cplusplus
}
#endif// 分页标记、非分页标记和初始化内存块
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
// INIT标志指明函数只是在加载的时候需要载入内存
// 在驱动程序成功加载之后,函数可以从内存中卸载掉
#define INITCODE code_seg("INIT")#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")#define arraysize(p) (sizeof(p)/sizeof((p)[0]))// 定义机构体 _DEVICE_EXTENSION ,并起别名
// 设备扩展结构体
// 该结构体广泛应用于驱动程序中
// 根据不同程序的需要,用于补充定义设备的相关信息
typedef struct _DEVICE_EXTENSION {PDEVICE_OBJECT fdo; PDEVICE_OBJECT NextStackDevice;UNICODE_STRING ustrDeviceName; // 设备名称UNICODE_STRING ustrSymLinkName; // 符号链接名称
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;// 函数声明
// 这个IN关键字可能是用来说明参数是传入参数
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT pDriverObject,IN PDEVICE_OBJECT PhysicalDeviceObject);
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,IN PIRP Irp);
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,IN PIRP Irp);
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject);extern "C"
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath);
HelloWDM.cp
#include "HelloWDM.h"/*
初始化驱动程序,定位和申请硬件资源,创建内核对象
参数列表pDriverObject:从IO管理器中传进来的驱动对象pRegistryPath:驱动程序在注册表中的路径
返回值:返回初始化驱动状态
*/
// 使用extern "C"对该函数进行修饰,这样在编译的时候会编译成_DriverEntry@8
// 如果没有这个修饰符的话,编译器会按照C++的符号名进行编译,链接的时候会报错
// 指明函数是加载到INIT内存区域中
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
{// 运行在内核中的程序是没有console的,所以只能使用KdPrint宏来输出调试信息// 这个宏只在调试版本中有作用(Free)// 在发行版本中不执行任何操作(Checked)KdPrint(("Enter DriverEntry\n"));// 注册其他驱动调用函数入口// 将我们自己定义的函数的地址传送给操作系统// 操作系统会在合适的时候调用这些函数// AddDevice回调函数只出现在WDM驱动程序中,在NT式的驱动程序中是没有这个回调函数的// 该函数的作用就是创建设备对象并由PNP(即插即用)管理器调用pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;// 对PNP的IRP处理,IRP会在后面介绍,这也是NT式和WDM式驱动的重大区别之一pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;// 这里只是演示,因此全部指向了一个简单的函数HelloWDMDispatchRoutinepDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =pDriverObject->MajorFunction[IRP_MJ_CREATE] =pDriverObject->MajorFunction[IRP_MJ_READ] = pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;// 注册卸载函数pDriverObject->DriverUnload = HelloWDMUnload;KdPrint(("Leave DriverEntry\n"));return STATUS_SUCCESS;
}// 定义IRP默认处理函数
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{// 确保该例程处于APC_LEVEL之下PAGED_CODE();KdPrint(("Enter DefaultPnPHandler\n"));// 略过当前堆栈IoSkipCurrentIrpStackLocation(Irp);KdPrint(("Leave DefaultPnpHandler\n"));// 使用下层堆栈的驱动设备对象处理此IRPreturn IoCallDriver(pdx->NextStackDevice, Irp);
}#pragma PAGEDCODE
// 该函数类似于NT式驱动中的卸载例程,在WDM式的驱动中,卸载历程几乎不用做处理
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{PAGED_CODE();KdPrint(("Enter HandleRemoveDevice\n"));Irp->IoStatus.Status = STATUS_SUCCESS;// 调用默认的IRP处理函数NTSTATUS status = DefaultPnpHandler(pdx, Irp);// 删除符号链接IoDeleteSymbolicLink(&pdx->ustrSymLinkName);// 调用IoDetachDevice函数将fdo从设备堆栈中脱离if(pdx->NextStackDevice) IoDetachDevice(pdx->NextStackDevice);// 删除fdoIoDeleteDevice(pdx->fdo);KdPrint(("Leave HandleRemoveDevice\n"));return status;
}// 在WDM式的驱动程序中,创建设备的工作不再由DriverEntry承担,而是通过AddDevice回调函数
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(/* pDriverObject是由PNP管理器传递进来的驱动对象,和DriverEntry函数中的参数是一致的 */IN PDRIVER_OBJECT pDriverObject,/* PhysicalDeviceObject是由PNP管理器传递进来的底层驱动设备对象,在NT式的驱动中没有这个概念 */IN PDEVICE_OBJECT PhysicalDeviceObject)
{// 这是一个宏,如果该例程所在的中断请求级超过APC_LEVEL时,会产生一个断言,该断言会使程序终止并报错出错地址// 中断请求级将会在后面介绍PAGED_CODE();KdPrint(("ENter HelloWDMAddDevice\n"));NTSTATUS status;PDEVICE_OBJECT fdo;// 设备名称// 构造Unicode字符串用来存储此设备对象的名称UNICODE_STRING devName; RtlInitUnicodeString(&devName, L"\\Device\\MyWDMDevice");// 创建设备status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&(UNICODE_STRING)devName,FILE_DEVICE_UNKNOWN,0, FALSE,&fdo);if(!NT_SUCCESS(status))return status;// 填写设备的扩展结构体PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;pdx->fdo = fdo;// 将fdo(功能设备对象)挂接在设备堆栈上,并将其返回值记录到设备扩展结构中pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);pdx->ustrDeviceName = devName;// 创建符号链接// 设备名称只在内核态可见,用户程序是看不到的// 因此需要暴露出一个符号连接,该符号链接指向真正的设备名称UNICODE_STRING symLinkName;RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");pdx->ustrSymLinkName = symLinkName;status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName, &(UNICODE_STRING)devName);// 创建成功则返回,否则调用IoDeleteDevice删除设备if(!NT_SUCCESS(status)){// 不知道为什么这里又尝试创建了一次,没有删除设备,而是删除的符号链接// 如果失败了,不应该是根本就没有创建出来符号链接?还用的着删除?// 感觉这代码写的有问题,我给他注释掉了/*IoDeleteSymbolicLink(&pdx->ustrSymLinkName);status = IoCreateSymbolicLink(&symLinkName, &devName);if(!NT_SUCCESS(status)){return status;}*/IoDeleteDevice(fdo);return status;}// 设置设备为BUFFER_IO设备,并指明驱动初始化完成fdo->Flags = fdo->Flags | DO_POWER_PAGABLE;fdo->Flags = fdo->Flags & ~DO_DEVICE_INITIALIZING;KdPrint(("Leave HelloWDMAddDevice\n"));return STATUS_SUCCESS;
}// 定义HelloWDMPnp函数,对即插即用IRP进行处理
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,IN PIRP Irp)
{PAGED_CODE();KdPrint(("Enter HelloWDMPnp\n"));NTSTATUS status = STATUS_SUCCESS;PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;// 得到当前IRP的堆栈,设备堆栈是一个非常复杂的概念,后面会讲到PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);// 对函数表进行初始化// 将对应类别的即插即用IRP调用做不同的处理static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp) ={DefaultPnpHandler, // IRP_MN_START_DEVICEDefaultPnpHandler, // IRP_MN_QUERY_REMOVE_DEVICEHandleRemoveDevice, // IRP_MN_REMOVE_DEVICEDefaultPnpHandler, // IRP_MN_CANCEL_REMOVE_DEVICEDefaultPnpHandler, // IRP_MN_STOP_DEVICEDefaultPnpHandler, // IRP_MN_QUERY_STOP_DEVICEDefaultPnpHandler, // IRP_MN_CANCEL_STOP_DEVICEDefaultPnpHandler, // IRP_MN_QUERY_DEVICE_RELATIONSDefaultPnpHandler, // IRP_MN_QUERY_INTERFACEDefaultPnpHandler, // IRP_MN_QUERY_CAPABILITIESDefaultPnpHandler, // IRP_MN_QUERY_RESOURCESDefaultPnpHandler, // IRP_MN_QUERY_RESOURCE_REQUIREMENTSDefaultPnpHandler, // IRP_MN_QUERY_DEVICE_TEXTDefaultPnpHandler, // IRP_MN_FILTER_RESOURCE_REQUIREMENTSDefaultPnpHandler, // DefaultPnpHandler, // IRP_MN_READ_CONFIGDefaultPnpHandler, // IRP_MN_WRITE_CONFIGDefaultPnpHandler, // IRP_MN_EJECTDefaultPnpHandler, // IRP_MN_SET_LOCKDefaultPnpHandler, // IRP_MN_QUERY_IDDefaultPnpHandler, // IRP_MN_QUERY_PNP_DEVICE_STATEDefaultPnpHandler, // IRP_MN_QUERY_BUS_INFORMATIONDefaultPnpHandler, // IRP_MN_DEVICE_USAGE_NOTIFICATIONDefaultPnpHandler, // IRP_MN_SURPRISE_REMOVAL};ULONG fcn = stack->MinorFunction;if(fcn >= arraysize(fcntab)){// 对于未知的IRP类别,使用DefaultPnpHandler函数去处理status = DefaultPnpHandler(pdx, Irp);return status;}#if DBGstatic char* fcnname[] = {"IRP_MN_START_DEVICE","IRP_MN_QUERY_REMOVE_DEVICE","IRP_MN_REMOVE_DEVICE","IRP_MN_CANCEL_REMOVE_DEVICE","IRP_MN_STOP_DEVICE","IRP_MN_QUERY_STOP_DEVICE","IRP_MN_CANCEL_STOP_DEVICE","IRP_MN_QUERY_DEVICE_RELATIONS","IRP_MN_QUERY_INTERFACE","IRP_MN_QUERY_CAPABILITIES","IRP_MN_QUERY_RESOURCES","IRP_MN_QUERY_RESOURCE_REQUIREMENTS","IRP_MN_QUERY_DEVICE_TEXT","IRP_MN_FILTER_RESOURCE_REQUIREMENTS","","IRP_MN_READ_CONFIG","IRP_MN_WRITE_CONFIG","IRP_MN_EJECT","IRP_MN_SET_LOCK","IRP_MN_QUERY_ID","IRP_MN_QUERY_PNP_DEVICE_STATE","IRP_MN_QUERY_BUS_INFORMATION","IRP_MN_DEVICE_USAGE_NOTIFICATION","IRP_MN_SURPRISE_REMOVAL"};KdPrint(("PNP Request (%s)\n", fcnname[fcn]));
#endifstatus = (*fcntab[fcn])(pdx, Irp);KdPrint(("Leave HelloWDMPnp\n"));return status;
}#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,IN PIRP Irp)
{// 和之前的HelloDDK代码几乎一致:https://blog.csdn.net/ma_de_hao_mei_le/article/details/125596301PAGED_CODE();KdPrint(("Enter HelloWDMDispatchRoutine\n"));Irp->IoStatus.Status = STATUS_SUCCESS;Irp->IoStatus.Information = 0;IoCompleteRequest(Irp, IO_NO_INCREMENT);KdPrint(("Leave HelloWDMDispatchRoutine\n"));return STATUS_SUCCESS;
}#pragma PAGEDCODE
// 负责驱动程序的卸载
// 由于驱动的卸载任务放到了IRP_MN_REMOVE_DEVICE处理函数中
// 所以没什么需要HelloWDMUnload去做的,打印两行调试信息就行了
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{PAGED_CODE();KdPrint(("Enter HelloWDMUnload\n"));KdPrint(("Leave HelloWDMUnload\n"));
}
source
TARGETNAME=HelloWDM
TARGETTYPE=DRIVER
DRIVERTYPE=WDM
TARGETPATH=OBJINCLUDES=$(BASEDIR)\inc;\$(BASEDIR)\inc\ddk;\SOURCES=HelloWDM.cpp\