|
@@ -0,0 +1,455 @@
|
|
|
|
|
+# CommModifyKit 设计文档
|
|
|
|
|
+
|
|
|
|
|
+## 1. 项目概述
|
|
|
|
|
+
|
|
|
|
|
+CommModifyKit 是一个串口监控与数据拦截套件,由**用户态 DLL** 和**内核态驱动**两部分组成。它以透明过滤驱动的方式挂载到串口设备栈上,实时捕获串口的读写数据,并通过回调函数将事件上报给上层应用(CommModifyService)。
|
|
|
|
|
+
|
|
|
|
|
+### 核心能力
|
|
|
|
|
+
|
|
|
|
|
+- **透明拦截**:作为串口 Upper Filter 驱动,无需修改应用程序即可捕获串口数据
|
|
|
|
|
+- **实时回调**:捕获的读/写事件通过 `TOnData` 回调实时上抛
|
|
|
|
|
+- **双向读写**:支持向串口写入数据、发起异步读请求
|
|
|
|
|
+- **多端口并行**:支持同时监控多个 COM 端口,每端口独立环形缓冲
|
|
|
|
|
+
|
|
|
|
|
+### 项目结构
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+CommModifyKit/
|
|
|
|
|
+├── CommModifyKit.sln # VS 解决方案
|
|
|
|
|
+├── common/ # 用户态/内核态共享定义
|
|
|
|
|
+│ ├── CommKitEvents.h # 事件类型常量
|
|
|
|
|
+│ └── CommKitIoctl.h # IOCTL 码 + 共享数据结构
|
|
|
|
|
+├── dll/ # 用户态 DLL 项目
|
|
|
|
|
+│ ├── CommModifyKit.vcxproj # VS 项目文件
|
|
|
|
|
+│ ├── CommModifyKit.def # 导出定义(无修饰名)
|
|
|
|
|
+│ ├── dllmain.cpp # DLL 入口(最小化)
|
|
|
|
|
+│ ├── exports.cpp # 7 个 __stdcall 导出函数
|
|
|
|
|
+│ ├── MonitorManager.h/cpp # 核心管理器(单例)
|
|
|
|
|
+│ ├── DriverClient.h/cpp # 驱动 IOCTL 通信封装
|
|
|
|
|
+│ ├── EventLoop.h/cpp # 事件循环工作线程
|
|
|
|
|
+│ ├── ErrorStore.h/cpp # 线程局部错误码
|
|
|
|
|
+│ ├── Logger.h/cpp # 日志系统
|
|
|
|
|
+│ └── pch.h # 预编译头
|
|
|
|
|
+├── driver/ # 内核态驱动项目
|
|
|
|
|
+│ ├── CommModifyKitDriver.vcxproj # VS 项目文件(WDK)
|
|
|
|
|
+│ ├── CommModifyKit.inf # 驱动安装 INF
|
|
|
|
|
+│ ├── DriverEntry.cpp # KMDF 驱动入口
|
|
|
|
|
+│ ├── ClientConnection.h/cpp # 用户态连接管理
|
|
|
|
|
+│ ├── QueueCallback.h/cpp # I/O Queue IOCTL 处理
|
|
|
|
|
+│ ├── SerialFilter.h/cpp # 串口 IRP 拦截
|
|
|
|
|
+│ ├── EventRingBuffer.h/cpp # 内核环形缓冲区
|
|
|
|
|
+│ └── DeviceContext.h # WDF 设备上下文
|
|
|
|
|
+└── installer/ # 安装/卸载脚本
|
|
|
|
|
+ ├── install.ps1 # 安装脚本(管理员)
|
|
|
|
|
+ └── uninstall.ps1 # 卸载脚本(管理员)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 2. 架构设计
|
|
|
|
|
+
|
|
|
|
|
+### 2.1 整体架构
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+┌─────────────────────────────────────────────────────┐
|
|
|
|
|
+│ CommModifyService (上层应用) │
|
|
|
|
|
+│ 通过 GetProcAddress 调用 DLL 导出函数 │
|
|
|
|
|
+└────────────────────────┬────────────────────────────┘
|
|
|
|
|
+ │ __stdcall 导出接口
|
|
|
|
|
+┌────────────────────────▼────────────────────────────┐
|
|
|
|
|
+│ CommModifyKit.dll (用户态) │
|
|
|
|
|
+│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
|
|
|
|
|
+│ │MonitorManager│──│ DriverClient │──│ EventLoop │ │
|
|
|
|
|
+│ │ (单例协调) │ │ (IOCTL 封装) │ │(回调线程) │ │
|
|
|
|
|
+│ └──────────────┘ └──────┬───────┘ └─────┬──────┘ │
|
|
|
|
|
+│ │ │ │ │
|
|
|
|
|
+│ ┌──────▼──────┐ ┌─────▼─────┐ ┌──────▼──────┐ │
|
|
|
|
|
+│ │ ErrorStore │ │DeviceIoCtl│ │ TOnData回调 │ │
|
|
|
|
|
+│ │(线程局部错误)│ │ (系统API) │ │ (上报上层) │ │
|
|
|
|
|
+│ └─────────────┘ └─────┬─────┘ └─────────────┘ │
|
|
|
|
|
+└────────────────────────────┼─────────────────────────┘
|
|
|
|
|
+ │ IOCTL
|
|
|
|
|
+┌────────────────────────────▼─────────────────────────┐
|
|
|
|
|
+│ CommModifyKit.sys (内核态 KMDF 驱动) │
|
|
|
|
|
+│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
|
|
|
|
|
+│ │QueueCallback │──│ClientConnection│──│SerialFilter│ │
|
|
|
|
|
+│ │ (IOCTL 分发) │ │ (端口/缓冲管理)│ │(IRP 拦截) │ │
|
|
|
|
|
+│ └──────────────┘ └──────┬───────┘ └─────┬──────┘ │
|
|
|
|
|
+│ │ │ │
|
|
|
|
|
+│ ┌──────▼──────┐ ┌──────▼──────┐ │
|
|
|
|
|
+│ │EventRingBuf │ │下层串口设备栈│ │
|
|
|
|
|
+│ │(每端口环形缓冲)│ │(真实串口驱动)│ │
|
|
|
|
|
+│ └─────────────┘ └─────────────┘ │
|
|
|
|
|
+└──────────────────────────────────────────────────────┘
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2.2 数据流
|
|
|
|
|
+
|
|
|
|
|
+#### 监控捕获流程(读/写事件上抛)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+应用读/写串口 → IRP 到达过滤驱动 → SerialFilter 拦截
|
|
|
|
|
+ → IRP 透传到下层串口 → 下层完成 IRP → 完成例程回调
|
|
|
|
|
+ → CaptureIrpData() 提取数据 → Push() 写入环形缓冲
|
|
|
|
|
+ → DLL EventLoop 线程 ReadEvents IOCTL → 批量弹出事件
|
|
|
|
|
+ → TOnData 回调 → 上层应用收到事件
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 写数据流程(应用主动写入)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+上层调用 SetWrite_Data → MonitorManager.WriteData()
|
|
|
|
|
+ → DriverClient.WritePort() → IOCTL_COMMKIT_WRITE_PORT
|
|
|
|
|
+ → 驱动 HandleWritePort() → 构造 IRP 发给下层串口
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 3. 模块详细设计
|
|
|
|
|
+
|
|
|
|
|
+### 3.1 用户态 DLL
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.1 导出接口(exports.cpp)
|
|
|
|
|
+
|
|
|
|
|
+DLL 导出 7 个 `__stdcall` 函数,通过 `.def` 文件确保导出名无修饰:
|
|
|
|
|
+
|
|
|
|
|
+| 序号 | 函数名 | 参数 | 返回值 | 说明 |
|
|
|
|
|
+|------|--------|------|--------|------|
|
|
|
|
|
+| 1 | `InitMonitor` | `wchar_t* cKey, TOnData aOnData` | BOOL | 初始化监控:校验 Key、启动驱动、注册回调 |
|
|
|
|
|
+| 2 | `Monitor` | `DWORD dwNumber` | BOOL | 开始监控指定串口(1=COM1) |
|
|
|
|
|
+| 3 | `Stop` | `DWORD dwNumber` | BOOL | 停止监控指定串口 |
|
|
|
|
|
+| 4 | `SetWrite_Data` | `DWORD dwNumber, char* lpBuffer, int Len` | BOOL | 向串口写数据 |
|
|
|
|
|
+| 5 | `SetRead_Data` | `DWORD dwNumber, char* lpBuffer, int Len` | BOOL | 发起异步读请求(结果通过回调返回) |
|
|
|
|
|
+| 6 | `FreeMonitor` | 无 | void | 释放所有资源 |
|
|
|
|
|
+| 7 | `get_LastErrror` | 无 | int | 获取最近错误码(注意 3 个 r) |
|
|
|
|
|
+
|
|
|
|
|
+**回调函数类型定义**:
|
|
|
|
|
+
|
|
|
|
|
+```cpp
|
|
|
|
|
+typedef LONG(CALLBACK* TOnData)(
|
|
|
|
|
+ int Sequence, // 递增序列号
|
|
|
|
|
+ double dTime, // OLE Automation date 时间戳
|
|
|
|
|
+ DWORD ComNumber, // 串口编号(COM n 的 n)
|
|
|
|
|
+ DWORD Irp, // 事件类型(1=OPEN, 2=READ, 3=WRITE, 4=CLOSE)
|
|
|
|
|
+ DWORD dwSize, // 数据字节数
|
|
|
|
|
+ char* lpData // 数据指针(只读,不应释放)
|
|
|
|
|
+);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.2 MonitorManager(核心管理器)
|
|
|
|
|
+
|
|
|
|
|
+**职责**:协调 DriverClient 与 EventLoop,提供 7 个导出函数的底层实现。
|
|
|
|
|
+
|
|
|
|
|
+**设计要点**:
|
|
|
|
|
+- **全局单例**:`GetMonitorManager()` 返回静态实例,导出函数通过它统一入口
|
|
|
|
|
+- **互斥保护**:所有公开方法使用 `std::mutex` 保护,保证线程安全
|
|
|
|
|
+- **延迟初始化**:不在 DllMain 中初始化,避免 Loader Lock 死锁
|
|
|
|
|
+- **幂等操作**:重复 InitMonitor / Monitor 已有端口均视为成功
|
|
|
|
|
+
|
|
|
|
|
+**InitMonitor 流程**:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+1. 校验回调非空、Key 非空
|
|
|
|
|
+2. EnsureDriverReady()
|
|
|
|
|
+ ├── 检查管理员权限(否则返回 -254)
|
|
|
|
|
+ ├── 打开 SCM,检查/创建驱动服务
|
|
|
|
|
+ ├── 检查驱动 .sys 文件存在性
|
|
|
|
|
+ └── 启动驱动服务(若未运行)
|
|
|
|
|
+3. DriverClient.Open() → CreateFile("\\\.\\CommModifyKit")
|
|
|
|
|
+4. DriverClient.RegisterCallback() → IOCTL_COMMKIT_REGISTER_CALLBACK
|
|
|
|
|
+5. EventLoop.Start() → 启动事件循环工作线程
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**错误码表**:
|
|
|
|
|
+
|
|
|
|
|
+| 错误码 | 含义 |
|
|
|
|
|
+|--------|------|
|
|
|
|
|
+| 0 | 成功 |
|
|
|
|
|
+| -1 | 未初始化 / 回调为空 |
|
|
|
|
|
+| 3 | 串口读取失败 |
|
|
|
|
|
+| 4 | 串口写入失败 |
|
|
|
|
|
+| -241 | 安装驱动失败(SCM 操作失败) |
|
|
|
|
|
+| -243 | 其他错误(注册回调/事件循环失败) |
|
|
|
|
|
+| -244 | 驱动文件不存在或设备打开失败 |
|
|
|
|
|
+| -252 | Key 无效 |
|
|
|
|
|
+| -253 | 启动驱动服务失败 |
|
|
|
|
|
+| -254 | 需要管理员权限 |
|
|
|
|
|
+| -255 | 绑定串口失败 |
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.3 DriverClient(驱动通信封装)
|
|
|
|
|
+
|
|
|
|
|
+**职责**:封装 `CreateFile` + `DeviceIoControl`,提供同步 IOCTL 调用接口。
|
|
|
|
|
+
|
|
|
|
|
+| 方法 | 对应 IOCTL | 说明 |
|
|
|
|
|
+|------|-----------|------|
|
|
|
|
|
+| `Open()` | CreateFile | 打开 `\\.\CommModifyKit` 设备 |
|
|
|
|
|
+| `RegisterCallback()` | IOCTL_COMMKIT_REGISTER_CALLBACK | 注册回调管道 |
|
|
|
|
|
+| `AttachPort(com)` | IOCTL_COMMKIT_ATTACH_PORT | 绑定串口 |
|
|
|
|
|
+| `DetachPort(com)` | IOCTL_COMMKIT_DETACH_PORT | 解绑串口 |
|
|
|
|
|
+| `WritePort(com,data,len)` | IOCTL_COMMKIT_WRITE_PORT | 写数据(METHOD_IN_DIRECT) |
|
|
|
|
|
+| `ReadPort(com,data,len)` | IOCTL_COMMKIT_READ_PORT | 读请求(METHOD_IN_DIRECT) |
|
|
|
|
|
+| `ReadEvents(buf,max)` | IOCTL_COMMKIT_READ_EVENTS | 批量读取事件 |
|
|
|
|
|
+| `FreeAll()` | IOCTL_COMMKIT_FREE_ALL | 释放所有 |
|
|
|
|
|
+
|
|
|
|
|
+**WritePort/ReadPort 数据组装**:使用 `COMMKIT_DATA_REQUEST` 变长结构,实际大小 = `sizeof(COMMKIT_DATA_REQUEST) - 1 + len`(Data 字段为 1 字节占位)。
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.4 EventLoop(事件循环线程)
|
|
|
|
|
+
|
|
|
|
|
+**职责**:独立工作线程循环从驱动读取事件,调用 `TOnData` 回调上报上层。
|
|
|
|
|
+
|
|
|
|
|
+**工作流程**:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+while (running_) {
|
|
|
|
|
+ // 阻塞式 IOCTL:驱动无事件时 IRP pending
|
|
|
|
|
+ DeviceIoControl(IOCTL_COMMKIT_READ_EVENTS, batch, ...);
|
|
|
|
|
+
|
|
|
|
|
+ // 处理返回事件
|
|
|
|
|
+ for (event in batch) {
|
|
|
|
|
+ callback_(event.Sequence, event.TimeStamp, ...);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**设计要点**:
|
|
|
|
|
+- **阻塞式读取**:IOCTL 在驱动端 pending,有事件时才返回,避免空转
|
|
|
|
|
+- **批量读取**:单次最多读取 `COMMKIT_EVENT_BATCH`(16) 条事件
|
|
|
|
|
+- **优雅停止**:`Stop()` 设置 `running_=false`,关闭驱动句柄使阻塞 IOCTL 返回错误,线程自然退出
|
|
|
|
|
+- **错误重试**:非致命错误 sleep 50ms 后重试,避免空转
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.5 ErrorStore(线程局部错误码)
|
|
|
|
|
+
|
|
|
|
|
+使用 `thread_local int` 存储,与 Win32 `GetLastError()` 语义一致:每线程独立,记录最近一次操作结果。
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.6 Logger(日志系统)
|
|
|
|
|
+
|
|
|
|
|
+- **输出方式**:`OutputDebugStringW`(DebugView 可捕获)+ 可选文件输出(`%TEMP%\CommModifyKit.log`)
|
|
|
|
|
+- **级别**:Debug / Info / Warning / Error,默认 Info
|
|
|
|
|
+- **格式**:`[2026-07-21 10:00:00][INF] message`
|
|
|
|
|
+- **独立实现**:不依赖 CommModifyService 的 Logger
|
|
|
|
|
+
|
|
|
|
|
+#### 3.1.7 DllMain(DLL 入口)
|
|
|
|
|
+
|
|
|
|
|
+**关键约束**:DllMain 在 Loader Lock 内执行,严禁:
|
|
|
|
|
+- `LoadLibrary` / `LoadLibraryEx`(递归加载死锁)
|
|
|
|
|
+- 复杂 STL 操作(`std::thread`、`std::mutex` 动态初始化)
|
|
|
|
|
+
|
|
|
|
|
+因此 DllMain 仅执行 `DisableThreadLibraryCalls`,所有初始化延迟到首次导出函数调用。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### 3.2 内核态驱动
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.1 DriverEntry(KMDF 入口)
|
|
|
|
|
+
|
|
|
|
|
+- 初始化 WDF 驱动对象
|
|
|
|
|
+- 调用 `CreateControlDevice()` 创建控制设备 `\\.\CommModifyKit`
|
|
|
|
|
+- 注册 `EvtDeviceAdd`(PnP 过滤设备创建)和 `EvtDriverUnload`
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.2 ClientConnection(连接管理)
|
|
|
|
|
+
|
|
|
|
|
+**职责**:维护 DLL 与驱动之间的连接状态和端口监控列表。
|
|
|
|
|
+
|
|
|
|
|
+- **端口表**:`ports_table_[256]`,最多支持 256 个 COM 端口
|
|
|
|
|
+- **环形缓冲**:每个端口首次 ATTACH 时分配 `EventRingBuffer`(非分页内存)
|
|
|
|
|
+- **IOCTL 处理**:HandleXxx 系列方法由 QueueCallback 调度
|
|
|
|
|
+- **同步**:`KSPIN_LOCK` 保护端口表(IRQL 安全)
|
|
|
|
|
+
|
|
|
|
|
+**HandleAttachPort 流程**:
|
|
|
|
|
+1. 查找端口上下文,不存在返回 `STATUS_DEVICE_DOES_NOT_EXIST`
|
|
|
|
|
+2. 若端口无环形缓冲,分配并初始化
|
|
|
|
|
+3. 推入 `OP_OPEN` 事件
|
|
|
|
|
+4. 设置 `MonitoringEnabled = TRUE`
|
|
|
|
|
+
|
|
|
|
|
+**HandleReadEvents 流程**:
|
|
|
|
|
+1. 遍历所有已启用端口的环形缓冲
|
|
|
|
|
+2. 逐端口 `PopBatch()` 弹出事件到用户缓冲
|
|
|
|
|
+3. 返回总字节数
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.3 QueueCallback(IOCTL 分发)
|
|
|
|
|
+
|
|
|
|
|
+基于 WDF I/O Queue 的 Sequential 派发模式,将 IOCTL 码路由到 `ClientConnection` 对应方法。
|
|
|
|
|
+
|
|
|
|
|
+**控制设备创建流程**:
|
|
|
|
|
+```
|
|
|
|
|
+WdfControlDeviceInitAllocate(SDDL_DEVOBJ_SYS_ONLY_ADM) // 仅管理员可访问
|
|
|
|
|
+→ WdfDeviceInitAssignName("\Device\CommModifyKit")
|
|
|
|
|
+→ WdfDeviceCreate()
|
|
|
|
|
+→ WdfDeviceCreateSymbolicLink("\DosDevices\CommModifyKit")
|
|
|
|
|
+→ WdfIoQueueCreate(Sequential)
|
|
|
|
|
+→ WdfControlFinishInitializing()
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.4 SerialFilter(串口 IRP 拦截)
|
|
|
|
|
+
|
|
|
|
|
+**职责**:作为上层过滤驱动 attach 到串口设备栈,拦截读写 IRP。
|
|
|
|
|
+
|
|
|
|
|
+| 派遣例程 | 行为 |
|
|
|
|
|
+|---------|------|
|
|
|
|
|
+| `DispatchRead` | 标记 IRP Pending → 设置 `OnReadComplete` 完成例程 → `IoCallDriver` 下层 |
|
|
|
|
|
+| `DispatchWrite` | 标记 IRP Pending → 设置 `OnWriteComplete` 完成例程 → `IoCallDriver` 下层 |
|
|
|
|
|
+| `DispatchDeviceControl` | `IoSkipCurrentIrpStackLocation` → `IoCallDriver` 透传 |
|
|
|
|
|
+
|
|
|
|
|
+**CaptureIrpData**(IRP 完成后提取数据):
|
|
|
|
|
+- 从 IRP 的 `MdlAddress` 或 `SystemBuffer` 获取数据指针
|
|
|
|
|
+- 从 `IoStatus.Information` 获取实际数据长度
|
|
|
|
|
+- 调用 `EventRingBuffer.Push()` 写入环形缓冲
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.5 EventRingBuffer(环形缓冲区)
|
|
|
|
|
+
|
|
|
|
|
+- **容量**:`COMMKIT_RING_CAPACITY` = 256 条/端口
|
|
|
|
|
+- **同步**:`KSPIN_LOCK`(IRQL <= DISPATCH_LEVEL 安全)
|
|
|
|
|
+- **溢出策略**:满了覆盖最旧事件(tail 跟随 head),不阻塞 IRP 完成例程
|
|
|
|
|
+- **内存**:非分页池分配(`ExAllocatePool2` + `POOL_FLAG_NON_PAGED`)
|
|
|
|
|
+- **批量弹出**:`PopBatch()` 一次弹出多条,减少锁竞争
|
|
|
|
|
+
|
|
|
|
|
+#### 3.2.6 DeviceContext(设备上下文)
|
|
|
|
|
+
|
|
|
|
|
+每个串口过滤设备关联的上下文结构:
|
|
|
|
|
+
|
|
|
|
|
+| 字段 | 类型 | 说明 |
|
|
|
|
|
+|------|------|------|
|
|
|
|
|
+| `ComNumber` | ULONG | COM 编号(0=未知) |
|
|
|
|
|
+| `MonitoringEnabled` | BOOLEAN | 是否已通过 ATTACH_PORT 启用 |
|
|
|
|
|
+| `RingBuffer` | PVOID | EventRingBuffer 实例指针 |
|
|
|
|
|
+| `LowerDevice` | PDEVICE_OBJECT | 下层真实串口设备 |
|
|
|
|
|
+| `WdfDevice` | WDFDEVICE | WDF 设备句柄 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### 3.3 共享定义(common/)
|
|
|
|
|
+
|
|
|
|
|
+#### CommKitEvents.h — 事件类型常量
|
|
|
|
|
+
|
|
|
|
|
+| 常量 | 值 | 含义 |
|
|
|
|
|
+|------|---|------|
|
|
|
|
|
+| `COMMKIT_OP_NONE` | 0 | 无事件 |
|
|
|
|
|
+| `COMMKIT_OP_OPEN` | 1 | 串口打开/开始监控 |
|
|
|
|
|
+| `COMMKIT_OP_READ` | 2 | 读取到数据 |
|
|
|
|
|
+| `COMMKIT_OP_WRITE` | 3 | 写入数据 |
|
|
|
|
|
+| `COMMKIT_OP_CLOSE` | 4 | 串口关闭/停止监控 |
|
|
|
|
|
+
|
|
|
|
|
+#### CommKitIoctl.h — IOCTL 码与数据结构
|
|
|
|
|
+
|
|
|
|
|
+| IOCTL 码 | 功能号 | 方法 | 说明 |
|
|
|
|
|
+|----------|--------|------|------|
|
|
|
|
|
+| `IOCTL_COMMKIT_REGISTER_CALLBACK` | 0x800 | METHOD_BUFFERED | 注册回调 |
|
|
|
|
|
+| `IOCTL_COMMKIT_ATTACH_PORT` | 0x801 | METHOD_BUFFERED | 绑定串口 |
|
|
|
|
|
+| `IOCTL_COMMKIT_DETACH_PORT` | 0x802 | METHOD_BUFFERED | 解绑串口 |
|
|
|
|
|
+| `IOCTL_COMMKIT_WRITE_PORT` | 0x803 | METHOD_IN_DIRECT | 写数据 |
|
|
|
|
|
+| `IOCTL_COMMKIT_READ_PORT` | 0x804 | METHOD_IN_DIRECT | 读请求 |
|
|
|
|
|
+| `IOCTL_COMMKIT_READ_EVENTS` | 0x805 | METHOD_OUT_DIRECT | 批量读事件 |
|
|
|
|
|
+| `IOCTL_COMMKIT_FREE_ALL` | 0x806 | METHOD_BUFFERED | 释放全部 |
|
|
|
|
|
+
|
|
|
|
|
+**关键数据结构**(均 `#pragma pack(1)` 对齐):
|
|
|
|
|
+
|
|
|
|
|
+```cpp
|
|
|
|
|
+// 事件结构(内核 → 用户态)
|
|
|
|
|
+struct COMMKIT_EVENT {
|
|
|
|
|
+ INT32 Sequence; // 递增序列号
|
|
|
|
|
+ double TimeStamp; // OLE Automation date
|
|
|
|
|
+ UINT32 ComNumber; // 串口编号
|
|
|
|
|
+ UINT32 EventType; // 事件类型
|
|
|
|
|
+ UINT32 DataSize; // 数据字节数(≤ 4096)
|
|
|
|
|
+ CHAR Data[4096]; // 内联数据缓冲
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 端口请求(ATTACH/DETACH 输入)
|
|
|
|
|
+struct COMMKIT_PORT_REQUEST {
|
|
|
|
|
+ UINT32 ComNumber;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 数据请求(WRITE/READ 输入,变长)
|
|
|
|
|
+struct COMMKIT_DATA_REQUEST {
|
|
|
|
|
+ UINT32 ComNumber;
|
|
|
|
|
+ UINT32 DataLen;
|
|
|
|
|
+ CHAR Data[1]; // 变长数据起始
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**常量**:
|
|
|
|
|
+
|
|
|
|
|
+| 常量 | 值 | 说明 |
|
|
|
|
|
+|------|---|------|
|
|
|
|
|
+| `COMMKIT_MAX_DATA` | 4096 | 单条事件最大数据字节 |
|
|
|
|
|
+| `COMMKIT_EVENT_BATCH` | 16 | 单次 IOCTL 最多返回事件数 |
|
|
|
|
|
+| `COMMKIT_RING_CAPACITY` | 256 | 每端口环形缓冲容量 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 4. 关键设计决策
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 Loader Lock 安全
|
|
|
|
|
+
|
|
|
|
|
+DllMain 仅执行 `DisableThreadLibraryCalls`,所有初始化延迟到 `InitMonitor` 首次调用。PROCESS_DETACH 也不调用 `FreeAll`,由上层 `FreeMonitor()` 显式释放。
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 SEH 异常保护
|
|
|
|
|
+
|
|
|
|
|
+WorkerThread(EventLoop::Run)使用 `__try/__except` 包裹,防止 SEH 异常导致线程静默崩溃和初始化超时。
|
|
|
|
|
+
|
|
|
|
|
+### 4.3 超时处理使用 detach()
|
|
|
|
|
+
|
|
|
|
|
+当 WorkerThread 超时未完成时,使用 `detach()` 而非 `join()`,避免主线程无限阻塞。
|
|
|
|
|
+
|
|
|
|
|
+### 4.4 环形缓冲溢出策略
|
|
|
|
|
+
|
|
|
|
|
+满了覆盖最旧事件而非阻塞 IRP 完成例程,保证串口 I/O 性能不受监控影响。
|
|
|
|
|
+
|
|
|
|
|
+### 4.5 驱动服务常驻
|
|
|
|
|
+
|
|
|
|
|
+`FreeMonitor` 不卸载驱动服务(不调用 `DeleteService`),驱动在系统生命周期内常驻,避免反复安装/卸载的开销和权限问题。
|
|
|
|
|
+
|
|
|
|
|
+### 4.6 1 字节 pack 对齐
|
|
|
|
|
+
|
|
|
|
|
+所有跨边界(内核态↔用户态,WOW64)传递的结构使用 `#pragma pack(1)`,避免对齐差异导致数据损坏。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 5. 编译与构建
|
|
|
|
|
+
|
|
|
|
|
+### 5.1 用户态 DLL
|
|
|
|
|
+
|
|
|
|
|
+- **工具链**:MSVC v142(VS2019)
|
|
|
|
|
+- **平台**:Win32(x86)为主,x64 可选
|
|
|
|
|
+- **配置**:Release / Debug
|
|
|
|
|
+- **输出**:`build\Release\Win32\CommModifyKit.dll`
|
|
|
|
|
+
|
|
|
|
|
+### 5.2 内核态驱动
|
|
|
|
|
+
|
|
|
|
|
+- **工具链**:WDK + MSVC(KMDF)
|
|
|
|
|
+- **平台**:x64(内核驱动仅 64 位)
|
|
|
|
|
+- **输出**:`build\Release\x64\CommModifyKit.sys`
|
|
|
|
|
+
|
|
|
|
|
+### 5.3 解决方案配置
|
|
|
|
|
+
|
|
|
|
|
+| 项目 | Win32 配置 | x64 配置 |
|
|
|
|
|
+|------|-----------|---------|
|
|
|
|
|
+| CommModifyKit (DLL) | Win32 | x64 |
|
|
|
|
|
+| CommModifyKitDriver | 映射到 x64 | x64 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 6. 与 CommModifyService 的集成
|
|
|
|
|
+
|
|
|
|
|
+CommModifyService 通过 `GetProcAddress` 动态加载 DLL 的 7 个导出函数:
|
|
|
|
|
+
|
|
|
|
|
+```cpp
|
|
|
|
|
+// CommModifyService/DllWrapper.cpp 伪代码
|
|
|
|
|
+HMODULE hDll = LoadLibrary("CommModifyKit.dll");
|
|
|
|
|
+auto pInitMonitor = (BOOL(__stdcall*)(wchar_t*, TOnData))
|
|
|
|
|
+ GetProcAddress(hDll, "InitMonitor");
|
|
|
|
|
+auto pMonitor = (BOOL(__stdcall*)(DWORD))
|
|
|
|
|
+ GetProcAddress(hDll, "Monitor");
|
|
|
|
|
+// ... 其余 5 个函数同理
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**调用时序**:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+1. InitMonitor(key, callback) // 初始化
|
|
|
|
|
+2. Monitor(com_number) // 监控各端口
|
|
|
|
|
+3. [循环] SetWrite_Data / SetRead_Data // 读写操作
|
|
|
|
|
+4. [回调] TOnData // 事件到达
|
|
|
|
|
+5. Stop(com_number) // 停止某端口
|
|
|
|
|
+6. FreeMonitor() // 全部释放
|
|
|
|
|
+```
|