| 项目 | 要求 |
|---|---|
| 操作系统 | Windows 10/11 x64 |
| 编译器 | MSVC v142(Visual Studio 2019) |
| WDK | Windows Driver Kit 10(编译内核驱动) |
| 权限 | 安装驱动需要管理员权限 |
| 测试签名 | 开发阶段需启用 Windows 测试签名模式 |
CommModifyKit.slnbuild\Release\Win32\CommModifyKit.dllbuild\Release\x64\CommModifyKit.sys注意:驱动项目仅支持 x64 平台,Win32 配置会自动映射到 x64。
以管理员身份打开 PowerShell,执行:
cd CommModifyKit\installer
.\install.ps1
脚本自动完成:
%SystemRoot%\System32\drivers\安装后确认:
CommModifyKit.dll 已拷贝到 CommModifyService.exe 所在目录config.json 中 comm.use_dll 设为 true# 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
将 CommModifyKit.dll 拷贝到 CommModifyService.exe 所在目录即可。
# 检查驱动服务状态
sc.exe query CommModifyKit
# 检查测试签名
bcdedit /enum | Select-String "testsigning"
# 检查 DLL 是否在目标目录
dir CommModifyService\Release\CommModifyKit.dll
以管理员身份打开 PowerShell,执行:
cd CommModifyKit\installer
.\uninstall.ps1
脚本自动完成:
%SystemRoot%\System32\drivers\ 下的 .sys 和 .inf 文件pnputil 移除驱动包# 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
CommModifyKit.dll 导出 7 个 __stdcall 函数:
| 函数 | 用途 |
|---|---|
InitMonitor |
初始化监控(必须最先调用) |
Monitor |
开始监控指定串口 |
Stop |
停止监控指定串口 |
SetWrite_Data |
向串口写数据 |
SetRead_Data |
发起异步读请求 |
FreeMonitor |
释放所有资源 |
get_LastErrror |
获取最近错误码 |
// 事件回调类型
typedef LONG(CALLBACK* TOnData)(
int Sequence, // 递增序列号
double dTime, // OLE Automation date 时间戳
DWORD ComNumber, // 串口编号(COM n 的 n)
DWORD Irp, // 事件类型
DWORD dwSize, // 数据字节数
char* lpData // 数据指针(只读!)
);
| 值 | 常量 | 含义 |
|---|---|---|
| 1 | COMMKIT_OP_OPEN |
串口打开/开始监控 |
| 2 | COMMKIT_OP_READ |
读取到数据(dwSize > 0, lpData 有效) |
| 3 | COMMKIT_OP_WRITE |
写入数据(dwSize > 0, lpData 有效) |
| 4 | COMMKIT_OP_CLOSE |
串口关闭/停止监控 |
重要:回调中的
lpData指针指向 DLL 内部缓冲,仅在回调执行期间有效。如需保留数据,请在回调内拷贝,切勿存储指针。
#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;
}
InitMonitor → Monitor → [SetWrite_Data / SetRead_Data] → Stop → FreeMonitor
│ │ │ │ │
│ │ │ │ │
必须最先 可多次调用 可随时调用 可多次调用 必须最后
规则:
InitMonitor 必须在所有其他函数之前调用Monitor / Stop / SetWrite_Data / SetRead_Data 要求先调用 InitMonitorFreeMonitor 后不可再调用任何函数(除非重新 InitMonitor)Monitor 同一端口多次调用是安全的(幂等)FreeMonitor 不卸载驱动服务,仅释放 DLL 内部资源每次导出函数调用后,通过 get_LastErrror() 获取最近一次操作的错误码:
if (!Monitor(1)) {
int err = get_LastErrror();
// 根据 err 处理错误
}
注意:
get_LastErrror是线程局部的,与 Win32GetLastError()语义一致。
| 错误码 | 含义 | 建议处理 |
|---|---|---|
| 0 | 成功 | 无需处理 |
| -1 | 未初始化 / 回调为空 | 先调用 InitMonitor |
| 3 | 串口读取失败 | 检查端口是否已监控、驱动是否运行 |
| 4 | 串口写入失败 | 检查端口状态和数据有效性 |
| -241 | 安装驱动失败(SCM 错误) | 以管理员权限运行 |
| -243 | 其他错误 | 查看日志获取详情 |
| -244 | 驱动文件不存在或设备打开失败 | 检查 .sys 文件是否在 System32\drivers |
| -252 | Key 无效 | 提供非空 Key |
| -253 | 启动驱动服务失败 | 检查驱动签名、测试签名是否启用 |
| -254 | 需要管理员权限 | 以管理员权限启动进程 |
| -255 | 绑定串口失败 | 检查端口是否存在、驱动过滤是否正常挂载 |
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
MonitorManager 中关键流程节点输出 OutputDebugStringA 诊断信息,可通过 DebugView 实时追踪:
[MonitorManager::EnsureDriverReady] — 驱动就绪检查各步骤[MonitorManager::InitMonitor] — 初始化各步骤原因:进程未以管理员权限运行。
解决:右键程序 → "以管理员身份运行",或在 manifest 中请求 requireAdministrator。
原因:驱动文件不存在或驱动服务未启动。
解决:
%SystemRoot%\System32\drivers\CommModifyKit.sys 存在sc.exe query CommModifyKit 检查服务状态sc.exe start CommModifyKit原因:驱动服务启动失败,通常是签名问题。
解决:
bcdedit /enum | findstr testsigningbcdedit /set testsigning on 并重启原因:驱动中未找到该端口的过滤设备。
解决:
原因:EventLoop 线程未运行或驱动未拦截到 IRP。
解决:
InitMonitor 返回 TRUEMonitor 返回 TRUE| 参数 | 值 | 说明 |
|---|---|---|
| 单条事件最大数据 | 4096 字节 | COMMKIT_MAX_DATA,超出截断 |
| 单次批量读取事件数 | 16 条 | COMMKIT_EVENT_BATCH |
| 每端口环形缓冲容量 | 256 条 | COMMKIT_RING_CAPACITY,满了覆盖最旧 |
| 最大支持端口数 | 256 | 驱动端口表大小 |
| 事件循环错误重试间隔 | 50 ms | EventLoop 非致命错误后 sleep |
SDDL_DEVOBJ_SYS_ONLY_ADM,仅管理员和系统可访问lpData 指针指向内部缓冲,回调不应修改或释放FreeMonitor 不卸载驱动,避免服务被恶意替换| 组件 | 接口版本 | 说明 |
|---|---|---|
| DLL 导出函数 | 7 个 __stdcall | 与原 CommModifyKit.dll 接口完全一致 |
| IOCTL 码 | 设备类型 0x8000,功能 0x800-0x806 | 用户自定义设备类型 |
| 事件类型 | 0-4(OPEN/READ/WRITE/CLOSE) | 与 CommWrapperBase.h::CommEventType 数值对齐 |
| 函数名拼写 | get_LastErrror(3 个 r) |
保留与原 DLL 一致的拼写 |