对于应用层来说并没有什么特别的 , 同样调用ReadFile读取内核中的参数 , 同样for循环读取五次 , 代码如下:
#include <stdio.h>#include <Windows.h>int main(int argc, char *argv[]){ HANDLE hFile; char Buffer[10] = { 0 }; DWORD dwRet = 0; BOOL bRet; hFile = CreateFileA("\\\\.\\LySharkSync", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE)return 0; for (int x = 0; x < 5; x++) {bRet = ReadFile(hFile, Buffer, 20, &dwRet, NULL);if (!bRet){CloseHandle(hFile);return 0;}printf("读入数据: %s -> 读取长度: %d \n", Buffer, dwRet); } return 0;}这段代码运行效果如下:
文章插图
与同步模式不同,异步模式虽然同样使用ReadFile实现通信,但在通信中引入了Event事件通知机制 , 这也是异步与同步最大的区别所在,用户层可以分别创建多个Event事件,等待内核依次做出相应并最终一并返回 。
首先驱动内定义了_DeviceExtension自定义接口,该接口用于保存此次事件所对应的Irp以及其所对应的DPC时间等 。
异步分发函数_AsyncReadDispatch同样是被IRP_MJ_READ派遣函数触发的,触发后其内部会首先IoGetCurrentIrpStackLocation得到当前IRP的堆栈信息,然后设置IoMarkIrpPending()并最终将该IRP通过InsertTailList()插入到IRP链表内等待被处理 。
- IoMarkIrpPending
- 用于标记指定的IRP,标志着某个驱动的分发例程(分发函数)因需要被其他的驱动程序进一步处理最终返回STATUS_PENDING状态 。
// 初始化IRP链表InitializeListHead(&pDevExt->IrpList);// 初始化定时器KeInitializeTimer(&(pDevExt->timer));// 初始化DPC pDevExt是传给_CustomDpc函数的参数KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt);// 设置定时时间位1spDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000);// 启动定时器KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);驱动层完成代码如下所示:
#include <ntddk.h>// 自定义接口扩展typedef struct _DeviceExtension{ LIST_ENTRY IrpList; KTIMER timer; LARGE_INTEGER liDueTime; KDPC dpc;}DEV_EXT, *PDEV_EXT;// 默认派遣函数NTSTATUS _DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp){ _pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; _pIrp->IoStatus.Information = 0; IoCompleteRequest(_pIrp, IO_NO_INCREMENT); return _pIrp->IoStatus.Status;}// 创建派遣函数NTSTATUS _AsyncCreateCloseDispatch(PDEVICE_OBJECT _pDevcieObject, PIRP _pIrp){ _pIrp->IoStatus.Status = STATUS_SUCCESS; _pIrp->IoStatus.Information = 0; IoCompleteRequest(_pIrp, IO_NO_INCREMENT); return _pIrp->IoStatus.Status;}// 读取派遣函数NTSTATUS _AsyncReadDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp){ NTSTATUS status; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(_pIrp); PDEV_EXT pDevExt = (PDEV_EXT)_pDeviceObject->DeviceExtension; IoMarkIrpPending(_pIrp); // 将IRP插入自定义链表中插入的是ListEntry InsertTailList(&pDevExt->IrpList, &_pIrp->Tail.Overlay.ListEntry); // 返回pending 主要返回给I/O管理器的值必须和IRP的Pending标志位一致 // By: LyShark.com // 即调用iomarkirppending和返回值要一致 return STATUS_PENDING;}// DPC线程VOID _CustomDpc(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2){ PIRP pIrp; PDEV_EXT pDevExt = (PDEV_EXT)DeferredContext; PVOID pBuffer = NULL; ULONG uBufferLen = 0; PIO_STACK_LOCATION pIrpStack = NULL; do {if (!pDevExt){break;}// 检查尾端IRP链表是否为空 为空则跳出if (IsListEmpty(&pDevExt->IrpList)){break;}// 从IRP链表中取出一个IRP并完成该IRP 取出的是ListEntry的地址PLIST_ENTRY pListEntry = (PLIST_ENTRY)RemoveHeadList(&pDevExt->IrpList);if (!pListEntry)break;pIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);pIrpStack = IoGetCurrentIrpStackLocation(pIrp);DbgPrint("当前DPC Irp: 0x%x\n", pIrp);// 驱动程序的读写方式位直接I/OpBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress);if (pBuffer == NULL){pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);break;}uBufferLen = pIrpStack->Parameters.Read.Length;DbgPrint("读取DPC长度: %d\n", uBufferLen);// 支持5字节以下的读请求uBufferLen = uBufferLen > 13 ? 13 : uBufferLen;// 复制请求内容RtlCopyMemory(pBuffer, "hello lyshark", uBufferLen);pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = uBufferLen;// 完成该IRPIoCompleteRequest(pIrp, IO_NO_INCREMENT); } while (FALSE); // 重新设置定时器 KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);}// 卸载驱动VOID _UnloadDispatch(PDRIVER_OBJECT _pDriverObject){ UNICODE_STRING Win32DeviceName; PDEV_EXT pDevExt = (PDEV_EXT)_pDriverObject->DeviceObject->DeviceExtension; RtlInitUnicodeString(&Win32DeviceName, L"\\DosDevices\\LySharkAsync"); // 删除定时器 // LyShark KeCancelTimer(&pDevExt->timer); // 删除创建的设备 IoDeleteDevice(_pDriverObject->DeviceObject);}// 驱动入口NTSTATUS DriverEntry(PDRIVER_OBJECT _pDriverObject, PUNICODE_STRING _pRegistryPath){ UNICODE_STRING DeviceName, Win32DeivceName; PDEVICE_OBJECT pDeviceObject = NULL; NTSTATUS status; PDEV_EXT pDevExt = NULL; HANDLE hThread; OBJECT_ATTRIBUTES ObjectAttributes; CLIENT_ID CID; RtlInitUnicodeString(&DeviceName, L"\\Device\\LySharkAsync"); RtlInitUnicodeString(&Win32DeivceName, L"\\DosDevices\\LySharkAsync"); for (ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {_pDriverObject->MajorFunction[i] = _DefaultDispatch; } _pDriverObject->MajorFunction[IRP_MJ_CREATE] = _AsyncCreateCloseDispatch; _pDriverObject->MajorFunction[IRP_MJ_CLOSE] = _AsyncCreateCloseDispatch; _pDriverObject->MajorFunction[IRP_MJ_READ] = _AsyncReadDispatch; _pDriverObject->DriverUnload = _UnloadDispatch; // 分配自定义扩展 status = IoCreateDevice(_pDriverObject, sizeof(DEV_EXT), &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); if (!NT_SUCCESS(status))return status; if (!pDeviceObject)return STATUS_UNEXPECTED_IO_ERROR; pDeviceObject->Flags |= DO_DIRECT_IO; pDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; status = IoCreateSymbolicLink(&Win32DeivceName, &DeviceName); pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; pDevExt = (PDEV_EXT)pDeviceObject->DeviceExtension; // 初始化IRP链表 InitializeListHead(&pDevExt->IrpList); // 初始化定时器 KeInitializeTimer(&(pDevExt->timer)); // 初始化DPC pDevExt是传给_CustomDpc函数的参数 KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt); // 设置定时时间位1s pDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000); // 启动定时器 KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc); return STATUS_SUCCESS;}
推荐阅读
- gRPC+Protocol Buffer Go微服务实战 - 用户服务开发
- WPF开发经验-实现自带触控键盘的TextBox
- 驱动通信:通过PIPE管道与内核层通信
- mac通过docker一键部署Jenkins
- 如何通过执行SQL为低代码项目提速?
- 驱动开发:通过ReadFile与内核层通信
- 三十六 Java开发学习----SpringBoot三种配置文件解析
- aardio + Python 可视化快速开发桌面程序,一键生成独立 EXE
- Windows esp-idf 安装
- aardio + PHP 可视化快速开发独立 EXE 桌面程序