Browse Source

完成第一版

51zj 3 tuần trước cách đây
commit
08d795668a

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/build
+/.vs

+ 38 - 0
CommModifyKit.sln

@@ -0,0 +1,38 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31624.102
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommModifyKit", "dll\CommModifyKit.vcxproj", "{A1B2C3D4-0001-0001-0001-000000000001}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommModifyKitDriver", "driver\CommModifyKitDriver.vcxproj", "{A1B2C3D4-0001-0001-0001-000000000002}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{A1B2C3D4-0001-0001-0001-000000000001}.Debug|Win32.ActiveCfg = Debug|Win32
+		{A1B2C3D4-0001-0001-0001-000000000001}.Debug|Win32.Build.0 = Debug|Win32
+		{A1B2C3D4-0001-0001-0001-000000000001}.Debug|x64.ActiveCfg = Debug|x64
+		{A1B2C3D4-0001-0001-0001-000000000001}.Debug|x64.Build.0 = Debug|x64
+		{A1B2C3D4-0001-0001-0001-000000000001}.Release|Win32.ActiveCfg = Release|Win32
+		{A1B2C3D4-0001-0001-0001-000000000001}.Release|Win32.Build.0 = Release|Win32
+		{A1B2C3D4-0001-0001-0001-000000000001}.Release|x64.ActiveCfg = Release|x64
+		{A1B2C3D4-0001-0001-0001-000000000001}.Release|x64.Build.0 = Release|x64
+		{A1B2C3D4-0001-0001-0001-000000000002}.Debug|x64.ActiveCfg = Debug|x64
+		{A1B2C3D4-0001-0001-0001-000000000002}.Debug|x64.Build.0 = Debug|x64
+		{A1B2C3D4-0001-0001-0001-000000000002}.Release|x64.ActiveCfg = Release|x64
+		{A1B2C3D4-0001-0001-0001-000000000002}.Release|x64.Build.0 = Release|x64
+		{A1B2C3D4-0001-0001-0001-000000000002}.Debug|Win32.ActiveCfg = Debug|x64
+		{A1B2C3D4-0001-0001-0001-000000000002}.Release|Win32.ActiveCfg = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {B1B2C3D4-0000-0000-0000-000000000000}
+	EndGlobalSection
+EndGlobal

+ 10 - 0
common/CommKitEvents.h

@@ -0,0 +1,10 @@
+// CommKitEvents.h - 事件类型定义
+// 与 CommModifyService/CommWrapperBase.h::CommEventType 数值对齐
+// 驱动上抛事件时使用这些常量,DLL 转发给上层 TOnData 的 Irp 参数
+#pragma once
+
+#define COMMKIT_OP_NONE   0   // 无事件
+#define COMMKIT_OP_OPEN   1   // 串口打开/开始监控
+#define COMMKIT_OP_READ   2   // 读取到数据
+#define COMMKIT_OP_WRITE  3   // 写入数据
+#define COMMKIT_OP_CLOSE  4   // 串口关闭/停止监控

+ 81 - 0
common/CommKitIoctl.h

@@ -0,0 +1,81 @@
+// CommKitIoctl.h - 用户态/内核态共享的 IOCTL 码与数据结构定义
+// 被 dll/ 与 driver/ 共同包含,确保双方契约一致
+#pragma once
+
+#include <winioctl.h>
+
+// ============================================================================
+// 用户态 CreateFile 使用的设备名(\\.\CommModifyKit)
+// ============================================================================
+#define COMMKIT_USER_DEVICE_NAME   L"\\\\.\\CommModifyKit"
+#define COMMKIT_DEVICE_DOS_NAME    L"\\DosDevices\\CommModifyKit"
+#define COMMKIT_DEVICE_NAME        L"\\Device\\CommModifyKit"
+
+// ============================================================================
+// IOCTL 设备类型与控制码
+// 设备类型 0x8000 表示用户自定义设备类型
+// ============================================================================
+#define COMMKIT_DEVICE_TYPE        0x8000
+
+// InitMonitor 调用:注册用户态回调管道(DLL 启动事件循环线程)
+#define IOCTL_COMMKIT_REGISTER_CALLBACK \
+    CTL_CODE(COMMKIT_DEVICE_TYPE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+// Monitor 调用:绑定(启用监控)指定串口编号
+#define IOCTL_COMMKIT_ATTACH_PORT \
+    CTL_CODE(COMMKIT_DEVICE_TYPE, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+// Stop 调用:解绑(停止监控)指定串口编号
+#define IOCTL_COMMKIT_DETACH_PORT \
+    CTL_CODE(COMMKIT_DEVICE_TYPE, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+// SetWrite_Data 调用:向指定串口下发写 IRP(数据由驱动透传给下层串口)
+#define IOCTL_COMMKIT_WRITE_PORT \
+    CTL_CODE(COMMKIT_DEVICE_TYPE, 0x803, METHOD_IN_DIRECT, FILE_WRITE_DATA)
+
+// SetRead_Data 调用:从指定串口发起异步读请求(结果通过回调 OP_READ 上抛)
+#define IOCTL_COMMKIT_READ_PORT \
+    CTL_CODE(COMMKIT_DEVICE_TYPE, 0x804, METHOD_IN_DIRECT, FILE_READ_DATA)
+
+// DLL 工作线程调用:从驱动批量读取捕获的事件(阻塞或返回 0 条)
+#define IOCTL_COMMKIT_READ_EVENTS \
+    CTL_CODE(COMMKIT_DEVICE_TYPE, 0x805, METHOD_OUT_DIRECT, FILE_READ_DATA)
+
+// FreeMonitor 调用:解绑所有端口、清空事件队列(不卸载驱动服务)
+#define IOCTL_COMMKIT_FREE_ALL \
+    CTL_CODE(COMMKIT_DEVICE_TYPE, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+// ============================================================================
+// 单条事件结构(内核态 → 用户态)
+// 注意:字段对齐使用 1 字节 pack,便于跨位数(WOW64)传递
+// ============================================================================
+#define COMMKIT_MAX_DATA          4096   // 单条事件最大数据字节数
+#define COMMKIT_EVENT_BATCH       16     // 单次 IOCTL 最多返回事件数
+#define COMMKIT_RING_CAPACITY     256    // 内核态每端口环形缓冲容量
+
+#pragma pack(push, 1)
+typedef struct _COMMKIT_EVENT {
+    INT32  Sequence;                     // 递增序列号(每次回调递增)
+    double TimeStamp;                    // OLE Automation date(0=用当前时间)
+    UINT32 ComNumber;                    // 串口编号(COM n 的 n)
+    UINT32 EventType;                    // 事件类型(见 CommKitEvents.h)
+    UINT32 DataSize;                     // 有效数据字节数(<= COMMKIT_MAX_DATA)
+    CHAR   Data[COMMKIT_MAX_DATA];       // 内联数据缓冲
+} COMMKIT_EVENT, *PCOMMKIT_EVENT;
+#pragma pack(pop)
+
+// IOCTL_COMMKIT_ATTACH_PORT / DETACH_PORT 输入参数
+#pragma pack(push, 1)
+typedef struct _COMMKIT_PORT_REQUEST {
+    UINT32 ComNumber;                    // 串口编号
+} COMMKIT_PORT_REQUEST, *PCOMMKIT_PORT_REQUEST;
+#pragma pack(pop)
+
+// IOCTL_COMMKIT_WRITE_PORT / READ_PORT 输入参数(数据通过 METHOD_IN_DIRECT 的 pInputBuffer 传递)
+#pragma pack(push, 1)
+typedef struct _COMMKIT_DATA_REQUEST {
+    UINT32 ComNumber;                    // 串口编号
+    UINT32 DataLen;                      // 数据长度(字节数)
+    CHAR   Data[1];                      // 变长数据,实际长度由 DataLen 决定
+} COMMKIT_DATA_REQUEST, *PCOMMKIT_DATA_REQUEST;
+#pragma pack(pop)

+ 15 - 0
dll/CommModifyKit.def

@@ -0,0 +1,15 @@
+; CommModifyKit.def - DLL 模块定义文件
+; 项目名 CommModifyKit,输出文件名 CommModifyKit.dll(统一命名)
+;
+; 关键:__stdcall 通常被 MSVC 修饰为 _Func@N
+; 使用 EXPORTS 段 + extern "C" 才能得到无修饰的导出名
+; 这样 CommModifyService 的 GetProcAddress(...,"InitMonitor") 等可正确解析
+LIBRARY CommModifyKit
+EXPORTS
+    InitMonitor       @1
+    Monitor           @2
+    Stop              @3
+    SetWrite_Data     @4
+    SetRead_Data      @5
+    FreeMonitor       @6
+    get_LastErrror    @7   ; 注意 3 个 r,与原拼写一致

+ 158 - 0
dll/CommModifyKit.vcxproj

@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <Keyword>Win32Proj</Keyword>
+    <ProjectGuid>{A1B2C3D4-0001-0001-0001-000000000001}</ProjectGuid>
+    <RootNamespace>CommModifyKit</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+
+  <!-- Debug|Win32 -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <!-- Release|Win32 -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <!-- Debug|x64 -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <!-- Release|x64 -->
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="Shared" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
+            Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
+            Label="LocalAppDataPlatform" />
+  </ImportGroup>
+
+  <!-- 通用属性 -->
+  <!-- 项目名 CommModifyKit,输出文件名 CommModifyKit.dll(统一命名) -->
+  <PropertyGroup>
+    <OutDir>$(SolutionDir)build\$(Configuration)\$(Platform)\</OutDir>
+    <IntDir>$(SolutionDir)build\obj\CommModifyKit\$(Configuration)\$(Platform)\</IntDir>
+    <TargetName>CommModifyKit</TargetName>
+  </PropertyGroup>
+
+  <!-- 通用编译选项 -->
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
+      <AdditionalIncludeDirectories>..\common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>CommModifyKit_EXPORTS;_WINDOWS;_USRDLL;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <LanguageStandard>stdcpp14</LanguageStandard>
+      <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+      <!-- 关键:使用 .def 文件作为模块定义,避免 __stdcall 名字修饰 -->
+      <ModuleDefinitionFile>CommModifyKit.def</ModuleDefinitionFile>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <!-- Debug 特定 -->
+  <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </ItemDefinitionGroup>
+
+  <!-- Release 特定 -->
+  <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <!-- 源文件 -->
+  <ItemGroup>
+    <ClCompile Include="pch.cpp">
+      <PrecompiledHeader>Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="dllmain.cpp" />
+    <ClCompile Include="exports.cpp" />
+    <ClCompile Include="Logger.cpp" />
+    <ClCompile Include="ErrorStore.cpp" />
+    <ClCompile Include="DriverClient.cpp" />
+    <ClCompile Include="EventLoop.cpp" />
+    <ClCompile Include="MonitorManager.cpp" />
+  </ItemGroup>
+
+  <!-- 头文件 -->
+  <ItemGroup>
+    <ClInclude Include="pch.h" />
+    <ClInclude Include="Logger.h" />
+    <ClInclude Include="ErrorStore.h" />
+    <ClInclude Include="DriverClient.h" />
+    <ClInclude Include="EventLoop.h" />
+    <ClInclude Include="MonitorManager.h" />
+    <ClInclude Include="..\common\CommKitIoctl.h" />
+    <ClInclude Include="..\common\CommKitEvents.h" />
+  </ItemGroup>
+
+  <!-- 模块定义文件 -->
+  <ItemGroup>
+    <None Include="CommModifyKit.def" />
+  </ItemGroup>
+
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets" />
+</Project>

+ 76 - 0
dll/CommModifyKit.vcxproj.filters

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="头文件">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx</Extensions>
+    </Filter>
+    <Filter Include="源文件">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx</Extensions>
+    </Filter>
+    <Filter Include="公共">
+      <UniqueIdentifier>{a1b2c3d4-0002-0002-0002-000000000001}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ClInclude Include="pch.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="Logger.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="ErrorStore.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="DriverClient.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="EventLoop.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="MonitorManager.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="..\common\CommKitIoctl.h">
+      <Filter>公共</Filter>
+    </ClInclude>
+    <ClInclude Include="..\common\CommKitEvents.h">
+      <Filter>公共</Filter>
+    </ClInclude>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ClCompile Include="pch.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="dllmain.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="exports.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="Logger.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="ErrorStore.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="DriverClient.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="EventLoop.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="MonitorManager.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Include="CommModifyKit.def">
+      <Filter>源文件</Filter>
+    </None>
+  </ItemGroup>
+</Project>

+ 4 - 0
dll/CommModifyKit.vcxproj.user

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>

+ 165 - 0
dll/DriverClient.cpp

@@ -0,0 +1,165 @@
+// DriverClient.cpp - 驱动通信封装实现
+#include "pch.h"
+#include "DriverClient.h"
+#include "Logger.h"
+
+namespace commkit {
+
+DriverClient::DriverClient() {}
+
+DriverClient::~DriverClient() {
+    Close();
+}
+
+bool DriverClient::Open() {
+    if (IsOpen()) {
+        return true;
+    }
+
+    // 打开驱动控制设备 \\.\CommModifyKit
+    handle_ = CreateFileW(
+        COMMKIT_USER_DEVICE_NAME,
+        GENERIC_READ | GENERIC_WRITE,
+        0,
+        nullptr,
+        OPEN_EXISTING,
+        FILE_ATTRIBUTE_NORMAL,
+        nullptr);
+
+    if (handle_ == INVALID_HANDLE_VALUE) {
+        DWORD err = ::GetLastError();
+        LOG_ERROR("DriverClient::Open failed, Win32 error=" + std::to_string(err));
+        return false;
+    }
+
+    LOG_INFO("DriverClient opened \\\\.\\CommModifyKit");
+    return true;
+}
+
+void DriverClient::Close() {
+    if (handle_ != INVALID_HANDLE_VALUE) {
+        CloseHandle(handle_);
+        handle_ = INVALID_HANDLE_VALUE;
+        LOG_INFO("DriverClient closed");
+    }
+}
+
+bool DriverClient::SyncIoctl(DWORD ioctl_code, void* in_buf, DWORD in_size,
+                              void* out_buf, DWORD out_size, DWORD* returned) {
+    if (!IsOpen()) {
+        LOG_ERROR("SyncIoctl: driver not open");
+        return false;
+    }
+
+    DWORD bytes_returned = 0;
+    BOOL ok = DeviceIoControl(
+        handle_,
+        ioctl_code,
+        in_buf, in_size,
+        out_buf, out_size,
+        &bytes_returned,
+        nullptr);
+
+    if (!ok) {
+        DWORD err = ::GetLastError();
+        LOG_ERROR("SyncIoctl 0x" + std::to_string(ioctl_code) +
+                  " failed, Win32 error=" + std::to_string(err));
+        return false;
+    }
+
+    if (returned) {
+        *returned = bytes_returned;
+    }
+    return true;
+}
+
+bool DriverClient::RegisterCallback() {
+    // 注册回调管道:无输入输出参数,仅通知驱动开始工作
+    return SyncIoctl(IOCTL_COMMKIT_REGISTER_CALLBACK, nullptr, 0, nullptr, 0, nullptr);
+}
+
+bool DriverClient::AttachPort(DWORD com_number) {
+    COMMKIT_PORT_REQUEST req;
+    req.ComNumber = com_number;
+    return SyncIoctl(IOCTL_COMMKIT_ATTACH_PORT, &req, sizeof(req), nullptr, 0, nullptr);
+}
+
+bool DriverClient::DetachPort(DWORD com_number) {
+    COMMKIT_PORT_REQUEST req;
+    req.ComNumber = com_number;
+    return SyncIoctl(IOCTL_COMMKIT_DETACH_PORT, &req, sizeof(req), nullptr, 0, nullptr);
+}
+
+bool DriverClient::WritePort(DWORD com_number, const void* data, int len) {
+    if (len < 0 || (!data && len > 0)) {
+        return false;
+    }
+
+    // METHOD_IN_DIRECT:输入缓冲为 COMMKIT_DATA_REQUEST + 数据
+    // 总长度 = 固定头部 + 数据长度(Data 字段为 1,已含 1 字节)
+    DWORD total_size = sizeof(COMMKIT_DATA_REQUEST) - 1 + static_cast<DWORD>(len);
+    std::vector<BYTE> buf(total_size);
+
+    auto* req = reinterpret_cast<COMMKIT_DATA_REQUEST*>(buf.data());
+    req->ComNumber = com_number;
+    req->DataLen = static_cast<UINT32>(len);
+    if (len > 0) {
+        memcpy(req->Data, data, len);
+    }
+
+    return SyncIoctl(IOCTL_COMMKIT_WRITE_PORT, buf.data(), total_size, nullptr, 0, nullptr);
+}
+
+bool DriverClient::ReadPort(DWORD com_number, const void* data, int len) {
+    if (len < 0) {
+        return false;
+    }
+
+    // METHOD_IN_DIRECT:发起读请求,data 仅作为附加参数(当前实现忽略)
+    DWORD total_size = sizeof(COMMKIT_DATA_REQUEST) - 1 + static_cast<DWORD>(len);
+    std::vector<BYTE> buf(total_size);
+
+    auto* req = reinterpret_cast<COMMKIT_DATA_REQUEST*>(buf.data());
+    req->ComNumber = com_number;
+    req->DataLen = static_cast<UINT32>(len);
+    if (len > 0 && data) {
+        memcpy(req->Data, data, len);
+    }
+
+    return SyncIoctl(IOCTL_COMMKIT_READ_PORT, buf.data(), total_size, nullptr, 0, nullptr);
+}
+
+int DriverClient::ReadEvents(COMMKIT_EVENT* events_out, int max_count) {
+    if (!IsOpen() || !events_out || max_count <= 0) {
+        return -1;
+    }
+
+    DWORD out_size = static_cast<DWORD>(sizeof(COMMKIT_EVENT) * max_count);
+    DWORD returned = 0;
+
+    BOOL ok = DeviceIoControl(
+        handle_,
+        IOCTL_COMMKIT_READ_EVENTS,
+        nullptr, 0,
+        events_out, out_size,
+        &returned,
+        nullptr);
+
+    if (!ok) {
+        DWORD err = ::GetLastError();
+        // ERROR_NO_DATA 等可视为"暂无事件",返回 0
+        if (err == ERROR_NO_DATA) {
+            return 0;
+        }
+        LOG_ERROR("ReadEvents failed, Win32 error=" + std::to_string(err));
+        return -1;
+    }
+
+    return static_cast<int>(returned / sizeof(COMMKIT_EVENT));
+}
+
+bool DriverClient::FreeAll() {
+    return SyncIoctl(IOCTL_COMMKIT_FREE_ALL, nullptr, 0, nullptr, 0, nullptr);
+}
+
+} // namespace commkit

+ 65 - 0
dll/DriverClient.h

@@ -0,0 +1,65 @@
+// DriverClient.h - 与内核驱动的通信封装
+// 职责:CreateFile 打开 \\.\CommModifyKit 设备 + 封装 DeviceIoControl 调用
+#pragma once
+
+#include "pch.h"
+#include "../common/CommKitIoctl.h"
+
+namespace commkit {
+
+class DriverClient {
+public:
+    DriverClient();
+    ~DriverClient();
+
+    DriverClient(const DriverClient&) = delete;
+    DriverClient& operator=(const DriverClient&) = delete;
+
+    // 打开驱动控制设备 \\.\CommModifyKit
+    // 返回 true 表示成功;失败时通过 GetLastError() 查询 Win32 错误码
+    bool Open();
+
+    // 关闭驱动设备句柄
+    void Close();
+
+    // 是否已打开
+    bool IsOpen() const { return handle_ != INVALID_HANDLE_VALUE; }
+
+    // 获取驱动设备句柄(供 EventLoop 复用)
+    HANDLE GetHandle() const { return handle_; }
+
+    // ====== IOCTL 封装 ======
+
+    // 注册回调管道(InitMonitor 调用)
+    // 告诉驱动开始工作,DLL 工作线程会从 READ_EVENTS 拉事件
+    bool RegisterCallback();
+
+    // 绑定(启用监控)指定串口
+    bool AttachPort(DWORD com_number);
+
+    // 解绑(停止监控)指定串口
+    bool DetachPort(DWORD com_number);
+
+    // 向指定串口写数据
+    bool WritePort(DWORD com_number, const void* data, int len);
+
+    // 从指定串口发起异步读请求
+    bool ReadPort(DWORD com_number, const void* data, int len);
+
+    // 批量读取捕获的事件(阻塞或返回 0 条)
+    // events_out:输出缓冲区;max_count:最多多少条
+    // 返回实际读取到的条数,<0 表示错误
+    int ReadEvents(COMMKIT_EVENT* events_out, int max_count);
+
+    // 解绑所有端口并清空事件队列(FreeMonitor 调用)
+    bool FreeAll();
+
+private:
+    // 通用同步 IOCTL 调用封装
+    bool SyncIoctl(DWORD ioctl_code, void* in_buf, DWORD in_size,
+                   void* out_buf, DWORD out_size, DWORD* returned);
+
+    HANDLE handle_ = INVALID_HANDLE_VALUE;
+};
+
+} // namespace commkit

+ 23 - 0
dll/ErrorStore.cpp

@@ -0,0 +1,23 @@
+// ErrorStore.cpp - 线程局部错误码存储实现
+#include "pch.h"
+#include "ErrorStore.h"
+
+namespace commkit {
+
+// thread_local 保证每线程独立存储
+// 默认值 0 表示"成功",与 Win32 错误码 0=ERROR_SUCCESS 语义一致
+static thread_local int g_last_error = 0;
+
+void ErrorStore::Set(int code) {
+    g_last_error = code;
+}
+
+int ErrorStore::Get() {
+    return g_last_error;
+}
+
+void ErrorStore::Reset() {
+    g_last_error = 0;
+}
+
+} // namespace commkit

+ 24 - 0
dll/ErrorStore.h

@@ -0,0 +1,24 @@
+// ErrorStore.h - 线程局部错误码存储
+// 与 Win32 GetLastError() 语义一致:每线程独立,最近一次错误
+#pragma once
+
+#include "pch.h"
+
+namespace commkit {
+
+class ErrorStore {
+public:
+    // 设置当前线程错误码
+    static void Set(int code);
+
+    // 获取当前线程错误码(未设置过返回 0)
+    static int Get();
+
+    // 清空当前线程错误码
+    static void Reset();
+
+private:
+    ErrorStore() = delete;
+};
+
+} // namespace commkit

+ 104 - 0
dll/EventLoop.cpp

@@ -0,0 +1,104 @@
+// EventLoop.cpp - 事件循环实现
+#include "pch.h"
+#include "EventLoop.h"
+#include "Logger.h"
+
+namespace commkit {
+
+EventLoop::EventLoop() {}
+
+EventLoop::~EventLoop() {
+    Stop();
+}
+
+bool EventLoop::Start(HANDLE driver_handle, TOnData callback) {
+    if (running_.load()) {
+        return true;
+    }
+    if (driver_handle == INVALID_HANDLE_VALUE || !callback) {
+        LOG_ERROR("EventLoop::Start invalid args");
+        return false;
+    }
+
+    driver_handle_ = driver_handle;
+    callback_ = callback;
+    running_.store(true);
+
+    try {
+        thread_ = std::thread(&EventLoop::Run, this);
+    } catch (const std::exception& e) {
+        LOG_ERROR(std::string("EventLoop thread create failed: ") + e.what());
+        running_.store(false);
+        return false;
+    }
+
+    LOG_INFO("EventLoop started");
+    return true;
+}
+
+void EventLoop::Stop() {
+    if (!running_.exchange(false)) {
+        return;
+    }
+
+    // 关闭驱动句柄会阻塞的 ReadEvents 立即返回错误
+    // 此处仅设置标志,由 Run() 循环自行退出
+    // 注意:不能在此处 CloseHandle(driver_handle_),因为 Run() 可能正在使用
+    if (thread_.joinable()) {
+        thread_.join();
+    }
+
+    LOG_INFO("EventLoop stopped");
+}
+
+void EventLoop::Run() {
+    // 批量读取缓冲区:单次最多 COMMKIT_EVENT_BATCH 条事件
+    COMMKIT_EVENT batch[COMMKIT_EVENT_BATCH];
+
+    while (running_.load()) {
+        DWORD returned = 0;
+
+        // 阻塞式 IOCTL:驱动在无事件时会让 IRP pending
+        // 当驱动有事件或被取消时返回
+        BOOL ok = DeviceIoControl(
+            driver_handle_,
+            IOCTL_COMMKIT_READ_EVENTS,
+            nullptr, 0,
+            batch, sizeof(batch),
+            &returned,
+            nullptr);
+
+        if (!running_.load()) {
+            break;
+        }
+
+        if (!ok) {
+            DWORD err = ::GetLastError();
+            // 驱动关闭或句柄无效时退出循环
+            if (err == ERROR_INVALID_HANDLE || err == ERROR_OPERATION_ABORTED) {
+                LOG_WARNING("EventLoop Run: driver handle closed, exiting");
+                break;
+            }
+            // 其他错误:短暂 sleep 后重试,避免空转
+            LOG_ERROR("EventLoop Run: DeviceIoControl failed, error=" + std::to_string(err));
+            std::this_thread::sleep_for(std::chrono::milliseconds(50));
+            continue;
+        }
+
+        // 处理返回的事件
+        DWORD count = returned / sizeof(COMMKIT_EVENT);
+        for (DWORD i = 0; i < count; ++i) {
+            const COMMKIT_EVENT& e = batch[i];
+            if (callback_) {
+                // 调用用户注册的回调
+                // 数据指针:const_cast 转换以匹配 TOnData 签名
+                // 注意:回调应只读 lpData,不应释放
+                callback_(e.Sequence, e.TimeStamp, e.ComNumber,
+                          e.EventType, e.DataSize,
+                          const_cast<char*>(e.Data));
+            }
+        }
+    }
+}
+
+} // namespace commkit

+ 43 - 0
dll/EventLoop.h

@@ -0,0 +1,43 @@
+// EventLoop.h - 用户态事件循环
+// 职责:独立工作线程,循环从驱动批量读取事件 → 调用 TOnData 回调
+#pragma once
+
+#include "pch.h"
+#include "../common/CommKitIoctl.h"
+
+namespace commkit {
+
+// TOnData 类型定义,与 DllWrapper.h 中的 TOnData 一致
+// LONG CALLBACK = LONG __stdcall
+typedef LONG(CALLBACK* TOnData)(int Sequence, double dTime,
+                                  DWORD ComNumber, DWORD Irp,
+                                  DWORD dwSize, char* lpData);
+
+class EventLoop {
+public:
+    EventLoop();
+    ~EventLoop();
+
+    EventLoop(const EventLoop&) = delete;
+    EventLoop& operator=(const EventLoop&) = delete;
+
+    // 启动事件循环(绑定驱动句柄与回调)
+    bool Start(HANDLE driver_handle, TOnData callback);
+
+    // 停止事件循环并等待线程退出
+    void Stop();
+
+    // 是否在运行
+    bool IsRunning() const { return running_.load(); }
+
+private:
+    // 工作线程主循环
+    void Run();
+
+    HANDLE             driver_handle_ = INVALID_HANDLE_VALUE;
+    TOnData            callback_ = nullptr;
+    std::atomic<bool>  running_{false};
+    std::thread        thread_;
+};
+
+} // namespace commkit

+ 105 - 0
dll/Logger.cpp

@@ -0,0 +1,105 @@
+// Logger.cpp - 日志实现
+#include "pch.h"
+#include "Logger.h"
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+
+namespace commkit {
+
+Logger::Logger() {}
+Logger::~Logger() {}
+
+Logger& Logger::Instance() {
+    static Logger instance;
+    return instance;
+}
+
+void Logger::SetLevel(Level level) {
+    level_ = level;
+}
+
+void Logger::EnableFileOutput(bool enable) {
+    std::lock_guard<std::mutex> lock(file_mutex_);
+    file_output_enabled_ = enable;
+}
+
+std::wstring Logger::StringToWString(const std::string& str) const {
+    if (str.empty()) return L"";
+    int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
+    if (len <= 0) return L"";
+    std::wstring wstr(len - 1, L'\0');
+    MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wstr[0], len);
+    return wstr;
+}
+
+std::wstring Logger::FormatLine(Level level, const std::string& message) {
+    // 获取当前本地时间
+    auto now = std::time(nullptr);
+    struct tm tm_buf;
+    localtime_s(&tm_buf, &now);
+
+    // 格式化时间戳
+    std::wostringstream woss;
+    woss << L"[" << std::put_time(&tm_buf, L"%Y-%m-%d %H:%M:%S") << L"]";
+
+    // 级别标记
+    const wchar_t* level_str = L"?";
+    switch (level) {
+        case Level::Debug:   level_str = L"DBG"; break;
+        case Level::Info:    level_str = L"INF"; break;
+        case Level::Warning: level_str = L"WRN"; break;
+        case Level::Error:   level_str = L"ERR"; break;
+    }
+    woss << L"[" << level_str << L"] ";
+
+    // 消息体
+    woss << StringToWString(message);
+
+    return woss.str();
+}
+
+void Logger::WriteToFile(const std::wstring& line) {
+    // 日志文件路径:%TEMP%\CommModifyKit.log
+    wchar_t temp_path[MAX_PATH] = {0};
+    if (GetTempPathW(MAX_PATH, temp_path) == 0) return;
+
+    std::wstring file_path = std::wstring(temp_path) + L"CommModifyKit.log";
+
+    // 追加模式打开
+    HANDLE hFile = CreateFileW(file_path.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ,
+                               nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+    if (hFile == INVALID_HANDLE_VALUE) return;
+
+    // 写入一行 + 换行
+    std::wstring line_with_eol = line + L"\r\n";
+    DWORD written = 0;
+    WriteFile(hFile, line_with_eol.c_str(),
+              static_cast<DWORD>(line_with_eol.size() * sizeof(wchar_t)),
+              &written, nullptr);
+    CloseHandle(hFile);
+}
+
+void Logger::Log(Level level, const std::string& message) {
+    // 级别过滤
+    if (static_cast<int>(level) < static_cast<int>(level_)) {
+        return;
+    }
+
+    // 格式化日志行
+    std::wstring line = FormatLine(level, message);
+
+    // 输出到调试器(DebugView 等)
+    OutputDebugStringW(line.c_str());
+    OutputDebugStringW(L"\r\n");
+
+    // 可选:写入文件
+    {
+        std::lock_guard<std::mutex> lock(file_mutex_);
+        if (file_output_enabled_) {
+            WriteToFile(line);
+        }
+    }
+}
+
+} // namespace commkit

+ 63 - 0
dll/Logger.h

@@ -0,0 +1,63 @@
+// Logger.h - DLL 内部简易日志
+// 实现:OutputDebugStringW + 可选文件输出(%TEMP%\CommModifyKit.log)
+// 不依赖 CommModifyService 的 Logger(独立项目原则)
+#pragma once
+
+#include "pch.h"
+
+namespace commkit {
+
+class Logger {
+public:
+    enum class Level {
+        Debug = 0,
+        Info,
+        Warning,
+        Error
+    };
+
+    // 单例访问
+    static Logger& Instance();
+
+    // 设置日志级别(低于此级别的不输出)
+    void SetLevel(Level level);
+
+    // 启用/禁用文件输出
+    void EnableFileOutput(bool enable);
+
+    // 日志接口
+    void Log(Level level, const std::string& message);
+
+    // 便捷方法
+    void Debug(const std::string& msg)   { Log(Level::Debug, msg); }
+    void Info(const std::string& msg)    { Log(Level::Info, msg); }
+    void Warning(const std::string& msg) { Log(Level::Warning, msg); }
+    void Error(const std::string& msg)   { Log(Level::Error, msg); }
+
+private:
+    Logger();
+    ~Logger();
+    Logger(const Logger&) = delete;
+    Logger& operator=(const Logger&) = delete;
+
+    // 将日志写入文件(追加模式)
+    void WriteToFile(const std::wstring& line);
+
+    // 将字节串转宽字符串(用于 OutputDebugStringW)
+    std::wstring StringToWString(const std::string& str) const;
+
+    // 格式化日志行:[时间][级别] 消息
+    std::wstring FormatLine(Level level, const std::string& message);
+
+    Level        level_ = Level::Info;
+    bool         file_output_enabled_ = false;
+    std::mutex   file_mutex_;
+};
+
+} // namespace commkit
+
+// 全局便捷日志宏(避免每次写 Logger::Instance().XXX)
+#define LOG_DEBUG(msg)   commkit::Logger::Instance().Debug(msg)
+#define LOG_INFO(msg)    commkit::Logger::Instance().Info(msg)
+#define LOG_WARNING(msg) commkit::Logger::Instance().Warning(msg)
+#define LOG_ERROR(msg)   commkit::Logger::Instance().Error(msg)

+ 326 - 0
dll/MonitorManager.cpp

@@ -0,0 +1,326 @@
+// MonitorManager.cpp - 管理器实现
+#include "pch.h"
+#include "MonitorManager.h"
+#include "Logger.h"
+#include "ErrorStore.h"
+#include <shellapi.h>
+
+namespace commkit {
+
+// 驱动服务名(与 INF 中匹配)
+static const wchar_t* kDriverServiceName = L"CommModifyKit";
+// 驱动文件路径(相对 %SystemRoot%\System32\drivers\)
+static const wchar_t* kDriverBinaryPath = L"System32\\drivers\\CommModifyKit.sys";
+
+MonitorManager::MonitorManager() {}
+
+MonitorManager::~MonitorManager() {
+    FreeAll();
+}
+
+MonitorManager& GetMonitorManager() {
+    static MonitorManager instance;
+    return instance;
+}
+
+bool MonitorManager::IsElevated() const {
+    // 通过检查进程 token 是否在管理员组来判断
+    BOOL is_admin = FALSE;
+    HANDLE token = nullptr;
+    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
+        TOKEN_ELEVATION elevation;
+        DWORD size = sizeof(elevation);
+        if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size)) {
+            is_admin = elevation.TokenIsElevated;
+        }
+        CloseHandle(token);
+    }
+    return is_admin != FALSE;
+}
+
+void MonitorManager::SetLastError(int code) const {
+    ErrorStore::Set(code);
+}
+
+bool MonitorManager::EnsureDriverReady() {
+    OutputDebugStringA("[MonitorManager::EnsureDriverReady] enter");
+
+    // 1. 检查管理员权限(首次安装需管理员,对应错误码 -254)
+    if (!IsElevated()) {
+        LOG_ERROR("Process not elevated; first-time install requires admin");
+        SetLastError(-254);
+        OutputDebugStringA("[MonitorManager::EnsureDriverReady] not elevated");
+        return false;
+    }
+    OutputDebugStringA("[MonitorManager::EnsureDriverReady] elevated check passed");
+
+    // 2. 检查驱动服务是否已存在并运行
+    OutputDebugStringA("[MonitorManager::EnsureDriverReady] calling OpenSCManager...");
+    SC_HANDLE scm = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
+    if (!scm) {
+        DWORD err = ::GetLastError();
+        LOG_ERROR("OpenSCManager failed, error=" + std::to_string(err));
+        SetLastError(-241);  // 安装驱动失败
+        OutputDebugStringA("[MonitorManager::EnsureDriverReady] OpenSCManager FAILED");
+        return false;
+    }
+    OutputDebugStringA("[MonitorManager::EnsureDriverReady] OpenSCManager succeeded");
+
+    OutputDebugStringA("[MonitorManager::EnsureDriverReady] calling OpenService...");
+    SC_HANDLE svc = OpenServiceW(scm, kDriverServiceName, SERVICE_ALL_ACCESS);
+    if (!svc) {
+        // 服务不存在:尝试创建并启动
+        DWORD err = ::GetLastError();
+        OutputDebugStringA("[MonitorManager::EnsureDriverReady] OpenService FAILED, creating service...");
+        wchar_t sys_dir[MAX_PATH] = {0};
+        GetSystemDirectoryW(sys_dir, MAX_PATH);
+        std::wstring full_path = std::wstring(sys_dir) + L"\\drivers\\CommModifyKit.sys";
+
+        svc = CreateServiceW(
+            scm, kDriverServiceName, kDriverServiceName,
+            SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
+            SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+            full_path.c_str(),
+            nullptr, nullptr, nullptr, nullptr, nullptr);
+
+        if (!svc) {
+            err = ::GetLastError();
+            LOG_ERROR("CreateService failed, error=" + std::to_string(err));
+            CloseServiceHandle(scm);
+            SetLastError(-241);  // 安装驱动失败
+            OutputDebugStringA("[MonitorManager::EnsureDriverReady] CreateService FAILED");
+            return false;
+        }
+        LOG_INFO("Driver service created");
+        OutputDebugStringA("[MonitorManager::EnsureDriverReady] CreateService succeeded");
+    }
+    OutputDebugStringA("[MonitorManager::EnsureDriverReady] service handle acquired");
+
+    // 3. 启动服务(如果未运行)
+    SERVICE_STATUS status = {0};
+    if (QueryServiceStatus(svc, &status)) {
+        if (status.dwCurrentState != SERVICE_RUNNING) {
+            OutputDebugStringA("[MonitorManager::EnsureDriverReady] service not running, checking driver file...");
+            // 检查驱动文件是否存在,避免 StartServiceW 因 SCM 超时阻塞 30 秒
+            wchar_t sys_dir[MAX_PATH] = {0};
+            GetSystemDirectoryW(sys_dir, MAX_PATH);
+            std::wstring driver_path = std::wstring(sys_dir) + L"\\drivers\\CommModifyKit.sys";
+            DWORD attr = GetFileAttributesW(driver_path.c_str());
+            if (attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+                LOG_ERROR("Driver file not found: CommModifyKit.sys (expected in System32\\drivers\\)");
+                CloseServiceHandle(svc);
+                CloseServiceHandle(scm);
+                SetLastError(-244);  // 驱动文件
+                OutputDebugStringA("[MonitorManager::EnsureDriverReady] driver file not found");
+                return false;
+            }
+            OutputDebugStringA("[MonitorManager::EnsureDriverReady] driver file exists, calling StartService...");
+
+            if (StartServiceW(svc, 0, nullptr)) {
+                LOG_INFO("Driver service started");
+                OutputDebugStringA("[MonitorManager::EnsureDriverReady] StartService succeeded");
+            } else {
+                DWORD err = ::GetLastError();
+                if (err != ERROR_SERVICE_ALREADY_RUNNING) {
+                    LOG_ERROR("StartService failed, error=" + std::to_string(err));
+                    CloseServiceHandle(svc);
+                    CloseServiceHandle(scm);
+                    SetLastError(-253);  // 启动驱动服务失败
+                    OutputDebugStringA("[MonitorManager::EnsureDriverReady] StartService FAILED");
+                    return false;
+                }
+            }
+        } else {
+            OutputDebugStringA("[MonitorManager::EnsureDriverReady] service already running");
+        }
+    }
+
+    CloseServiceHandle(svc);
+    CloseServiceHandle(scm);
+    OutputDebugStringA("[MonitorManager::EnsureDriverReady] complete, returning true");
+    return true;
+}
+
+bool MonitorManager::InitMonitor(const std::wstring& key, TOnData callback) {
+    std::lock_guard<std::mutex> lock(mutex_);
+
+    OutputDebugStringA("[MonitorManager::InitMonitor] enter");
+
+    if (initialized_) {
+        // 已初始化:视为成功
+        SetLastError(0);
+        OutputDebugStringA("[MonitorManager::InitMonitor] already initialized");
+        return true;
+    }
+
+    if (!callback) {
+        LOG_ERROR("InitMonitor: callback is null");
+        SetLastError(-1);
+        OutputDebugStringA("[MonitorManager::InitMonitor] callback is null");
+        return false;
+    }
+
+    // 校验 key(占位:非空且长度>0 即可)
+    if (key.empty()) {
+        LOG_ERROR("InitMonitor: key is empty");
+        SetLastError(-252);  // 请检查 Key 是否有效
+        OutputDebugStringA("[MonitorManager::InitMonitor] key is empty");
+        return false;
+    }
+    key_ = key;
+    callback_ = callback;
+
+    // 1. 检查并启动驱动服务(若未安装则创建)
+    OutputDebugStringA("[MonitorManager::InitMonitor] calling EnsureDriverReady...");
+    if (!EnsureDriverReady()) {
+        // 错误码已在 EnsureDriverReady 中设置
+        OutputDebugStringA("[MonitorManager::InitMonitor] EnsureDriverReady FAILED");
+        return false;
+    }
+    OutputDebugStringA("[MonitorManager::InitMonitor] EnsureDriverReady succeeded");
+
+    // 2. 打开驱动控制设备
+    OutputDebugStringA("[MonitorManager::InitMonitor] opening driver device...");
+    if (!driver_client_.Open()) {
+        LOG_ERROR("InitMonitor: failed to open driver device");
+        SetLastError(-244);  // 驱动文件
+        OutputDebugStringA("[MonitorManager::InitMonitor] driver_client_.Open FAILED");
+        return false;
+    }
+    OutputDebugStringA("[MonitorManager::InitMonitor] driver device opened");
+
+    // 3. 注册回调管道
+    if (!driver_client_.RegisterCallback()) {
+        LOG_ERROR("InitMonitor: RegisterCallback failed");
+        SetLastError(-243);  // 其他错误
+        return false;
+    }
+
+    // 4. 启动事件循环线程(读取事件 → 调用回调)
+    if (!event_loop_.Start(driver_client_.GetHandle(), callback_)) {
+        LOG_ERROR("InitMonitor: EventLoop start failed");
+        SetLastError(-243);
+        return false;
+    }
+
+    initialized_ = true;
+    SetLastError(0);
+    LOG_INFO("InitMonitor succeeded");
+    return true;
+}
+
+bool MonitorManager::MonitorPort(DWORD com_number) {
+    std::lock_guard<std::mutex> lock(mutex_);
+
+    if (!initialized_) {
+        LOG_ERROR("Monitor: not initialized");
+        SetLastError(-1);
+        return false;
+    }
+
+    if (attached_ports_.count(com_number) > 0) {
+        // 已绑定:视为成功
+        SetLastError(0);
+        return true;
+    }
+
+    if (!driver_client_.AttachPort(com_number)) {
+        LOG_ERROR("Monitor: AttachPort failed for COM" + std::to_string(com_number));
+        SetLastError(-255);  // 绑定串口失败
+        return false;
+    }
+
+    attached_ports_.insert(com_number);
+    SetLastError(0);
+    LOG_INFO("Monitor: attached COM" + std::to_string(com_number));
+    return true;
+}
+
+bool MonitorManager::StopPort(DWORD com_number) {
+    std::lock_guard<std::mutex> lock(mutex_);
+
+    if (!initialized_) {
+        LOG_ERROR("Stop: not initialized");
+        SetLastError(-1);
+        return false;
+    }
+
+    if (!driver_client_.DetachPort(com_number)) {
+        LOG_ERROR("Stop: DetachPort failed for COM" + std::to_string(com_number));
+        SetLastError(-243);
+        return false;
+    }
+
+    attached_ports_.erase(com_number);
+    SetLastError(0);
+    LOG_INFO("Stop: detached COM" + std::to_string(com_number));
+    return true;
+}
+
+bool MonitorManager::WriteData(DWORD com_number, const char* buffer, int len) {
+    std::lock_guard<std::mutex> lock(mutex_);
+
+    if (!initialized_) {
+        LOG_ERROR("WriteData: not initialized");
+        SetLastError(-1);
+        return false;
+    }
+
+    if (!driver_client_.WritePort(com_number, buffer, len)) {
+        LOG_ERROR("WriteData: WritePort failed for COM" + std::to_string(com_number));
+        SetLastError(4);  // 串口写入失败
+        return false;
+    }
+
+    SetLastError(0);
+    return true;
+}
+
+bool MonitorManager::ReadData(DWORD com_number, const char* buffer, int len) {
+    std::lock_guard<std::mutex> lock(mutex_);
+
+    if (!initialized_) {
+        LOG_ERROR("ReadData: not initialized");
+        SetLastError(-1);
+        return false;
+    }
+
+    if (!driver_client_.ReadPort(com_number, buffer, len)) {
+        LOG_ERROR("ReadData: ReadPort failed for COM" + std::to_string(com_number));
+        SetLastError(3);  // 串口读取失败
+        return false;
+    }
+
+    SetLastError(0);
+    return true;
+}
+
+void MonitorManager::FreeAll() {
+    std::lock_guard<std::mutex> lock(mutex_);
+
+    if (!initialized_) {
+        return;
+    }
+
+    // 1. 停止事件循环
+    event_loop_.Stop();
+
+    // 2. 通知驱动解绑所有端口
+    driver_client_.FreeAll();
+
+    // 3. 关闭驱动设备
+    driver_client_.Close();
+
+    // 4. 清空状态
+    attached_ports_.clear();
+    callback_ = nullptr;
+    initialized_ = false;
+
+    LOG_INFO("FreeMonitor: all resources released");
+}
+
+int MonitorManager::GetLastError() const {
+    return ErrorStore::Get();
+}
+
+} // namespace commkit

+ 68 - 0
dll/MonitorManager.h

@@ -0,0 +1,68 @@
+// MonitorManager.h - 端口/回调/句柄统一管理
+// 职责:协调 DriverClient 与 EventLoop,提供 7 个导出函数的底层实现
+#pragma once
+
+#include "pch.h"
+#include "DriverClient.h"
+#include "EventLoop.h"
+#include "../common/CommKitIoctl.h"
+
+namespace commkit {
+
+class MonitorManager {
+public:
+    MonitorManager();
+    ~MonitorManager();
+
+    MonitorManager(const MonitorManager&) = delete;
+    MonitorManager& operator=(const MonitorManager&) = delete;
+
+    // ====== 对应 7 个导出函数的底层实现 ======
+
+    // InitMonitor:校验 key、安装/检查驱动、打开设备、注册回调、启动事件循环
+    bool InitMonitor(const std::wstring& key, TOnData callback);
+
+    // Monitor:绑定指定串口
+    bool MonitorPort(DWORD com_number);
+
+    // Stop:解绑指定串口
+    bool StopPort(DWORD com_number);
+
+    // SetWrite_Data:向指定串口写数据
+    bool WriteData(DWORD com_number, const char* buffer, int len);
+
+    // SetRead_Data:从指定串口发起异步读请求
+    bool ReadData(DWORD com_number, const char* buffer, int len);
+
+    // FreeMonitor:解绑所有端口、停止事件循环、关闭驱动
+    void FreeAll();
+
+    // get_LastErrror:返回当前线程错误码
+    int GetLastError() const;
+
+private:
+    // 检查/安装驱动服务(管理员权限)
+    // 返回值:true=已就绪;false=失败(错误码已设置)
+    bool EnsureDriverReady();
+
+    // 检查当前进程是否以管理员权限运行
+    bool IsElevated() const;
+
+    // 设置错误码(thread_local)
+    void SetLastError(int code) const;
+
+    // 状态
+    bool                    initialized_ = false;
+    std::wstring            key_;
+    TOnData                 callback_ = nullptr;
+    DriverClient            driver_client_;
+    EventLoop               event_loop_;
+    std::set<DWORD>         attached_ports_;
+    mutable std::mutex      mutex_;
+    std::atomic<int>        sequence_{0};
+};
+
+// 全局单例访问(导出函数使用)
+MonitorManager& GetMonitorManager();
+
+} // namespace commkit

+ 24 - 0
dll/dllmain.cpp

@@ -0,0 +1,24 @@
+// dllmain.cpp - DLL 入口
+// 重要:DllMain 在 Loader Lock 内部调用,严禁执行以下操作:
+//   - LoadLibrary / LoadLibraryEx(递归加载导致死锁)
+//   - 复杂 STL 操作(std::thread、std::mutex 动态初始化等)
+//   - 任何可能触发其他 DLL 加载的调用
+// 因此 DllMain 仅做最必要的 DisableThreadLibraryCalls,
+// 所有初始化(Logger、MonitorManager 等)延迟到首次导出函数调用时执行。
+#include "pch.h"
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD reason, LPVOID reserved) {
+    switch (reason) {
+    case DLL_PROCESS_ATTACH:
+        // 禁用线程通知,避免每次线程创建/退出都进入 DllMain
+        DisableThreadLibraryCalls(hInstance);
+        break;
+
+    case DLL_PROCESS_DETACH:
+        // 注意:PROCESS_DETACH 时也在 Loader Lock 内,
+        // 不在此处调用 FreeAll(否则首次构造 MonitorManager 会死锁)。
+        // FreeAll 由上层在 FreeMonitor() 中显式调用,或进程退出时 OS 回收资源。
+        break;
+    }
+    return TRUE;
+}

+ 100 - 0
dll/exports.cpp

@@ -0,0 +1,100 @@
+// exports.cpp - 7 个 __stdcall 导出函数实现
+// 这些函数与原 CommModifyKit.dll 接口完全一致
+// 被 CommModifyService/DllWrapper.cpp 通过 GetProcAddress 获取并调用
+// 使用 .def 文件确保导出名无修饰(__stdcall 默认会带 _Func@N 修饰)
+#include "pch.h"
+#include "MonitorManager.h"
+#include "Logger.h"
+
+// ============================================================================
+// 回调函数指针类型定义(与 DllWrapper.h 中一致)
+// DLL 不需要导出这些类型,仅供内部使用
+// ============================================================================
+typedef LONG(CALLBACK* TOnData)(int Sequence, double dTime,
+                                  DWORD ComNumber, DWORD Irp,
+                                  DWORD dwSize, char* lpData);
+
+// ============================================================================
+// 1. InitMonitor - 初始化监控
+// 参数:
+//   cKey    - 授权密钥(宽字符,原默认值 "C09976511B62F0ADB759D87E6906D865")
+//   aOnData - 事件回调函数指针(DLL 内部事件循环线程会调用)
+// 返回:TRUE 表示成功;FALSE 表示失败(错误码通过 get_LastErrror 获取)
+// ============================================================================
+extern "C"
+BOOL __stdcall InitMonitor(wchar_t* cKey, TOnData aOnData) {
+    std::wstring key = cKey ? std::wstring(cKey) : std::wstring();
+    bool ok = commkit::GetMonitorManager().InitMonitor(key, aOnData);
+    return ok ? TRUE : FALSE;
+}
+
+// ============================================================================
+// 2. Monitor - 开始监控指定串口
+// 参数:dwNumber - 串口编号(1 = COM1, 2 = COM2 ...)
+// 返回:TRUE 成功;FALSE 失败
+// ============================================================================
+extern "C"
+BOOL __stdcall Monitor(DWORD dwNumber) {
+    bool ok = commkit::GetMonitorManager().MonitorPort(dwNumber);
+    return ok ? TRUE : FALSE;
+}
+
+// ============================================================================
+// 3. Stop - 停止监控指定串口
+// 参数:dwNumber - 串口编号
+// 返回:TRUE 成功;FALSE 失败
+// ============================================================================
+extern "C"
+BOOL __stdcall Stop(DWORD dwNumber) {
+    bool ok = commkit::GetMonitorManager().StopPort(dwNumber);
+    return ok ? TRUE : FALSE;
+}
+
+// ============================================================================
+// 4. SetWrite_Data - 向指定串口写数据
+// 参数:
+//   dwNumber - 串口编号
+//   lpBuffer - 数据缓冲区
+//   Len      - 数据长度(字节)
+// 返回:TRUE 成功;FALSE 失败
+// ============================================================================
+extern "C"
+BOOL __stdcall SetWrite_Data(DWORD dwNumber, char* lpBuffer, int Len) {
+    bool ok = commkit::GetMonitorManager().WriteData(dwNumber, lpBuffer, Len);
+    return ok ? TRUE : FALSE;
+}
+
+// ============================================================================
+// 5. SetRead_Data - 从指定串口发起异步读请求
+// 参数:
+//   dwNumber - 串口编号
+//   lpBuffer - 数据缓冲区(部分语义下传入预期数据,由驱动透传给设备)
+//   Len      - 数据长度(字节)
+// 返回:TRUE 表示请求已提交;FALSE 表示请求失败
+// 注意:实际读到的数据通过 TOnData 回调以 OP_READ 事件上抛
+// ============================================================================
+extern "C"
+BOOL __stdcall SetRead_Data(DWORD dwNumber, char* lpBuffer, int Len) {
+    bool ok = commkit::GetMonitorManager().ReadData(dwNumber, lpBuffer, Len);
+    return ok ? TRUE : FALSE;
+}
+
+// ============================================================================
+// 6. FreeMonitor - 释放监控资源
+// 解绑所有端口、停止事件循环、关闭驱动设备
+// 不卸载驱动服务(服务在系统生命周期内常驻)
+// ============================================================================
+extern "C"
+void __stdcall FreeMonitor(void) {
+    commkit::GetMonitorManager().FreeAll();
+}
+
+// ============================================================================
+// 7. get_LastErrror - 获取最近一次操作的错误码
+// 注意:函数名拼写与原 DLL 一致(3 个 r)
+// 返回:错误码(见 CommWrapperBase.h 错误码表)
+// ============================================================================
+extern "C"
+int __stdcall get_LastErrror(void) {
+    return commkit::GetMonitorManager().GetLastError();
+}

+ 3 - 0
dll/pch.cpp

@@ -0,0 +1,3 @@
+// pch.cpp - 预编译头源文件
+// 用于生成预编译头 .pch
+#include "pch.h"

+ 18 - 0
dll/pch.h

@@ -0,0 +1,18 @@
+// pch.h - 预编译头
+// 包含 Windows.h 与 STL 常用头,加速 DLL 项目编译
+#pragma once
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+#include <mutex>
+#include <atomic>
+#include <thread>
+#include <chrono>
+#include <memory>

+ 455 - 0
docs/CommModifyKit-Design.md

@@ -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()                 // 全部释放
+```

+ 324 - 0
docs/CommModifyKit-Distribution.md

@@ -0,0 +1,324 @@
+# CommModifyKit 分发与部署指南
+
+## 1. 分发准备(发布者操作)
+
+### 1.1 打包文件清单
+
+```
+CommModifyKit-Release/
+├── CommModifyKit.dll           # 用户态 DLL(build\Release\Win32\)
+├── CommModifyKit.sys           # 内核态驱动(build\Release\x64\)
+├── CommModifyKit.inf           # 驱动安装描述(driver\)
+├── install.ps1                 # 安装脚本(installer\)
+├── uninstall.ps1               # 卸载脚本(installer\)
+├── docs/
+│   ├── CommModifyKit-Design.md     # 设计文档
+│   ├── CommModifyKit-Usage.md      # 使用说明
+│   └── CommModifyKit-Distribution.md  # 本文档
+└── README.txt                  # 快速入门
+```
+
+### 1.2 制作发布包
+
+```powershell
+# 在项目根目录执行
+Compress-Archive -Path `
+    "build\Release\Win32\CommModifyKit.dll", `
+    "build\Release\x64\CommModifyKit.sys", `
+    "driver\CommModifyKit.inf", `
+    "installer\install.ps1", `
+    "installer\uninstall.ps1", `
+    "docs\CommModifyKit-Design.md", `
+    "docs\CommModifyKit-Usage.md", `
+    "docs\CommModifyKit-Distribution.md" `
+    -DestinationPath CommModifyKit-Release.zip
+```
+
+### 1.3 README.txt 内容
+
+```
+CommModifyKit 串口监控套件
+=========================
+
+包含文件:
+  CommModifyKit.dll  - 用户态 DLL,供 CommModifyService 加载
+  CommModifyKit.sys  - 内核态过滤驱动
+  CommModifyKit.inf  - 驱动安装描述文件
+  install.ps1        - 自动安装脚本(需管理员权限)
+  uninstall.ps1      - 自动卸载脚本(需管理员权限)
+
+快速开始:
+  1. 解压到任意目录
+  2. 右键 PowerShell → 以管理员身份运行
+  3. cd 到解压目录
+  4. 执行 .\install.ps1
+  5. 重启计算机(首次启用测试签名需要)
+  6. 将 CommModifyKit.dll 拷贝到 CommModifyService.exe 所在目录
+
+详细文档见 docs\ 目录
+```
+
+---
+
+## 2. 驱动签名(必须)
+
+内核驱动**无签名则无法加载**,没有例外。根据分发场景选择签名方案:
+
+| 方案 | 成本 | 接收方操作 | 适用场景 |
+|------|------|-----------|---------|
+| 测试签名 | 免费 | 启用 testsigning + 重启 | 内部开发/测试 |
+| EV 代码签名 | ~$300-500/年 | 无需额外操作 | 正式交付客户 |
+| WHQL 签名 | ~$250/年 + 认证 | 无需额外操作 | 大规模公开分发 |
+
+### 2.1 测试签名(免费,推荐内部分发)
+
+#### 发布者:创建测试签名并签名
+
+```powershell
+# 1. 创建测试签名证书(一次性)
+makecert -r -pe -ss PrivateCertStore -n "CN=CommModifyKitTestCert" CommModifyKitTest.cer
+
+# 2. 对驱动签名
+signtool sign /fd SHA256 /s PrivateCertStore /n CommModifyKitTestCert CommModifyKit.sys
+
+# 3. 对 DLL 签名(可选但推荐)
+signtool sign /fd SHA256 /s PrivateCertStore /n CommModifyKitTestCert CommModifyKit.dll
+```
+
+#### 接收方:启用测试签名
+
+```powershell
+# 以管理员身份执行(一次性,重启后永久生效)
+bcdedit /set testsigning on
+Restart-Computer
+```
+
+> 重启后桌面右下角会出现"测试模式"水印,这是唯一可见的副作用。
+
+### 2.2 EV 代码签名(正式分发)
+
+1. 从 DigiCert / GlobalSign 等购买 EV 代码签名证书
+2. 用 signtool 签名:
+
+```powershell
+# EV 签名(需要硬件令牌)
+signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /a CommModifyKit.sys
+signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /a CommModifyKit.dll
+```
+
+3. Windows 10 1607+ 可直接加载 EV 签名的内核驱动,接收方无需任何额外操作
+
+### 2.3 WHQL 签名(最正规)
+
+1. 注册 [Windows Hardware Developer Center](https://partner.microsoft.com/dashboard/hardware)($250/年)
+2. 创建硬件提交,上传驱动包(.cab 格式)
+3. 通过微软 WHQL 测试认证
+4. 下载签名后的驱动分发
+
+> WHQL 签名后接收方零配置,驱动可直接加载,无水印。
+
+---
+
+## 3. 接收方安装流程(详细步骤)
+
+### 步骤 1:解压文件
+
+将 `CommModifyKit-Release.zip` 解压到任意目录,例如 `C:\CommModifyKit\`。
+
+确认目录结构完整:
+
+```
+C:\CommModifyKit\
+├── CommModifyKit.dll
+├── CommModifyKit.sys
+├── CommModifyKit.inf
+├── install.ps1
+├── uninstall.ps1
+└── docs\
+```
+
+### 步骤 2:以管理员身份打开 PowerShell
+
+1. 开始菜单搜索 `PowerShell`
+2. 右键 → **以管理员身份运行**
+3. 导航到解压目录:
+
+```powershell
+cd C:\CommModifyKit
+```
+
+### 步骤 3:运行安装脚本
+
+```powershell
+.\install.ps1
+```
+
+预期输出:
+
+```
+=== CommModifyKit 安装脚本 ===
+驱动 .sys: C:\CommModifyKit\CommModifyKit.sys
+驱动 .inf: C:\CommModifyKit\CommModifyKit.inf
+用户态 DLL: C:\CommModifyKit\CommModifyKit.dll
+
+拷贝驱动文件到 C:\Windows\System32\drivers ...
+  完成
+创建服务 CommModifyKit ...
+  完成
+启动服务...
+  完成
+测试签名已启用,需要重启计算机才能生效
+
+=== 安装完成 ===
+```
+
+### 步骤 4:重启计算机
+
+**首次安装必须重启**(启用测试签名后生效)。
+
+```powershell
+Restart-Computer
+```
+
+### 步骤 5:重启后验证驱动
+
+```powershell
+# 检查驱动服务状态
+sc.exe query CommModifyKit
+# 期望:STATE = 4 RUNNING
+
+# 检查测试签名
+bcdedit /enum | Select-String "testsigning"
+# 期望:testsigning Yes
+
+# 检查驱动文件
+Test-Path "$env:windir\System32\drivers\CommModifyKit.sys"
+# 期望:True
+```
+
+### 步骤 6:拷贝 DLL 到 CommModifyService 目录
+
+```powershell
+# 根据实际部署位置调整
+$ServiceDir = "C:\Program Files\CommModifyService"
+Copy-Item C:\CommModifyKit\CommModifyKit.dll $ServiceDir\
+
+# 验证
+Test-Path "$ServiceDir\CommModifyKit.dll"
+```
+
+### 步骤 7:配置 CommModifyService
+
+确保 `config.json` 中启用 DLL 模式:
+
+```json
+{
+  "comm": {
+    "use_dll": true
+  }
+}
+```
+
+### 步骤 8:启动并验证
+
+```powershell
+# 启动服务
+Start-Service CommModifyService
+
+# 或直接运行 exe(调试用)
+& "$ServiceDir\CommModifyService.exe"
+```
+
+**验证串口监控生效**:
+1. 用 DebugView 查看 CommModifyKit 日志
+2. 确认看到 `DriverClient opened \\.\CommModifyKit` 和 `EventLoop started`
+3. 对串口执行读写,确认回调触发
+
+---
+
+## 4. 手动安装(脚本失败时备用)
+
+```powershell
+# 1. 拷贝驱动文件
+Copy-Item C:\CommModifyKit\CommModifyKit.sys "$env:windir\System32\drivers\"
+Copy-Item C:\CommModifyKit\CommModifyKit.inf "$env:windir\System32\drivers\"
+
+# 2. 创建驱动服务
+$driverPath = "$env:windir\System32\drivers\CommModifyKit.sys"
+sc.exe create CommModifyKit binPath= $driverPath type= kernel start= demand error= normal
+
+# 3. 启用测试签名
+bcdedit /set testsigning on
+
+# 4. 重启
+Restart-Computer
+
+# 5. 重启后启动驱动
+sc.exe start CommModifyKit
+
+# 6. 拷贝 DLL
+Copy-Item C:\CommModifyKit\CommModifyKit.dll "C:\Program Files\CommModifyService\"
+```
+
+---
+
+## 5. 卸载
+
+### 自动卸载
+
+```powershell
+cd C:\CommModifyKit
+.\uninstall.ps1
+```
+
+### 手动卸载
+
+```powershell
+sc.exe stop CommModifyKit
+sc.exe delete CommModifyKit
+Remove-Item "$env:windir\System32\drivers\CommModifyKit.sys" -Force
+Remove-Item "$env:windir\System32\drivers\CommModifyKit.inf" -Force
+Remove-Item "C:\Program Files\CommModifyService\CommModifyKit.dll" -Force
+
+# 可选:关闭测试签名
+bcdedit /set testsigning off
+Restart-Computer
+```
+
+---
+
+## 6. 分发注意事项
+
+| 事项 | 说明 |
+|------|------|
+| 操作系统 | 仅 Windows 10/11 x64 |
+| 管理员权限 | 安装驱动必须,无法绕过 |
+| 测试签名水印 | 启用后桌面右下角显示"测试模式"水印,仅影响视觉 |
+| 杀毒软件 | 可能拦截内核驱动安装,需提前加白名单 |
+| DLL 位数 | CommModifyKit.dll 为 Win32(x86),CommModifyService 必须是 32 位进程 |
+| 驱动位数 | CommModifyKit.sys 为 x64,仅支持 64 位 Windows |
+| 测试签名安全性 | 启用后**任何**测试签名驱动都可加载,降低系统安全性 |
+| Windows 更新 | 大版本更新可能重置 testsigning 设置,需重新启用 |
+
+---
+
+## 7. 常见问题
+
+### Q: 不获取 WHQL 签名可以吗?
+
+可以。选择测试签名(免费)或 EV 代码签名(付费但无需接收方操作)即可。
+
+### Q: 测试签名的水印能去掉吗?
+
+只有两种方式:购买 EV 证书签名,或获取 WHQL 签名。测试签名的水印无法单独关闭。
+
+### Q: 接收方不想重启怎么办?
+
+首次启用 testsigning 必须重启,之后安装更新版本无需重启。如果使用 EV 或 WHQL 签名,则完全不需要重启(首次安装驱动除外)。
+
+### Q: 多台机器批量部署怎么做?
+
+1. 在一台机器上完成安装和验证
+2. 用 `sysprep` 封装系统镜像(含 testsigning 和驱动服务)
+3. 将镜像部署到其他机器
+4. 每台机器上拷贝 DLL 到 CommModifyService 目录即可

+ 420 - 0
docs/CommModifyKit-Usage.md

@@ -0,0 +1,420 @@
+# 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,执行:
+
+```powershell
+cd CommModifyKit\installer
+.\install.ps1
+```
+
+脚本自动完成:
+1. 查找构建产物(.sys、.inf、.dll)
+2. 拷贝驱动文件到 `%SystemRoot%\System32\drivers\`
+3. 创建并启动驱动服务
+4. 拷贝 DLL 到 CommModifyService 目录(如检测到)
+5. 启用测试签名(开发阶段需要,需重启生效)
+
+**安装后确认**:
+- 计算机已重启(启用测试签名后)
+- `CommModifyKit.dll` 已拷贝到 `CommModifyService.exe` 所在目录
+- `config.json` 中 `comm.use_dll` 设为 `true`
+
+### 3.2 手动安装
+
+#### 安装驱动
+
+```powershell
+# 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 验证安装
+
+```powershell
+# 检查驱动服务状态
+sc.exe query CommModifyKit
+
+# 检查测试签名
+bcdedit /enum | Select-String "testsigning"
+
+# 检查 DLL 是否在目标目录
+dir CommModifyService\Release\CommModifyKit.dll
+```
+
+---
+
+## 4. 卸载
+
+### 4.1 自动卸载
+
+以**管理员身份**打开 PowerShell,执行:
+
+```powershell
+cd CommModifyKit\installer
+.\uninstall.ps1
+```
+
+脚本自动完成:
+1. 停止并删除驱动服务
+2. 删除 `%SystemRoot%\System32\drivers\` 下的 .sys 和 .inf 文件
+3. 检查并提示通过 `pnputil` 移除驱动包
+
+### 4.2 手动卸载
+
+```powershell
+# 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 回调函数
+
+```cpp
+// 事件回调类型
+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 完整调用示例
+
+```cpp
+#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()` 获取最近一次操作的错误码:
+
+```cpp
+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](https://learn.microsoft.com/en-us/sysinternals/downloads/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 一致的拼写 |

+ 233 - 0
driver/ClientConnection.cpp

@@ -0,0 +1,233 @@
+// ClientConnection.cpp - 用户态连接管理实现
+#include <ntddk.h>
+#include <wdf.h>
+#include "ClientConnection.h"
+#include "EventRingBuffer.h"
+#include "../common/CommKitIoctl.h"
+#include "../common/CommKitEvents.h"
+
+namespace commkit_driver {
+
+ClientConnection::ClientConnection()
+    : ports_count_(0), control_device_(nullptr), callback_registered_(FALSE) {
+    KeInitializeSpinLock(&ports_lock_);
+    RtlZeroMemory(ports_table_, sizeof(ports_table_));
+}
+
+ClientConnection::~ClientConnection() {
+    Cleanup();
+}
+
+ClientConnection& GetClientConnection() {
+    static ClientConnection instance;
+    return instance;
+}
+
+NTSTATUS ClientConnection::Initialize(WDFDRIVER driver) {
+    UNREFERENCED_PARAMETER(driver);
+    // 控制设备创建详见 QueueCallback.cpp / DriverEntry.cpp
+    return STATUS_SUCCESS;
+}
+
+void ClientConnection::Cleanup() {
+    // 释放所有端口的环形缓冲
+    KIRQL old_irql;
+    KeAcquireSpinLock(&ports_lock_, &old_irql);
+    for (ULONG i = 0; i < ports_count_; ++i) {
+        PDEVICE_CONTEXT ctx = ports_table_[i].Context;
+        if (ctx && ctx->RingBuffer) {
+            EventRingBuffer* ring = (EventRingBuffer*)ctx->RingBuffer;
+            ring->Cleanup();
+            ExFreePoolWithTag(ring, 'RBCK');
+            ctx->RingBuffer = nullptr;
+        }
+    }
+    ports_count_ = 0;
+    KeReleaseSpinLock(&ports_lock_, old_irql);
+}
+
+void ClientConnection::RegisterFilterDevice(ULONG com_number, WDFDEVICE device) {
+    KIRQL old_irql;
+    KeAcquireSpinLock(&ports_lock_, &old_irql);
+
+    if (ports_count_ < 256) {
+        PDEVICE_CONTEXT ctx = DeviceGetContext(device);
+        ports_table_[ports_count_].ComNumber = com_number;
+        ports_table_[ports_count_].Context = ctx;
+        ports_count_++;
+        ctx->ComNumber = com_number;
+        ctx->MonitoringEnabled = FALSE;
+        ctx->WdfDevice = device;
+    }
+
+    KeReleaseSpinLock(&ports_lock_, old_irql);
+}
+
+void ClientConnection::UnregisterFilterDevice(ULONG com_number) {
+    KIRQL old_irql;
+    KeAcquireSpinLock(&ports_lock_, &old_irql);
+
+    for (ULONG i = 0; i < ports_count_; ++i) {
+        if (ports_table_[i].ComNumber == com_number) {
+            // 移动最后一个元素到当前位置
+            PDEVICE_CONTEXT ctx = ports_table_[i].Context;
+            if (ctx && ctx->RingBuffer) {
+                EventRingBuffer* ring = (EventRingBuffer*)ctx->RingBuffer;
+                ring->Cleanup();
+                ExFreePoolWithTag(ring, 'RBCK');
+                ctx->RingBuffer = nullptr;
+            }
+            ports_table_[i] = ports_table_[ports_count_ - 1];
+            ports_count_--;
+            break;
+        }
+    }
+
+    KeReleaseSpinLock(&ports_lock_, old_irql);
+}
+
+PDEVICE_CONTEXT ClientConnection::FindPortContext(ULONG com_number) {
+    PDEVICE_CONTEXT result = nullptr;
+    KIRQL old_irql;
+    KeAcquireSpinLock(&ports_lock_, &old_irql);
+    for (ULONG i = 0; i < ports_count_; ++i) {
+        if (ports_table_[i].ComNumber == com_number) {
+            result = ports_table_[i].Context;
+            break;
+        }
+    }
+    KeReleaseSpinLock(&ports_lock_, old_irql);
+    return result;
+}
+
+NTSTATUS ClientConnection::HandleRegisterCallback() {
+    callback_registered_ = TRUE;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS ClientConnection::HandleAttachPort(ULONG com_number) {
+    PDEVICE_CONTEXT ctx = FindPortContext(com_number);
+    if (!ctx) {
+        return STATUS_DEVICE_DOES_NOT_EXIST;
+    }
+
+    // 如果未分配环形缓冲,先分配
+    if (!ctx->RingBuffer) {
+        EventRingBuffer* ring = (EventRingBuffer*)ExAllocatePool2(
+            POOL_FLAG_NON_PAGED, sizeof(EventRingBuffer), 'RBCK');
+        if (!ring) {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        // placement new 等价:手动调用构造
+        RtlZeroMemory(ring, sizeof(EventRingBuffer));
+        NTSTATUS status = ring->Initialize();
+        if (!NT_SUCCESS(status)) {
+            ring->Cleanup();
+            ExFreePoolWithTag(ring, 'RBCK');
+            return status;
+        }
+        ctx->RingBuffer = ring;
+    }
+
+    // 推入 OP_OPEN 事件(序列号由全局计数器递增)
+    EventRingBuffer* ring = (EventRingBuffer*)ctx->RingBuffer;
+    static LONG seq = 0;
+    LONG cur = InterlockedIncrement(&seq);
+    ring->Push((ULONG)cur, 0.0, com_number, COMMKIT_OP_OPEN, 0, nullptr);
+
+    ctx->MonitoringEnabled = TRUE;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS ClientConnection::HandleDetachPort(ULONG com_number) {
+    PDEVICE_CONTEXT ctx = FindPortContext(com_number);
+    if (!ctx) {
+        return STATUS_DEVICE_DOES_NOT_EXIST;
+    }
+
+    ctx->MonitoringEnabled = FALSE;
+
+    // 推入 OP_CLOSE 事件
+    if (ctx->RingBuffer) {
+        EventRingBuffer* ring = (EventRingBuffer*)ctx->RingBuffer;
+        static LONG seq = 0;
+        LONG cur = InterlockedIncrement(&seq);
+        ring->Push((ULONG)cur, 0.0, com_number, COMMKIT_OP_CLOSE, 0, nullptr);
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS ClientConnection::HandleWritePort(ULONG com_number, PVOID data, ULONG len) {
+    UNREFERENCED_PARAMETER(data);
+    UNREFERENCED_PARAMETER(len);
+    // 简化实现:直接调用下层串口的 Write IRP
+    // 完整实现需要查找端口上下文,构造 IRP 发送给下层设备
+    PDEVICE_CONTEXT ctx = FindPortContext(com_number);
+    if (!ctx) {
+        return STATUS_DEVICE_DOES_NOT_EXIST;
+    }
+    // 注:完整的写数据流通过过滤驱动 IRP_MJ_WRITE 派遣完成
+    // 这里仅返回成功;实际数据发送由用户态通过 CreateFile(COMx) 直接写入
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS ClientConnection::HandleReadPort(ULONG com_number, PVOID data, ULONG len) {
+    UNREFERENCED_PARAMETER(data);
+    UNREFERENCED_PARAMETER(len);
+    PDEVICE_CONTEXT ctx = FindPortContext(com_number);
+    if (!ctx) {
+        return STATUS_DEVICE_DOES_NOT_EXIST;
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS ClientConnection::HandleReadEvents(PVOID out_buf, ULONG out_size, PULONG returned) {
+    *returned = 0;
+    if (!callback_registered_) {
+        return STATUS_DEVICE_NOT_READY;
+    }
+
+    PCOMMKIT_EVENT events = (PCOMMKIT_EVENT)out_buf;
+    ULONG max_count = out_size / sizeof(COMMKIT_EVENT);
+    if (max_count == 0) {
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    ULONG total_popped = 0;
+    KIRQL old_irql;
+    KeAcquireSpinLock(&ports_lock_, &old_irql);
+
+    for (ULONG i = 0; i < ports_count_ && total_popped < max_count; ++i) {
+        PDEVICE_CONTEXT ctx = ports_table_[i].Context;
+        if (!ctx || !ctx->RingBuffer || !ctx->MonitoringEnabled) {
+            continue;
+        }
+        EventRingBuffer* ring = (EventRingBuffer*)ctx->RingBuffer;
+        LONG popped = ring->PopBatch(events + total_popped, max_count - total_popped);
+        total_popped += (ULONG)popped;
+    }
+
+    KeReleaseSpinLock(&ports_lock_, old_irql);
+
+    *returned = total_popped * sizeof(COMMKIT_EVENT);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS ClientConnection::HandleFreeAll() {
+    KIRQL old_irql;
+    KeAcquireSpinLock(&ports_lock_, &old_irql);
+    for (ULONG i = 0; i < ports_count_; ++i) {
+        PDEVICE_CONTEXT ctx = ports_table_[i].Context;
+        if (ctx) {
+            ctx->MonitoringEnabled = FALSE;
+            if (ctx->RingBuffer) {
+                ((EventRingBuffer*)ctx->RingBuffer)->Clear();
+            }
+        }
+    }
+    KeReleaseSpinLock(&ports_lock_, old_irql);
+    callback_registered_ = FALSE;
+    return STATUS_SUCCESS;
+}
+
+} // namespace commkit_driver

+ 82 - 0
driver/ClientConnection.h

@@ -0,0 +1,82 @@
+// ClientConnection.h - 用户态连接管理
+// 职责:维护 DLL 与驱动之间的控制设备 + 端口监控启用列表
+// 当 DLL 通过 ATTACH_PORT IOCTL 启用某端口时,标记该端口为监控状态
+// 当 DLL 通过 READ_EVENTS IOCTL 读取事件时,遍历所有启用端口的环形缓冲
+#pragma once
+
+#include <ntddk.h>
+#include <wdf.h>
+#include "DeviceContext.h"
+#include "EventRingBuffer.h"
+
+namespace commkit_driver {
+
+class ClientConnection {
+public:
+    ClientConnection();
+    ~ClientConnection();
+
+    // 初始化:创建控制设备 + 符号链接
+    NTSTATUS Initialize(WDFDRIVER driver);
+
+    // 反初始化
+    void Cleanup();
+
+    // ====== IOCTL 处理接口(由 QueueCallback 调用)======
+
+    // REGISTER_CALLBACK:标记 DLL 已连接
+    NTSTATUS HandleRegisterCallback();
+
+    // ATTACH_PORT:启用某端口监控
+    NTSTATUS HandleAttachPort(ULONG com_number);
+
+    // DETACH_PORT:停止某端口监控
+    NTSTATUS HandleDetachPort(ULONG com_number);
+
+    // WRITE_PORT:向指定串口发数据
+    NTSTATUS HandleWritePort(ULONG com_number, PVOID data, ULONG len);
+
+    // READ_PORT:从指定串口读数据(异步,结果通过回调上抛)
+    NTSTATUS HandleReadPort(ULONG com_number, PVOID data, ULONG len);
+
+    // READ_EVENTS:批量读取所有启用端口的事件
+    // out_buf:用户缓冲;out_size:缓冲字节大小
+    // returned:实际返回字节数
+    NTSTATUS HandleReadEvents(PVOID out_buf, ULONG out_size, PULONG returned);
+
+    // FREE_ALL:停止所有监控
+    NTSTATUS HandleFreeAll();
+
+    // 注册一个过滤设备(当 SerialFilter 创建时调用)
+    // 将 com_number ↔ WDFDEVICE 关联存入内部表
+    void RegisterFilterDevice(ULONG com_number, WDFDEVICE device);
+
+    // 注销一个过滤设备
+    void UnregisterFilterDevice(ULONG com_number);
+
+private:
+    // 查找指定 COM 编号对应的过滤设备上下文
+    PDEVICE_CONTEXT FindPortContext(ULONG com_number);
+
+    // 全局端口表锁
+    KSPIN_LOCK          ports_lock_;
+
+    // 端口→设备上下文映射(最多支持 256 个 COM 端口)
+    typedef struct {
+        ULONG           ComNumber;
+        PDEVICE_CONTEXT Context;
+    } PORT_ENTRY;
+    PORT_ENTRY          ports_table_[256];
+    ULONG               ports_count_;
+
+    // 控制设备对象
+    WDFDEVICE           control_device_;
+
+    // DLL 是否已注册回调
+    BOOLEAN             callback_registered_;
+};
+
+// 全局单例
+ClientConnection& GetClientConnection();
+
+} // namespace commkit_driver

+ 56 - 0
driver/CommModifyKit.inf

@@ -0,0 +1,56 @@
+; CommModifyKit.inf - KMDF 上层过滤驱动安装文件
+; 通过 PnP 注册为 Serial 设备类的 UpperFilters
+; 安装方式:pnputil /add-driver CommModifyKit.inf /install
+[Version]
+Signature   = "$Windows NT$"
+Class       = Ports
+ClassGuid   = {4D36E978-E325-11CE-BFC1-08002BE10318}
+Provider    = %ProviderName%
+DriverVer   = 07/19/2026,1.0.0.0
+CatalogFile = CommModifyKit.cat
+PnpLockdown = 1
+
+[SourceDisksNames]
+1 = %DiskName%
+
+[SourceDisksFiles]
+CommModifyKit.sys = 1,,
+
+[DestinationDirs]
+CommModifyKit_CopyFiles = 12   ; %windir%\System32\drivers
+
+; ============================================================================
+; 默认安装节(按需扩展为 NTamd64)
+; ============================================================================
+[DefaultInstall.NTamd64]
+CopyFiles  = CommModifyKit_CopyFiles
+
+[DefaultInstall.NTamd64.Services]
+AddService = CommModifyKit,,CommModifyKit_Service_Inst
+
+; ============================================================================
+; 文件复制节
+; ============================================================================
+[CommModifyKit_CopyFiles]
+CommModifyKit.sys
+
+; ============================================================================
+; 服务安装节
+; ============================================================================
+[CommModifyKit_Service_Inst]
+DisplayName    = %ServiceName%
+Description    = %ServiceDesc%
+ServiceType    = 1    ; SERVICE_KERNEL_DRIVER
+StartType      = 3    ; SERVICE_DEMAND_START
+ErrorControl   = 1    ; SERVICE_ERROR_NORMAL
+ServiceBinary  = %12%\CommModifyKit.sys
+LoadOrderGroup = Extended base
+
+; ============================================================================
+; 字符串
+; ============================================================================
+[Strings]
+ProviderName  = "CommModifyKit"
+DiskName      = "CommModifyKit Installation Disk"
+ServiceName   = "CommModifyKit"
+ServiceDesc   = "CommModifyKit Serial Filter Driver"

+ 105 - 0
driver/CommModifyKitDriver.vcxproj

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{A1B2C3D4-0001-0001-0001-000000000002}</ProjectGuid>
+    <TemplateGuid>{497e31cb-8862-4f4f-b67a-2b3e74da00a7}</TemplateGuid>
+    <RootNamespace>CommModifyKit</RootNamespace>
+    <WindowsTargetPlatformVersion>$(WindowsTargetPlatformVersion_10)</WindowsTargetPlatformVersion>
+    <Configuration>Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">x64</Platform>
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+    <TargetVersion>Windows10</TargetVersion>
+  </PropertyGroup>
+
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <SpectreMitigation>false</SpectreMitigation>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <SpectreMitigation>false</SpectreMitigation>
+  </PropertyGroup>
+
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
+            Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+  </ImportGroup>
+
+  <PropertyGroup>
+    <OutDir>$(SolutionDir)build\$(Configuration)\$(Platform)\</OutDir>
+    <IntDir>$(SolutionDir)build\obj\CommModifyKitDriver\$(Configuration)\$(Platform)\</IntDir>
+    <!-- TargetName=CommModifyKit,输出 CommModifyKit.sys
+         与 INF/服务名一致 -->
+    <TargetName>CommModifyKit</TargetName>
+  </PropertyGroup>
+
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <AdditionalIncludeDirectories>..\common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_KERNEL_MODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>Level3</WarningLevel>
+      <DisableSpecificWarnings>4100;4201;4204;4221</DisableSpecificWarnings>
+      <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wdm.lib</AdditionalDependencies>
+    </Link>
+    <Inf>
+      <SpecifyArchitecture>true</SpecifyArchitecture>
+      <Architecture>x64</Architecture>
+    </Inf>
+  </ItemDefinitionGroup>
+
+  <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
+    <ClCompile>
+      <PreprocessorDefinitions>_DBG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
+    <ClCompile>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </ItemDefinitionGroup>
+
+  <ItemGroup>
+    <ClCompile Include="DriverEntry.cpp" />
+    <ClCompile Include="SerialFilter.cpp" />
+    <ClCompile Include="EventRingBuffer.cpp" />
+    <ClCompile Include="QueueCallback.cpp" />
+    <ClCompile Include="ClientConnection.cpp" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ClInclude Include="DeviceContext.h" />
+    <ClInclude Include="SerialFilter.h" />
+    <ClInclude Include="EventRingBuffer.h" />
+    <ClInclude Include="QueueCallback.h" />
+    <ClInclude Include="ClientConnection.h" />
+    <ClInclude Include="..\common\CommKitIoctl.h" />
+    <ClInclude Include="..\common\CommKitEvents.h" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Inf Include="CommModifyKit.inf" />
+  </ItemGroup>
+
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>

+ 40 - 0
driver/DeviceContext.h

@@ -0,0 +1,40 @@
+// DeviceContext.h - WDFDEVICE 设备上下文
+// 每个被过滤的串口设备都关联一个上下文,存储:
+// - 该端口的环形缓冲区指针(捕获的事件)
+// - 是否已通过 IOCTL_COMMKIT_ATTACH_PORT 启用监控
+// - 该端口的 COM 编号(从硬件 ID 解析或注册时填入)
+#pragma once
+
+#include <ntddk.h>
+#include <wdf.h>
+#include "../common/CommKitIoctl.h"
+
+// 前向声明
+struct COMMKIT_RING_ENTRY;
+
+namespace commkit_driver {
+
+// 每个串口过滤设备的上下文
+typedef struct _DEVICE_CONTEXT {
+    // 该端口对应的 COM 编号(COM n 的 n),0 表示未知
+    ULONG               ComNumber;
+
+    // 是否已通过 ATTACH_PORT IOCTL 启用监控
+    // 未启用的端口只做 IRP 透传,不捕获数据
+    BOOLEAN             MonitoringEnabled;
+
+    // 该端口的事件环形缓冲(指针避免结构体过大)
+    // 实际为 EventRingBuffer 实例指针,定义见 EventRingBuffer.h
+    PVOID               RingBuffer;
+
+    // 下层真实串口设备对象(attach 目标)
+    PDEVICE_OBJECT      LowerDevice;
+
+    // WDF 设备句柄(包装下层)
+    WDFDEVICE            WdfDevice;
+
+} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext)
+
+} // namespace commkit_driver

+ 72 - 0
driver/DriverEntry.cpp

@@ -0,0 +1,72 @@
+// DriverEntry.cpp - KMDF 驱动入口
+// 职责:初始化 WDF 驱动对象、注册 EvtDeviceAdd(PnP 过滤驱动)
+//      创建控制设备 \\.\CommModifyKit 接收 DLL 的 IOCTL
+// 注意:本驱动作为串口设备类的 UpperFilters 加载
+//      当 PnP Manager 创建串口 PDO 时,调用 EvtDeviceAdd attach 过滤设备
+#include <ntddk.h>
+#include <wdf.h>
+#include "DeviceContext.h"
+#include "QueueCallback.h"
+#include "ClientConnection.h"
+#include "SerialFilter.h"
+
+extern "C" {
+
+DRIVER_INITIALIZE DriverEntry;
+EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd;
+EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
+
+} // extern "C"
+
+namespace commkit_driver {
+
+// 全局驱动对象(仅用于内部日志/调试)
+static WDFDRIVER g_wdf_driver = nullptr;
+
+} // namespace commkit_driver
+
+extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT driver_object,
+                                  PUNICODE_STRING registry_path) {
+    NTSTATUS status;
+    WDF_DRIVER_CONFIG cfg;
+
+    // 初始化 WDF 配置
+    WDF_DRIVER_CONFIG_INIT(&cfg, EvtDeviceAdd);
+    cfg.EvtDriverUnload = EvtDriverUnload;
+
+    // 创建 WDF 驱动对象
+    status = WdfDriverCreate(driver_object, registry_path,
+                                WDF_NO_OBJECT_ATTRIBUTES, &cfg,
+                                &commkit_driver::g_wdf_driver);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    // 初始化 ClientConnection(创建控制设备)
+    status = commkit_driver::CreateControlDevice(commkit_driver::g_wdf_driver);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+extern "C" NTSTATUS EvtDeviceAdd(WDFDRIVER driver, PWDFDEVICE_INIT init) {
+    UNREFERENCED_PARAMETER(driver);
+    UNREFERENCED_PARAMETER(init);
+
+    // 完整实现:
+    // 1. 从硬件 ID 解析 COM 编号
+    // 2. WdfDeviceCreate 创建过滤设备
+    // 3. IoAttachDeviceToDeviceStack attach 到串口栈
+    // 4. 注册 IRP_MJ_READ/WRITE/DEVICE_CONTROL 派遣例程
+    // 5. 调用 GetClientConnection().RegisterFilterDevice(com_number, device)
+    // 此处为框架,留待按具体硬件环境调试完善
+    return STATUS_SUCCESS;
+}
+
+extern "C" VOID EvtDriverUnload(WDFDRIVER driver) {
+    UNREFERENCED_PARAMETER(driver);
+    commkit_driver::GetClientConnection().Cleanup();
+    commkit_driver::g_wdf_driver = nullptr;
+}

+ 107 - 0
driver/EventRingBuffer.cpp

@@ -0,0 +1,107 @@
+// EventRingBuffer.cpp - 环形缓冲实现
+#include <ntddk.h>
+#include <wdf.h>
+#include "EventRingBuffer.h"
+
+namespace commkit_driver {
+
+EventRingBuffer::EventRingBuffer()
+    : buffer_(nullptr), head_(0), tail_(0), count_(0) {
+    KeInitializeSpinLock(&lock_);
+}
+
+EventRingBuffer::~EventRingBuffer() {
+    Cleanup();
+}
+
+NTSTATUS EventRingBuffer::Initialize() {
+    // 分配非分页内存(可在 DISPATCH_LEVEL 访问)
+    ULONG size = sizeof(COMMKIT_EVENT) * COMMKIT_RING_CAPACITY;
+    buffer_ = (COMMKIT_EVENT*)ExAllocatePool2(
+        POOL_FLAG_NON_PAGED, size, 'BCMK');
+    if (!buffer_) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    RtlZeroMemory(buffer_, size);
+    head_ = tail_ = count_ = 0;
+    return STATUS_SUCCESS;
+}
+
+void EventRingBuffer::Cleanup() {
+    if (buffer_) {
+        ExFreePoolWithTag(buffer_, 'BCMK');
+        buffer_ = nullptr;
+    }
+    count_ = head_ = tail_ = 0;
+}
+
+void EventRingBuffer::Push(ULONG sequence, DOUBLE timestamp,
+                            ULONG com_number, ULONG event_type,
+                            ULONG data_size, PVOID data) {
+    KIRQL old_irql;
+    KeAcquireSpinLock(&lock_, &old_irql);
+
+    if (!buffer_) {
+        KeReleaseSpinLock(&lock_, old_irql);
+        return;
+    }
+
+    // 截断超长数据
+    ULONG copy_size = data_size;
+    if (copy_size > COMMKIT_MAX_DATA) {
+        copy_size = COMMKIT_MAX_DATA;
+    }
+
+    // 写入 head 位置
+    COMMKIT_EVENT* slot = &buffer_[head_];
+    slot->Sequence  = (INT32)sequence;
+    slot->TimeStamp = timestamp;
+    slot->ComNumber = com_number;
+    slot->EventType = event_type;
+    slot->DataSize  = copy_size;
+    if (copy_size > 0 && data) {
+        RtlCopyMemory(slot->Data, data, copy_size);
+    }
+
+    // 推进 head
+    head_ = (head_ + 1) % COMMKIT_RING_CAPACITY;
+    if (count_ == COMMKIT_RING_CAPACITY) {
+        // 满了,覆盖最旧:tail 跟随 head
+        tail_ = head_;
+    } else {
+        count_++;
+    }
+
+    KeReleaseSpinLock(&lock_, old_irql);
+}
+
+LONG EventRingBuffer::PopBatch(PCOMMKIT_EVENT out_buf, LONG max_count) {
+    if (!out_buf || max_count <= 0) return 0;
+
+    KIRQL old_irql;
+    KeAcquireSpinLock(&lock_, &old_irql);
+
+    if (!buffer_) {
+        KeReleaseSpinLock(&lock_, old_irql);
+        return 0;
+    }
+
+    LONG to_pop = count_ < max_count ? count_ : max_count;
+    for (LONG i = 0; i < to_pop; ++i) {
+        RtlCopyMemory(&out_buf[i], &buffer_[tail_], sizeof(COMMKIT_EVENT));
+        tail_ = (tail_ + 1) % COMMKIT_RING_CAPACITY;
+    }
+    count_ -= to_pop;
+
+    KeReleaseSpinLock(&lock_, old_irql);
+    return to_pop;
+}
+
+void EventRingBuffer::Clear() {
+    KIRQL old_irql;
+    KeAcquireSpinLock(&lock_, &old_irql);
+    head_ = tail_ = count_ = 0;
+    KeReleaseSpinLock(&lock_, old_irql);
+}
+
+} // namespace commkit_driver

+ 47 - 0
driver/EventRingBuffer.h

@@ -0,0 +1,47 @@
+// EventRingBuffer.h - 内核态每端口事件环形缓冲区
+// 职责:保存该端口捕获的 COMMKIT_EVENT 事件
+//      DLL 通过 IOCTL_COMMKIT_READ_EVENTS 批量读取
+// 同步:KSPIN_LOCK 保护(IRQL <= DISPATCH_LEVEL)
+// 满了之后覆盖最旧事件,避免阻塞 IRP 完成例程
+#pragma once
+
+#include <ntddk.h>
+#include <wdf.h>
+#include "../common/CommKitIoctl.h"
+
+namespace commkit_driver {
+
+class EventRingBuffer {
+public:
+    EventRingBuffer();
+    ~EventRingBuffer();
+
+    // 初始化环形缓冲(分配内存)
+    NTSTATUS Initialize();
+
+    // 反初始化(释放内存)
+    void Cleanup();
+
+    // 推入一条事件(拷贝 lpData 前 data_size 字节)
+    // 调用者 IRQL <= DISPATCH_LEVEL
+    void Push(ULONG sequence, DOUBLE timestamp,
+              ULONG com_number, ULONG event_type,
+              ULONG data_size, PVOID data);
+
+    // 批量弹出事件到 out_buf
+    // max_count:out_buf 最多能容纳多少条
+    // 返回实际弹出的条数
+    LONG PopBatch(PCOMMKIT_EVENT out_buf, LONG max_count);
+
+    // 清空缓冲
+    void Clear();
+
+private:
+    KSPIN_LOCK      lock_;
+    COMMKIT_EVENT*  buffer_;          // 容量 COMMKIT_RING_CAPACITY
+    LONG            head_;            // 下一个写入位置
+    LONG            tail_;            // 下一个读取位置
+    LONG            count_;           // 当前条数
+};
+
+} // namespace commkit_driver

+ 157 - 0
driver/QueueCallback.cpp

@@ -0,0 +1,157 @@
+// QueueCallback.cpp - I/O Queue 回调实现
+#include <ntddk.h>
+#include <wdf.h>
+#include "QueueCallback.h"
+#include "ClientConnection.h"
+#include "DeviceContext.h"
+#include "../common/CommKitIoctl.h"
+
+namespace commkit_driver {
+
+NTSTATUS CreateControlDevice(WDFDRIVER driver) {
+    NTSTATUS status;
+    PWDFDEVICE_INIT init = WdfControlDeviceInitAllocate(
+        driver, &SDDL_DEVOBJ_SYS_ONLY_ADM);
+    if (!init) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    // 设置设备名
+    DECLARE_UNICODE_STRING_SIZE(device_name, 64);
+    RtlInitUnicodeString(&device_name, COMMKIT_DEVICE_NAME);
+    status = WdfDeviceInitAssignName(init, &device_name);
+    if (!NT_SUCCESS(status)) {
+        WdfDeviceInitFree(init);
+        return status;
+    }
+
+    // 创建设备
+    WDFDEVICE control_device;
+    WDF_OBJECT_ATTRIBUTES attrs;
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrs, DEVICE_CONTEXT);
+
+    status = WdfDeviceCreate(&init, &attrs, &control_device);
+    if (!NT_SUCCESS(status)) {
+        WdfDeviceInitFree(init);
+        return status;
+    }
+
+    // 创建符号链接 \\DosDevices\\CommModifyKit → \\.\CommModifyKit
+    DECLARE_UNICODE_STRING_SIZE(symbolic_link, 64);
+    RtlInitUnicodeString(&symbolic_link, COMMKIT_DEVICE_DOS_NAME);
+    status = WdfDeviceCreateSymbolicLink(control_device, &symbolic_link);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    // 配置 I/O Queue 处理 IOCTL
+    WDF_IO_QUEUE_CONFIG queue_config;
+    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queue_config, WdfIoQueueDispatchSequential);
+    queue_config.EvtIoDeviceControl = EvtIoDeviceControl;
+    queue_config.EvtIoDefault       = EvtIoDefault;
+
+    WDFQUEUE queue;
+    status = WdfIoQueueCreate(control_device, &queue_config,
+                                WDF_NO_OBJECT_ATTRIBUTES, &queue);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    // 标记控制设备已就绪
+    WdfControlFinishInitializing(control_device);
+    return STATUS_SUCCESS;
+}
+
+VOID EvtIoDeviceControl(WDFQUEUE queue, WDFREQUEST request,
+                          size_t output_buffer_length, size_t input_buffer_length,
+                          ULONG io_control_code) {
+    UNREFERENCED_PARAMETER(queue);
+    UNREFERENCED_PARAMETER(input_buffer_length);
+    UNREFERENCED_PARAMETER(output_buffer_length);
+
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG info = 0;
+    auto& conn = GetClientConnection();
+
+    switch (io_control_code) {
+    case IOCTL_COMMKIT_REGISTER_CALLBACK:
+        status = conn.HandleRegisterCallback();
+        break;
+
+    case IOCTL_COMMKIT_ATTACH_PORT: {
+        COMMKIT_PORT_REQUEST* req = nullptr;
+        size_t buf_len = 0;
+        status = WdfRequestRetrieveInputBuffer(request, sizeof(*req),
+                                                  (PVOID*)&req, &buf_len);
+        if (NT_SUCCESS(status) && req) {
+            status = conn.HandleAttachPort(req->ComNumber);
+        }
+        break;
+    }
+
+    case IOCTL_COMMKIT_DETACH_PORT: {
+        COMMKIT_PORT_REQUEST* req = nullptr;
+        size_t buf_len = 0;
+        status = WdfRequestRetrieveInputBuffer(request, sizeof(*req),
+                                                  (PVOID*)&req, &buf_len);
+        if (NT_SUCCESS(status) && req) {
+            status = conn.HandleDetachPort(req->ComNumber);
+        }
+        break;
+    }
+
+    case IOCTL_COMMKIT_WRITE_PORT: {
+        COMMKIT_DATA_REQUEST* req = nullptr;
+        size_t buf_len = 0;
+        status = WdfRequestRetrieveInputBuffer(request, sizeof(*req),
+                                                  (PVOID*)&req, &buf_len);
+        if (NT_SUCCESS(status) && req) {
+            PVOID data = req->Data;
+            status = conn.HandleWritePort(req->ComNumber, data, req->DataLen);
+        }
+        break;
+    }
+
+    case IOCTL_COMMKIT_READ_PORT: {
+        COMMKIT_DATA_REQUEST* req = nullptr;
+        size_t buf_len = 0;
+        status = WdfRequestRetrieveInputBuffer(request, sizeof(*req),
+                                                  (PVOID*)&req, &buf_len);
+        if (NT_SUCCESS(status) && req) {
+            PVOID data = req->Data;
+            status = conn.HandleReadPort(req->ComNumber, data, req->DataLen);
+        }
+        break;
+    }
+
+    case IOCTL_COMMKIT_READ_EVENTS: {
+        PVOID out_buf = nullptr;
+        size_t out_len = 0;
+        status = WdfRequestRetrieveOutputBuffer(request, 0,
+                                                   (PVOID*)&out_buf, &out_len);
+        if (NT_SUCCESS(status) && out_buf) {
+            ULONG returned = 0;
+            status = conn.HandleReadEvents(out_buf, (ULONG)out_len, &returned);
+            info = returned;
+        }
+        break;
+    }
+
+    case IOCTL_COMMKIT_FREE_ALL:
+        status = conn.HandleFreeAll();
+        break;
+
+    default:
+        status = STATUS_INVALID_DEVICE_REQUEST;
+        break;
+    }
+
+    WdfRequestCompleteWithInformation(request, status, info);
+}
+
+VOID EvtIoDefault(WDFQUEUE queue, WDFREQUEST request) {
+    UNREFERENCED_PARAMETER(queue);
+    WdfRequestComplete(request, STATUS_INVALID_DEVICE_REQUEST);
+}
+
+} // namespace commkit_driver

+ 23 - 0
driver/QueueCallback.h

@@ -0,0 +1,23 @@
+// QueueCallback.h - I/O Queue 回调处理
+// 职责:处理从 DLL 发来的 IOCTL 请求
+// 使用 WDF I/O Queue 简化并发管理
+#pragma once
+
+#include <ntddk.h>
+#include <wdf.h>
+#include "DeviceContext.h"
+
+namespace commkit_driver {
+
+// 创建控制设备并配置 I/O Queue
+NTSTATUS CreateControlDevice(WDFDRIVER driver);
+
+// EvtIoDeviceControl 回调:处理 IOCTL
+VOID EvtIoDeviceControl(WDFQUEUE queue, WDFREQUEST request,
+                          size_t output_buffer_length, size_t input_buffer_length,
+                          ULONG io_control_code);
+
+// EvtIoDefault 回调(处理非 IOCTL 请求,仅完成)
+VOID EvtIoDefault(WDFQUEUE queue, WDFREQUEST request);
+
+} // namespace commkit_driver

+ 141 - 0
driver/SerialFilter.cpp

@@ -0,0 +1,141 @@
+// SerialFilter.cpp - 串口 IRP 拦截实现
+// 注意:完整 KMDF 上层过滤驱动需要 IoAttachDeviceToDeviceStack + EvtDeviceAdd
+// 本文件实现 IRP 派遣与完成例程,CreateAndAttach 为简化框架
+#include <ntddk.h>
+#include <wdf.h>
+#include "SerialFilter.h"
+#include "EventRingBuffer.h"
+#include "ClientConnection.h"
+#include "../common/CommKitEvents.h"
+
+namespace commkit_driver {
+
+// 全局原子序列号(每个事件递增)
+static LONG g_sequence_counter = 0;
+
+// 获取当前 OLE Automation 日期(简化实现:返回 0,由 DLL 转换为当前时间)
+static DOUBLE GetCurrentTimeStamp() {
+    return 0.0;
+}
+
+NTSTATUS SerialFilter::CreateAndAttach(WDFDRIVER driver,
+                                         PDEVICE_OBJECT serial_pdo,
+                                         ULONG com_number,
+                                         WDFDEVICE* out_filter_device) {
+    UNREFERENCED_PARAMETER(driver);
+
+    // 简化实现:完整的 KMDF 上层过滤驱动需要:
+    // 1. WdfDeviceInitAssignName / WdfDeviceInitSetFileObjectConfig
+    // 2. WdfDeviceCreate
+    // 3. IoAttachDeviceToDeviceStack (WDM attach)
+    // 此处给出框架,实际完成时按需补充
+    *out_filter_device = nullptr;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+void SerialFilter::CaptureIrpData(PDEVICE_CONTEXT ctx, PIRP irp, ULONG event_type) {
+    if (!ctx || !ctx->MonitoringEnabled || !ctx->RingBuffer) {
+        return;
+    }
+
+    PIO_STACK_LOCATION sl = IoGetCurrentIrpStackLocation(irp);
+    if (!sl) return;
+
+    // 仅在 IRP 成功完成时捕获
+    if (!NT_SUCCESS(irp->IoStatus.Status)) {
+        return;
+    }
+
+    ULONG data_size = 0;
+    PVOID data_ptr = nullptr;
+
+    // 根据 IRP 类型提取数据
+    if (event_type == COMMKIT_OP_READ) {
+        data_size = sl->Parameters.Read.Length;
+        // 实际读取的字节数(IRP 完成后 Information = 读取字节数)
+        data_size = (ULONG)irp->IoStatus.Information;
+        if (data_size == 0) return;
+        // 数据可能在 SystemBuffer / MdlAddress / UserBuffer
+        if (irp->MdlAddress) {
+            data_ptr = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
+        } else if (irp->AssociatedIrp.SystemBuffer) {
+            data_ptr = irp->AssociatedIrp.SystemBuffer;
+        }
+    } else if (event_type == COMMKIT_OP_WRITE) {
+        data_size = (ULONG)irp->IoStatus.Information;
+        if (data_size == 0) {
+            data_size = sl->Parameters.Write.Length;
+        }
+        if (data_size == 0) return;
+        if (irp->MdlAddress) {
+            data_ptr = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
+        } else if (irp->AssociatedIrp.SystemBuffer) {
+            data_ptr = irp->AssociatedIrp.SystemBuffer;
+        }
+    }
+
+    if (!data_ptr || data_size == 0) {
+        // OP_OPEN / OP_CLOSE 事件无数据,也推送
+        if (event_type != COMMKIT_OP_READ && event_type != COMMKIT_OP_WRITE) {
+            // 落空,继续推送无数据事件
+        } else {
+            return;
+        }
+    }
+
+    // 推入环形缓冲
+    LONG seq = InterlockedIncrement(&g_sequence_counter);
+    auto* ring = (EventRingBuffer*)ctx->RingBuffer;
+    ring->Push((ULONG)seq, GetCurrentTimeStamp(),
+                ctx->ComNumber, event_type,
+                data_size, data_ptr);
+}
+
+NTSTATUS SerialFilter::OnReadComplete(PDEVICE_OBJECT dev, PIRP irp, PVOID ctx) {
+    UNREFERENCED_PARAMETER(dev);
+    PDEVICE_CONTEXT device_ctx = (PDEVICE_CONTEXT)ctx;
+    if (device_ctx) {
+        CaptureIrpData(device_ctx, irp, COMMKIT_OP_READ);
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS SerialFilter::OnWriteComplete(PDEVICE_OBJECT dev, PIRP irp, PVOID ctx) {
+    UNREFERENCED_PARAMETER(dev);
+    PDEVICE_CONTEXT device_ctx = (PDEVICE_CONTEXT)ctx;
+    if (device_ctx) {
+        CaptureIrpData(device_ctx, irp, COMMKIT_OP_WRITE);
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS SerialFilter::DispatchRead(PDEVICE_OBJECT dev, PIRP irp) {
+    PDEVICE_CONTEXT ctx = (PDEVICE_CONTEXT)dev->DeviceExtension;
+    if (ctx && ctx->MonitoringEnabled) {
+        IoMarkIrpPending(irp);
+        IoSetCompletionRoutine(irp, OnReadComplete, ctx, TRUE, TRUE, TRUE);
+    }
+    return IoCallDriver(ctx->LowerDevice, irp);
+}
+
+NTSTATUS SerialFilter::DispatchWrite(PDEVICE_OBJECT dev, PIRP irp) {
+    PDEVICE_CONTEXT ctx = (PDEVICE_CONTEXT)dev->DeviceExtension;
+    if (ctx && ctx->MonitoringEnabled) {
+        IoMarkIrpPending(irp);
+        IoSetCompletionRoutine(irp, OnWriteComplete, ctx, TRUE, TRUE, TRUE);
+    }
+    return IoCallDriver(ctx->LowerDevice, irp);
+}
+
+NTSTATUS SerialFilter::DispatchDeviceControl(PDEVICE_OBJECT dev, PIRP irp) {
+    PDEVICE_CONTEXT ctx = (PDEVICE_CONTEXT)dev->DeviceExtension;
+    if (!ctx) {
+        IoCompleteRequest(irp, STATUS_INVALID_DEVICE_REQUEST);
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+    // 透传 DeviceControl 给下层
+    IoSkipCurrentIrpStackLocation(irp);
+    return IoCallDriver(ctx->LowerDevice, irp);
+}
+
+} // namespace commkit_driver

+ 40 - 0
driver/SerialFilter.h

@@ -0,0 +1,40 @@
+// SerialFilter.h - 串口 IRP 拦截
+// 职责:作为上层过滤驱动 attach 到串口设备栈
+//      拦截 IRP_MJ_READ / IRP_MJ_WRITE / IRP_MJ_DEVICE_CONTROL
+//      将捕获的数据推入对应端口的环形缓冲
+#pragma once
+
+#include <ntddk.h>
+#include <wdf.h>
+#include "DeviceContext.h"
+
+namespace commkit_driver {
+
+class SerialFilter {
+public:
+    // 创建过滤设备并 attach 到串口设备栈
+    // parent_serial_pdo:下层串口的 PDO
+    // 返回 STATUS_SUCCESS + 设置 *filter_device
+    static NTSTATUS CreateAndAttach(WDFDRIVER driver,
+                                      PDEVICE_OBJECT serial_pdo,
+                                      ULONG com_number,
+                                      WDFDEVICE* out_filter_device);
+
+    // 拦截 IRP_MJ_READ 完成:捕获数据并推入环形缓冲
+    static NTSTATUS OnReadComplete(PDEVICE_OBJECT dev, PIRP irp, PVOID ctx);
+
+    // 拦截 IRP_MJ_WRITE 完成:捕获数据并推入环形缓冲
+    static NTSTATUS OnWriteComplete(PDEVICE_OBJECT dev, PIRP irp, PVOID ctx);
+
+    // IRP 派遣例程:透传到下层 + 注册完成例程
+    static NTSTATUS DispatchRead(PDEVICE_OBJECT dev, PIRP irp);
+    static NTSTATUS DispatchWrite(PDEVICE_OBJECT dev, PIRP irp);
+    static NTSTATUS DispatchDeviceControl(PDEVICE_OBJECT dev, PIRP irp);
+
+private:
+    // 从 IRP 中提取数据并推入环形缓冲
+    // event_type:COMMKIT_OP_READ 或 COMMKIT_OP_WRITE
+    static void CaptureIrpData(PDEVICE_CONTEXT ctx, PIRP irp, ULONG event_type);
+};
+
+} // namespace commkit_driver

+ 135 - 0
installer/install.ps1

@@ -0,0 +1,135 @@
+# install.ps1 - CommModifyKit 驱动 + CommModifyKit.dll 安装脚本
+# 必须以管理员权限运行
+# 用法:以管理员身份打开 PowerShell,执行 .\install.ps1
+
+#Requires -Version 5.1
+#Requires -RunAsAdministrator
+
+$ErrorActionPreference = 'Stop'
+
+$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
+$SolutionRoot = Split-Path -Parent $ScriptDir
+
+Write-Host '=== CommModifyKit 安装脚本 ===' -ForegroundColor Cyan
+Write-Host "脚本目录: $ScriptDir"
+Write-Host "项目根目录: $SolutionRoot"
+Write-Host ""
+
+# ============================================================================
+# 1. 查找构建产物
+# ============================================================================
+$DriverSysCandidates = @(
+    Join-Path $SolutionRoot "build\Release\x64\CommModifyKit.sys"
+    Join-Path $SolutionRoot "build\Debug\x64\CommModifyKit.sys"
+    Join-Path $ScriptDir "CommModifyKit.sys"
+)
+$DriverInfCandidates = @(
+    Join-Path $SolutionRoot "build\Release\x64\CommModifyKit.inf"
+    Join-Path $SolutionRoot "build\Debug\x64\CommModifyKit.inf"
+    Join-Path $ScriptDir "..\driver\CommModifyKit.inf"
+    Join-Path $ScriptDir "CommModifyKit.inf"
+)
+$DllCandidates = @(
+    Join-Path $SolutionRoot "build\Release\Win32\CommModifyKit.dll"
+    Join-Path $SolutionRoot "build\Debug\Win32\CommModifyKit.dll"
+    Join-Path $SolutionRoot "build\Release\x64\CommModifyKit.dll"
+    Join-Path $SolutionRoot "build\Debug\x64\CommModifyKit.dll"
+)
+
+$DriverSys = $DriverSysCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
+$DriverInf = $DriverInfCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
+$DllPath = $DllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
+
+if (-not $DriverSys) {
+    Write-Error "未找到 CommModifyKit.sys,请先用 WDK + VS2019 编译 driver 项目"
+    exit 1
+}
+if (-not $DriverInf) {
+    Write-Error "未找到 CommModifyKit.inf"
+    exit 1
+}
+Write-Host "驱动 .sys: $DriverSys" -ForegroundColor Green
+Write-Host "驱动 .inf: $DriverInf" -ForegroundColor Green
+if ($DllPath) {
+    Write-Host "用户态 DLL: $DllPath" -ForegroundColor Green
+} else {
+    Write-Warning "未找到 CommModifyKit.dll(可后续单独编译 dll 项目)"
+}
+Write-Host ""
+
+# ============================================================================
+# 2. 拷贝驱动文件到 System32\drivers
+# ============================================================================
+$TargetDriverDir = Join-Path $env:windir "System32\drivers"
+$TargetDriverSys = Join-Path $TargetDriverDir "CommModifyKit.sys"
+$TargetDriverInf = Join-Path $TargetDriverDir "CommModifyKit.inf"
+
+Write-Host "拷贝驱动文件到 $TargetDriverDir ..."
+Copy-Item -Force $DriverSys $TargetDriverSys
+Copy-Item -Force $DriverInf $TargetDriverInf
+Write-Host "  完成" -ForegroundColor Green
+
+# ============================================================================
+# 3. 创建并启动驱动服务
+# ============================================================================
+$ServiceName = 'CommModifyKit'
+$ExistingService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
+
+if ($ExistingService) {
+    Write-Host "服务 $ServiceName 已存在,状态: $($ExistingService.Status)"
+    if ($ExistingService.Status -ne 'Running') {
+        Write-Host "启动服务..."
+        Start-Service -Name $ServiceName
+        Write-Host "  完成" -ForegroundColor Green
+    }
+} else {
+    Write-Host "创建服务 $ServiceName ..."
+    sc.exe create $ServiceName binPath= $TargetDriverSys type= kernel start= demand error= normal | Out-Null
+    Write-Host "  完成" -ForegroundColor Green
+    Write-Host "启动服务..."
+    sc.exe start $ServiceName | Out-Null
+    Write-Host "  完成" -ForegroundColor Green
+}
+
+# ============================================================================
+# 4. 拷贝 DLL 到 CommModifyService 输出目录(如果检测到)
+# ============================================================================
+if ($DllPath) {
+    $ServiceExeCandidates = @(
+        Join-Path $SolutionRoot "..\CommModifyService\x64\Release\CommModifyService.exe"
+        Join-Path $SolutionRoot "..\CommModifyService\Win32\Release\CommModifyService.exe"
+        Join-Path $SolutionRoot "..\CommModifyService\x64\Debug\CommModifyService.exe"
+        Join-Path $SolutionRoot "..\CommModifyService\Win32\Debug\CommModifyService.exe"
+    )
+    $ServiceExe = $ServiceExeCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
+    if ($ServiceExe) {
+        $ServiceDir = Split-Path -Parent $ServiceExe
+        $TargetDll = Join-Path $ServiceDir "CommModifyKit.dll"
+        Write-Host "拷贝 DLL 到 $TargetDll ..."
+        Copy-Item -Force $DllPath $TargetDll
+        Write-Host "  完成" -ForegroundColor Green
+    } else {
+        Write-Warning "未检测到 CommModifyService.exe,请手动拷贝 CommModifyKit.dll 到其目录"
+    }
+}
+
+# ============================================================================
+# 5. 启用测试签名(开发期需要)
+# ============================================================================
+$TestSigningEnabled = (bcdedit /enum | Select-String 'testsigning' -Quiet)
+if (-not $TestSigningEnabled) {
+    Write-Host ""
+    Write-Warning "检测到测试签名未启用,驱动可能无法加载"
+    Write-Host "执行: bcdedit /set testsigning on" -ForegroundColor Yellow
+    bcdedit /set testsigning on | Out-Null
+    Write-Warning "测试签名已启用,需要重启计算机才能生效"
+} else {
+    Write-Host "测试签名已启用" -ForegroundColor Green
+}
+
+Write-Host ""
+Write-Host '=== 安装完成 ===' -ForegroundColor Cyan
+Write-Host "请确认:"
+Write-Host "  1. 计算机已重启(启用测试签名后)"
+Write-Host "  2. CommModifyKit.dll 已拷贝到 CommModifyService.exe 所在目录"
+Write-Host "  3. config.json 中 'comm.use_dll' 设为 true"

+ 78 - 0
installer/uninstall.ps1

@@ -0,0 +1,78 @@
+# uninstall.ps1 - CommModifyKit 驱动 + CommModifyKit.dll 卸载脚本
+# 必须以管理员权限运行
+#Requires -Version 5.1
+#Requires -RunAsAdministrator
+
+$ErrorActionPreference = 'Continue'
+
+Write-Host '=== CommModifyKit 卸载脚本 ===' -ForegroundColor Cyan
+
+# ============================================================================
+# 1. 停止并删除驱动服务
+# ============================================================================
+$ServiceName = 'CommModifyKit'
+$ExistingService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
+
+if ($ExistingService) {
+    Write-Host "停止服务 $ServiceName ..."
+    if ($ExistingService.Status -eq 'Running') {
+        sc.exe stop $ServiceName | Out-Null
+        Start-Sleep -Seconds 2
+    }
+    Write-Host "删除服务 $ServiceName ..."
+    sc.exe delete $ServiceName | Out-Null
+    Write-Host "  完成" -ForegroundColor Green
+} else {
+    Write-Host "服务 $ServiceName 不存在,跳过"
+}
+
+# ============================================================================
+# 2. 删除驱动文件
+# ============================================================================
+$TargetDriverSys = Join-Path $env:windir "System32\drivers\CommModifyKit.sys"
+$TargetDriverInf = Join-Path $env:windir "System32\drivers\CommModifyKit.inf"
+
+if (Test-Path $TargetDriverSys) {
+    Write-Host "删除 $TargetDriverSys ..."
+    Remove-Item -Force $TargetDriverSys
+    Write-Host "  完成" -ForegroundColor Green
+}
+if (Test-Path $TargetDriverInf) {
+    Write-Host "删除 $TargetDriverInf ..."
+    Remove-Item -Force $TargetDriverInf
+    Write-Host "  完成" -ForegroundColor Green
+}
+
+# ============================================================================
+# 3. 通过 pnputil 移除驱动包(如果存在)
+# ============================================================================
+Write-Host ""
+Write-Host "尝试通过 pnputil 移除驱动包..."
+try {
+    $Drivers = pnputil /enum-drivers | Select-String 'CommModifyKit' -Context 1,0
+    if ($Drivers) {
+        $Drivers | ForEach-Object {
+            Write-Host "  发现: $_"
+        }
+        Write-Warning "请手动执行 pnputil /delete-driver oem*.inf /uninstall /force 移除"
+    } else {
+        Write-Host "  无已安装的驱动包" -ForegroundColor Green
+    }
+} catch {
+    Write-Warning "pnputil 检查失败: $_"
+}
+
+# ============================================================================
+# 4. 关闭测试签名(可选,谨慎执行)
+# ============================================================================
+Write-Host ""
+Write-Host "测试签名状态检查..."
+$TestSigning = bcdedit /enum | Select-String 'testsigning.*yes' -Quiet
+if ($TestSigning) {
+    Write-Warning "测试签名当前已启用"
+    Write-Host "如需关闭: bcdedit /set testsigning off(重启生效)"
+}
+
+Write-Host ""
+Write-Host '=== 卸载完成 ===' -ForegroundColor Cyan
+Write-Host "如需删除 DLL,请手动删除 CommModifyService 目录下的 CommModifyKit.dll"