CommModifyKit-Usage.md 13 KB

CommModifyKit 使用说明文档

1. 环境要求

项目 要求
操作系统 Windows 10/11 x64
编译器 MSVC v142(Visual Studio 2019)
WDK Windows Driver Kit 10(编译内核驱动)
权限 安装驱动需要管理员权限
测试签名 开发阶段需启用 Windows 测试签名模式

2. 编译构建

2.1 编译用户态 DLL

  1. 用 VS2019 打开 CommModifyKit.sln
  2. 选择配置 Release | Win32
  3. 生成项目 CommModifyKit
  4. 产物位于 build\Release\Win32\CommModifyKit.dll

2.2 编译内核态驱动

  1. 确保已安装 WDK 10
  2. 选择配置 Release | x64
  3. 生成项目 CommModifyKitDriver
  4. 产物位于 build\Release\x64\CommModifyKit.sys

注意:驱动项目仅支持 x64 平台,Win32 配置会自动映射到 x64。


3. 安装

3.1 自动安装(推荐)

管理员身份打开 PowerShell,执行:

cd CommModifyKit\installer
.\install.ps1

脚本自动完成:

  1. 查找构建产物(.sys、.inf、.dll)
  2. 拷贝驱动文件到 %SystemRoot%\System32\drivers\
  3. 创建并启动驱动服务
  4. 拷贝 DLL 到 CommModifyService 目录(如检测到)
  5. 启用测试签名(开发阶段需要,需重启生效)

安装后确认

  • 计算机已重启(启用测试签名后)
  • CommModifyKit.dll 已拷贝到 CommModifyService.exe 所在目录
  • config.jsoncomm.use_dll 设为 true

3.2 手动安装

安装驱动

# 1. 拷贝驱动文件
copy CommModifyKit.sys %SystemRoot%\System32\drivers\
copy CommModifyKit.inf %SystemRoot%\System32\drivers\

# 2. 创建驱动服务
sc.exe create CommModifyKit binPath= "%SystemRoot%\System32\drivers\CommModifyKit.sys" type= kernel start= demand error= normal

# 3. 启动驱动服务
sc.exe start CommModifyKit

# 4. 启用测试签名(首次需要,重启生效)
bcdedit /set testsigning on

安装 DLL

CommModifyKit.dll 拷贝到 CommModifyService.exe 所在目录即可。

3.3 验证安装

# 检查驱动服务状态
sc.exe query CommModifyKit

# 检查测试签名
bcdedit /enum | Select-String "testsigning"

# 检查 DLL 是否在目标目录
dir CommModifyService\Release\CommModifyKit.dll

4. 卸载

4.1 自动卸载

管理员身份打开 PowerShell,执行:

cd CommModifyKit\installer
.\uninstall.ps1

脚本自动完成:

  1. 停止并删除驱动服务
  2. 删除 %SystemRoot%\System32\drivers\ 下的 .sys 和 .inf 文件
  3. 检查并提示通过 pnputil 移除驱动包

4.2 手动卸载

# 1. 停止驱动服务
sc.exe stop CommModifyKit

# 2. 删除驱动服务
sc.exe delete CommModifyKit

# 3. 删除驱动文件
del %SystemRoot%\System32\drivers\CommModifyKit.sys
del %SystemRoot%\System32\drivers\CommModifyKit.inf

# 4. 删除 DLL(手动)
del CommModifyService\Release\CommModifyKit.dll

# 5. 关闭测试签名(可选,重启生效)
bcdedit /set testsigning off

5. API 使用说明

5.1 导出函数一览

CommModifyKit.dll 导出 7 个 __stdcall 函数:

函数 用途
InitMonitor 初始化监控(必须最先调用)
Monitor 开始监控指定串口
Stop 停止监控指定串口
SetWrite_Data 向串口写数据
SetRead_Data 发起异步读请求
FreeMonitor 释放所有资源
get_LastErrror 获取最近错误码

5.2 回调函数

// 事件回调类型
typedef LONG(CALLBACK* TOnData)(
    int    Sequence,    // 递增序列号
    double dTime,       // OLE Automation date 时间戳
    DWORD  ComNumber,   // 串口编号(COM n 的 n)
    DWORD  Irp,         // 事件类型
    DWORD  dwSize,      // 数据字节数
    char*  lpData        // 数据指针(只读!)
);

事件类型(Irp 参数)

常量 含义
1 COMMKIT_OP_OPEN 串口打开/开始监控
2 COMMKIT_OP_READ 读取到数据(dwSize > 0, lpData 有效)
3 COMMKIT_OP_WRITE 写入数据(dwSize > 0, lpData 有效)
4 COMMKIT_OP_CLOSE 串口关闭/停止监控

重要:回调中的 lpData 指针指向 DLL 内部缓冲,仅在回调执行期间有效。如需保留数据,请在回调内拷贝,切勿存储指针。

5.3 完整调用示例

#include <windows.h>
#include <cstdio>

// 回调函数类型(与 DLL 导出一致)
typedef LONG(CALLBACK* TOnData)(int, double, DWORD, DWORD, DWORD, char*);

// 导出函数指针类型
typedef BOOL (__stdcall *FnInitMonitor)(wchar_t*, TOnData);
typedef BOOL (__stdcall *FnMonitor)(DWORD);
typedef BOOL (__stdcall *FnStop)(DWORD);
typedef BOOL (__stdcall *FnSetWriteData)(DWORD, char*, int);
typedef BOOL (__stdcall *FnSetReadData)(DWORD, char*, int);
typedef void (__stdcall *FnFreeMonitor)(void);
typedef int  (__stdcall *FnGetLastError)(void);

// 事件回调实现
LONG CALLBACK MyOnData(int Sequence, double dTime,
                        DWORD ComNumber, DWORD Irp,
                        DWORD dwSize, char* lpData) {
    const char* type_name = "?";
    switch (Irp) {
        case 1: type_name = "OPEN";  break;
        case 2: type_name = "READ";  break;
        case 3: type_name = "WRITE"; break;
        case 4: type_name = "CLOSE"; break;
    }
    printf("[COM%d] %s seq=%d size=%u\n",
           ComNumber, type_name, Sequence, dwSize);
    // 如需保留数据,在此处拷贝:
    // std::vector<char> data(lpData, lpData + dwSize);
    return 0;
}

int main() {
    // 1. 加载 DLL
    HMODULE hDll = LoadLibraryW(L"CommModifyKit.dll");
    if (!hDll) {
        printf("LoadLibrary failed, error=%lu\n", GetLastError());
        return 1;
    }

    // 2. 获取导出函数指针
    auto InitMonitor   = (FnInitMonitor)  GetProcAddress(hDll, "InitMonitor");
    auto Monitor       = (FnMonitor)      GetProcAddress(hDll, "Monitor");
    auto Stop          = (FnStop)         GetProcAddress(hDll, "Stop");
    auto SetWrite_Data = (FnSetWriteData) GetProcAddress(hDll, "SetWrite_Data");
    auto SetRead_Data  = (FnSetReadData)  GetProcAddress(hDll, "SetRead_Data");
    auto FreeMonitor   = (FnFreeMonitor)  GetProcAddress(hDll, "FreeMonitor");
    auto get_LastErrror= (FnGetLastError) GetProcAddress(hDll, "get_LastErrror");

    // 3. 初始化监控
    wchar_t key[] = L"C09976511B62F0ADB759D87E6906D865";
    if (!InitMonitor(key, MyOnData)) {
        int err = get_LastErrror();
        printf("InitMonitor failed, error=%d\n", err);
        FreeLibrary(hDll);
        return 1;
    }
    printf("InitMonitor succeeded\n");

    // 4. 开始监控 COM1 和 COM3
    Monitor(1);  // COM1
    Monitor(3);  // COM3

    // 5. 向 COM1 写数据
    char data[] = "AT\r\n";
    SetWrite_Data(1, data, (int)strlen(data));

    // 6. 从 COM1 发起异步读(结果通过回调 MyOnData 返回)
    char buf[256] = {0};
    SetRead_Data(1, buf, sizeof(buf));

    // 7. 等待事件...(实际应用中事件循环在 DLL 内部线程运行)
    Sleep(5000);

    // 8. 停止监控 COM3
    Stop(3);

    // 9. 释放所有资源
    FreeMonitor();
    printf("Resources released\n");

    // 10. 卸载 DLL
    FreeLibrary(hDll);
    return 0;
}

5.4 调用时序规则

InitMonitor → Monitor → [SetWrite_Data / SetRead_Data] → Stop → FreeMonitor
     │            │               │                        │          │
     │            │               │                        │          │
   必须最先     可多次调用     可随时调用               可多次调用  必须最后

规则

  1. InitMonitor 必须在所有其他函数之前调用
  2. Monitor / Stop / SetWrite_Data / SetRead_Data 要求先调用 InitMonitor
  3. FreeMonitor 后不可再调用任何函数(除非重新 InitMonitor
  4. Monitor 同一端口多次调用是安全的(幂等)
  5. FreeMonitor 不卸载驱动服务,仅释放 DLL 内部资源

6. 错误处理

6.1 错误码获取

每次导出函数调用后,通过 get_LastErrror() 获取最近一次操作的错误码:

if (!Monitor(1)) {
    int err = get_LastErrror();
    // 根据 err 处理错误
}

注意:get_LastErrror线程局部的,与 Win32 GetLastError() 语义一致。

6.2 错误码表

错误码 含义 建议处理
0 成功 无需处理
-1 未初始化 / 回调为空 先调用 InitMonitor
3 串口读取失败 检查端口是否已监控、驱动是否运行
4 串口写入失败 检查端口状态和数据有效性
-241 安装驱动失败(SCM 错误) 以管理员权限运行
-243 其他错误 查看日志获取详情
-244 驱动文件不存在或设备打开失败 检查 .sys 文件是否在 System32\drivers
-252 Key 无效 提供非空 Key
-253 启动驱动服务失败 检查驱动签名、测试签名是否启用
-254 需要管理员权限 以管理员权限启动进程
-255 绑定串口失败 检查端口是否存在、驱动过滤是否正常挂载

7. 日志与诊断

7.1 查看日志

DebugView(实时查看)

  1. 下载 DebugView
  2. 以管理员身份运行,开启 Capture Global Win32
  3. 过滤 CommModifyKit 相关日志

日志文件

DLL 可选输出日志到 %TEMP%\CommModifyKit.log,格式:

[2026-07-21 10:00:00][INF] DriverClient opened \\.\CommModifyKit
[2026-07-21 10:00:00][INF] EventLoop started
[2026-07-21 10:00:01][ERR] AttachPort failed for COM3

7.2 OutputDebugStringA 诊断点

MonitorManager 中关键流程节点输出 OutputDebugStringA 诊断信息,可通过 DebugView 实时追踪:

  • [MonitorManager::EnsureDriverReady] — 驱动就绪检查各步骤
  • [MonitorManager::InitMonitor] — 初始化各步骤

7.3 常见问题诊断

InitMonitor 返回 FALSE,错误码 -254

原因:进程未以管理员权限运行。

解决:右键程序 → "以管理员身份运行",或在 manifest 中请求 requireAdministrator

InitMonitor 返回 FALSE,错误码 -244

原因:驱动文件不存在或驱动服务未启动。

解决

  1. 确认 %SystemRoot%\System32\drivers\CommModifyKit.sys 存在
  2. 执行 sc.exe query CommModifyKit 检查服务状态
  3. 若服务未运行:sc.exe start CommModifyKit

InitMonitor 返回 FALSE,错误码 -253

原因:驱动服务启动失败,通常是签名问题。

解决

  1. 确认测试签名已启用:bcdedit /enum | findstr testsigning
  2. 若未启用:bcdedit /set testsigning on 并重启
  3. 生产环境需对驱动进行正式签名

Monitor 返回 FALSE,错误码 -255

原因:驱动中未找到该端口的过滤设备。

解决

  1. 确认该 COM 端口存在且未被占用
  2. 确认驱动已作为 Upper Filter 正确挂载到串口设备栈
  3. 检查设备管理器中串口属性 → "驱动程序" → "详细设置" → "上层筛选器"

回调不触发

原因:EventLoop 线程未运行或驱动未拦截到 IRP。

解决

  1. 确认 InitMonitor 返回 TRUE
  2. 确认 Monitor 返回 TRUE
  3. 用 DebugView 查看 EventLoop 日志
  4. 确认驱动过滤设备已 attach 到串口栈

8. 性能参数

参数 说明
单条事件最大数据 4096 字节 COMMKIT_MAX_DATA,超出截断
单次批量读取事件数 16 条 COMMKIT_EVENT_BATCH
每端口环形缓冲容量 256 条 COMMKIT_RING_CAPACITY,满了覆盖最旧
最大支持端口数 256 驱动端口表大小
事件循环错误重试间隔 50 ms EventLoop 非致命错误后 sleep

9. 安全说明

  • 驱动访问控制:控制设备使用 SDDL_DEVOBJ_SYS_ONLY_ADM,仅管理员和系统可访问
  • DLL 不直接暴露到网络:通过 CommModifyService 封装后提供服务
  • 回调数据只读lpData 指针指向内部缓冲,回调不应修改或释放
  • 驱动服务常驻FreeMonitor 不卸载驱动,避免服务被恶意替换
  • 测试签名:仅用于开发,生产环境必须使用正式 WHQL 签名

10. 版本兼容性

组件 接口版本 说明
DLL 导出函数 7 个 __stdcall 与原 CommModifyKit.dll 接口完全一致
IOCTL 码 设备类型 0x8000,功能 0x800-0x806 用户自定义设备类型
事件类型 0-4(OPEN/READ/WRITE/CLOSE) 与 CommWrapperBase.h::CommEventType 数值对齐
函数名拼写 get_LastErrror(3 个 r) 保留与原 DLL 一致的拼写