利用结构体实现一个控制码多个功能的驱动通信方式 ✅ SharedProtocol.h(通信协议头) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #pragma once #define MAX_PAYLOAD 512 #define IOCTL_EXCHANGE_MESSAGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) typedef enum _MESSAGE_TYPE { MSG_TYPE_SEND = 1 , MSG_TYPE_QUERY = 2 } MESSAGE_TYPE; typedef struct _USER_REQUEST { MESSAGE_TYPE MsgType; ULONG PayloadSize; CHAR Payload[MAX_PAYLOAD]; } USER_REQUEST, *PUSER_REQUEST;
✅ Common.h(通用头) 1 2 3 4 5 6 7 8 9 10 11 12 13 #pragma once #include <ntddk.h> #include "SharedProtocol.h" #define DEVICE_NAME L"\\Device\\MyMinimalDevice" #define SYMLINK_NAME L"\\DosDevices\\MyMinimalDevice" #define Log(...) DbgPrint("qi: " __VA_ARGS__) extern CHAR g_KernelMessage[MAX_PAYLOAD];extern PDEVICE_OBJECT g_DeviceObj;
✅ Driver.c(入口和卸载) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include "Common.h" NTSTATUS CreateDevAndSymLink (PDRIVER_OBJECT) ; NTSTATUS DispatchAll (PDEVICE_OBJECT, PIRP) ; NTSTATUS DispatchIoctl (PDEVICE_OBJECT, PIRP) ; CHAR g_KernelMessage[MAX_PAYLOAD] = "默认内核消息" ; PDEVICE_OBJECT g_DeviceObj = NULL ; VOID UnloadDriver (PDRIVER_OBJECT drv) { UNICODE_STRING symlink = RTL_CONSTANT_STRING(SYMLINK_NAME); IoDeleteSymbolicLink(&symlink); if (g_DeviceObj) IoDeleteDevice(g_DeviceObj); Log("卸载驱动完成\n" ); } NTSTATUS DriverEntry (PDRIVER_OBJECT drv, PUNICODE_STRING reg) { UNREFERENCED_PARAMETER(reg); NTSTATUS status = CreateDevAndSymLink(drv); if (!NT_SUCCESS(status)) return status; for (int i = 0 ; i < IRP_MJ_MAXIMUM_FUNCTION; i++) drv->MajorFunction[i] = DispatchAll; drv->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl; drv->DriverUnload = UnloadDriver; return STATUS_SUCCESS; }
✅ Device.c(通用 IRP 分发和设备创建) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include "Common.h" NTSTATUS CreateDevAndSymLink (PDRIVER_OBJECT drv) { UNICODE_STRING devName = RTL_CONSTANT_STRING(DEVICE_NAME); UNICODE_STRING symLink = RTL_CONSTANT_STRING(SYMLINK_NAME); NTSTATUS status = IoCreateDevice(drv, 0 , &devName, FILE_DEVICE_UNKNOWN, 0 , FALSE, &g_DeviceObj); if (!NT_SUCCESS(status)) return status; g_DeviceObj->Flags |= DO_BUFFERED_IO; return IoCreateSymbolicLink(&symLink, &devName); } NTSTATUS DispatchAll (PDEVICE_OBJECT dev, PIRP irp) { UNREFERENCED_PARAMETER(dev); PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(irp); NTSTATUS status = STATUS_SUCCESS; ULONG info = 0 ; switch (stack ->MajorFunction) { case IRP_MJ_CREATE: Log("Create\n" ); break ; case IRP_MJ_CLOSE: Log("Close\n" ); break ; case IRP_MJ_READ: { CHAR* userBuf = irp->AssociatedIrp.SystemBuffer; ULONG len = stack ->Parameters.Read.Length; size_t toCopy = min(len, strlen (g_KernelMessage) + 1 ); RtlCopyMemory(userBuf, g_KernelMessage, toCopy); info = (ULONG)toCopy; break ; } case IRP_MJ_WRITE: { CHAR* inBuf = irp->AssociatedIrp.SystemBuffer; ULONG len = stack ->Parameters.Write.Length; RtlZeroMemory(g_KernelMessage, MAX_PAYLOAD); RtlCopyMemory(g_KernelMessage, inBuf, min(len, MAX_PAYLOAD - 1 )); Log("Write: %s\n" , g_KernelMessage); info = len; break ; } default : status = STATUS_INVALID_DEVICE_REQUEST; break ; } irp->IoStatus.Status = status; irp->IoStatus.Information = info; IoCompleteRequest(irp, IO_NO_INCREMENT); return status; }
✅ Ioctl.c(专门处理 IRP_MJ_DEVICE_CONTROL) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include "Common.h" NTSTATUS DispatchIoctl (PDEVICE_OBJECT dev, PIRP irp) { UNREFERENCED_PARAMETER(dev); PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(irp); PUSER_REQUEST req = (PUSER_REQUEST)irp->AssociatedIrp.SystemBuffer; ULONG inLen = stack ->Parameters.DeviceIoControl.InputBufferLength; ULONG outLen = stack ->Parameters.DeviceIoControl.OutputBufferLength; NTSTATUS status = STATUS_SUCCESS; ULONG info = 0 ; switch (stack ->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_EXCHANGE_MESSAGE: if (inLen < sizeof (USER_REQUEST)) { status = STATUS_BUFFER_TOO_SMALL; break ; } switch (req->MsgType) { case MSG_TYPE_SEND: RtlZeroMemory(g_KernelMessage, MAX_PAYLOAD); RtlCopyMemory(g_KernelMessage, req->Payload, min(req->PayloadSize, MAX_PAYLOAD - 1 )); Log("收到结构体消息: %s\n" , g_KernelMessage); break ; case MSG_TYPE_QUERY: if (outLen >= sizeof (USER_REQUEST)) { PUSER_REQUEST out = (PUSER_REQUEST)irp->AssociatedIrp.SystemBuffer; out->MsgType = MSG_TYPE_QUERY; out->PayloadSize = (ULONG)min(strlen (g_KernelMessage), MAX_PAYLOAD - 1 ); RtlCopyMemory(out->Payload, g_KernelMessage, out->PayloadSize + 1 ); info = sizeof (USER_REQUEST); } else { status = STATUS_BUFFER_TOO_SMALL; } break ; default : status = STATUS_INVALID_PARAMETER; break ; } break ; default : status = STATUS_INVALID_DEVICE_REQUEST; break ; } irp->IoStatus.Status = status; irp->IoStatus.Information = info; IoCompleteRequest(irp, IO_NO_INCREMENT); return status; }
✅ 优点总结:
结构清晰 :每类功能单独文件管理。
扩展方便 :想加 IOCTL 只需改 Ioctl.c,结构体放 SharedProtocol.h。
日志可控 :统一 Log() 宏打印,方便控制开关。
更符合内核代码风格 ,适合以后上 WDF 或移植到 KMDF。
✅ 用户层测试代码 (UserApp.c) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define DEVICE_NAME L"\\\\.\\MyMinimalDevice" #define IOCTL_EXCHANGE_MESSAGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define MAX_PAYLOAD 512 typedef enum _MESSAGE_TYPE { MSG_TYPE_SEND = 1 , MSG_TYPE_QUERY = 2 } MESSAGE_TYPE; typedef struct _USER_REQUEST { MESSAGE_TYPE MsgType; ULONG PayloadSize; CHAR Payload[MAX_PAYLOAD]; } USER_REQUEST, *PUSER_REQUEST; void TestIoctl (HANDLE hDevice) { USER_REQUEST req; DWORD bytesReturned = 0 ; memset (&req, 0 , sizeof (req)); req.MsgType = MSG_TYPE_SEND; strcpy_s(req.Payload, MAX_PAYLOAD, "Hello from user space!" ); req.PayloadSize = (ULONG)strlen (req.Payload); if (!DeviceIoControl( hDevice, IOCTL_EXCHANGE_MESSAGE, &req, sizeof (req), NULL , 0 , &bytesReturned, NULL )) { printf ("DeviceIoControl failed with error %lu\n" , GetLastError()); } else { printf ("Message sent successfully: %s\n" , req.Payload); } req.MsgType = MSG_TYPE_QUERY; req.PayloadSize = 0 ; if (!DeviceIoControl( hDevice, IOCTL_EXCHANGE_MESSAGE, &req, sizeof (req), &req, sizeof (req), &bytesReturned, NULL )) { printf ("DeviceIoControl failed with error %lu\n" , GetLastError()); } else { printf ("Message received: %s\n" , req.Payload); } } int main () { HANDLE hDevice = CreateFile( DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0 , NULL , OPEN_EXISTING, 0 , NULL ); if (hDevice == INVALID_HANDLE_VALUE) { printf ("Failed to open device with error %lu\n" , GetLastError()); return 1 ; } TestIoctl(hDevice); CloseHandle(hDevice); return 0 ; }
代码说明: CreateFile:用于打开设备,成功时返回设备句柄。如果打开失败,会返回 INVALID_HANDLE_VALUE,并打印错误信息。
DeviceIoControl:用于与驱动进行 IO 控制交换。
第一个 DeviceIoControl 调用发送消息到内核驱动,控制码为 IOCTL_EXCHANGE_MESSAGE。
第二个 DeviceIoControl 调用查询内核驱动中的消息。
USER_REQUEST 结构体:定义了发送和查询消息的格式,包括 MsgType(消息类型)和 Payload(消息内容)。
错误处理:检查 DeviceIoControl 调用的返回值,失败时通过 GetLastError() 获取详细的错误信息。