소스 검색

完成驱动完整功能

Gogs 3 주 전
부모
커밋
bf007f1772

+ 3 - 0
.gitignore

@@ -1,2 +1,5 @@
 /build
+/.vscode
 /.vs
+driver/build
+dll/build

+ 4 - 4
dll/CommModifyKit.vcxproj

@@ -30,14 +30,14 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v145</PlatformToolset>
+    <PlatformToolset>v142</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   <!-- Release|Win32 -->
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v145</PlatformToolset>
+    <PlatformToolset>v142</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
@@ -45,14 +45,14 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v145</PlatformToolset>
+    <PlatformToolset>v142</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   <!-- Release|x64 -->
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v145</PlatformToolset>
+    <PlatformToolset>v142</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>

+ 5 - 0
dll/EventLoop.cpp

@@ -98,6 +98,11 @@ void EventLoop::Run() {
                           const_cast<char*>(e.Data));
             }
         }
+
+        // 无事件时短暂 sleep,避免忙循环占满 CPU(驱动当前实现是立即返回而非 pending IRP)
+        if (count == 0) {
+            std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        }
     }
 }
 

+ 4 - 0
dll/MonitorManager.cpp

@@ -144,6 +144,10 @@ bool MonitorManager::EnsureDriverReady() {
 bool MonitorManager::InitMonitor(const std::wstring& key, TOnData callback) {
     std::lock_guard<std::mutex> lock(mutex_);
 
+    // 启用文件日志,便于诊断(输出到 %TEMP%\CommModifyKit.log)
+    Logger::Instance().EnableFileOutput(true);
+    Logger::Instance().SetLevel(Logger::Level::Debug);
+
     OutputDebugStringA("[MonitorManager::InitMonitor] enter");
 
     if (initialized_) {

+ 33 - 6
docs/CommModifyKit-Design.md

@@ -246,6 +246,19 @@ while (running_) {
 - 调用 `CreateControlDevice()` 创建控制设备 `\\.\CommModifyKit`
 - 注册 `EvtDeviceAdd`(PnP 过滤设备创建)和 `EvtDriverUnload`
 
+**EvtDeviceAdd 实现**(核心):
+1. 调用 `WdfFdoInitSetFilter()` 标记为过滤设备
+2. 注册 `EvtDevicePrepareHardware` 回调(解析 COM 编号)
+3. 调用 `WdfDeviceInitAssignWdmIrpPreprocessCallback` 注册 IRP_MJ_READ/WRITE/DEVICE_CONTROL 预处理回调
+4. 调用 `WdfDeviceCreate()` 创建 WDF 过滤设备
+5. 初始化 `DEVICE_CONTEXT`(ComNumber=0,由 PrepareHardware 解析)
+6. 调用 `RegisterFilterDevice()` 注册到全局端口表
+
+**EvtDevicePrepareHardware 实现**:
+- 从 `DevicePropertyFriendlyName` 或 `DevicePropertyDeviceDescription` 解析 COM 编号
+- 支持多种格式:"COMx (port)"、"通信端口 (COMx)"、"Serial Port (COMx)"
+- 调用 `UpdatePortComNumber()` 更新端口表
+
 #### 3.2.2 ClientConnection(连接管理)
 
 **职责**:维护 DLL 与驱动之间的连接状态和端口监控列表。
@@ -256,11 +269,16 @@ while (running_) {
 - **同步**:`KSPIN_LOCK` 保护端口表(IRQL 安全)
 
 **HandleAttachPort 流程**:
-1. 查找端口上下文,不存在返回 `STATUS_DEVICE_DOES_NOT_EXIST`
+1. 查找端口上下文(通过 COM 编号在 `ports_table_` 中查找)
 2. 若端口无环形缓冲,分配并初始化
 3. 推入 `OP_OPEN` 事件
 4. 设置 `MonitoringEnabled = TRUE`
 
+**端口上下文来源**:
+- 驱动作为 Upper Filters 自动加载到串口设备栈
+- `EvtDeviceAdd` 为每个串口创建过滤设备并注册到 `ports_table_`(ComNumber=0)
+- `EvtDevicePrepareHardware` 从设备属性解析 COM 编号并更新端口表
+
 **HandleReadEvents 流程**:
 1. 遍历所有已启用端口的环形缓冲
 2. 逐端口 `PopBatch()` 弹出事件到用户缓冲
@@ -284,11 +302,20 @@ WdfControlDeviceInitAllocate(SDDL_DEVOBJ_SYS_ONLY_ADM)  // 仅管理员可访问
 
 **职责**:作为上层过滤驱动 attach 到串口设备栈,拦截读写 IRP。
 
-| 派遣例程 | 行为 |
-|---------|------|
-| `DispatchRead` | 标记 IRP Pending → 设置 `OnReadComplete` 完成例程 → `IoCallDriver` 下层 |
-| `DispatchWrite` | 标记 IRP Pending → 设置 `OnWriteComplete` 完成例程 → `IoCallDriver` 下层 |
-| `DispatchDeviceControl` | `IoSkipCurrentIrpStackLocation` → `IoCallDriver` 透传 |
+**KMDF WDM IRP 预处理模式**:
+通过 `WdfDeviceInitAssignWdmIrpPreprocessCallback` 在 `EvtDeviceAdd` 中注册。
+
+| 预处理回调 | 行为 |
+|-----------|------|
+| `DispatchRead(WDFDEVICE, PIRP)` | 注册 `OnReadComplete` 完成例程 → `WdfDeviceWdmDispatchPreprocessedIrp` |
+| `DispatchWrite(WDFDEVICE, PIRP)` | 注册 `OnWriteComplete` 完成例程 → `WdfDeviceWdmDispatchPreprocessedIrp` |
+| `DispatchDeviceControl(WDFDEVICE, PIRP)` | 直接 `WdfDeviceWdmDispatchPreprocessedIrp` 透传 |
+
+**完成例程**(OnReadComplete/OnWriteComplete):
+- 从 IRP 提取数据指针(`MdlAddress` 或 `SystemBuffer`)
+- 从 `IoStatus.Information` 获取实际长度
+- 调用 `EventRingBuffer.Push()` 写入环形缓冲
+- 返回 `STATUS_SUCCESS` 继续完成 IRP
 
 **CaptureIrpData**(IRP 完成后提取数据):
 - 从 IRP 的 `MdlAddress` 或 `SystemBuffer` 获取数据指针

+ 31 - 27
docs/CommModifyKit-Distribution.md

@@ -161,17 +161,15 @@ cd C:\CommModifyKit
 驱动 .inf: C:\CommModifyKit\CommModifyKit.inf
 用户态 DLL: C:\CommModifyKit\CommModifyKit.dll
 
-拷贝驱动文件到 C:\Windows\System32\drivers ...
-  完成
-创建服务 CommModifyKit ...
-  完成
-启动服务...
+添加驱动包...
   完成
 测试签名已启用,需要重启计算机才能生效
 
 === 安装完成 ===
 ```
 
+> **说明**:驱动使用 PnP 方式安装(`pnputil /add-driver`),自动注册为 Ports 类的 UpperFilters。
+
 ### 步骤 4:重启计算机
 
 **首次安装必须重启**(启用测试签名后生效)。
@@ -183,9 +181,8 @@ Restart-Computer
 ### 步骤 5:重启后验证驱动
 
 ```powershell
-# 检查驱动服务状态
-sc.exe query CommModifyKit
-# 期望:STATE = 4 RUNNING
+# 检查驱动是否在驱动库中
+pnputil /enum-drivers | Select-String "CommModifyKit"
 
 # 检查测试签名
 bcdedit /enum | Select-String "testsigning"
@@ -194,6 +191,10 @@ bcdedit /enum | Select-String "testsigning"
 # 检查驱动文件
 Test-Path "$env:windir\System32\drivers\CommModifyKit.sys"
 # 期望:True
+
+# 在设备管理器中确认串口设备有 CommModifyKit 驱动
+# 设备管理器 → 端口 (COM 和 LPT) → 右键串口 → 属性 → 驱动程序
+# 应显示 CommModifyKit 作为过滤驱动
 ```
 
 ### 步骤 6:拷贝 DLL 到 CommModifyService 目录
@@ -239,24 +240,20 @@ Start-Service CommModifyService
 ## 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. 启用测试签名
+# 1. 启用测试签名
 bcdedit /set testsigning on
 
-# 4. 重启
-Restart-Computer
+# 2. 使用 pnputil 安装驱动(PnP 方式)
+cd C:\CommModifyKit
+pnputil /add-driver CommModifyKit.inf /install
+
+# 3. 扫描硬件改动(触发 PnP 重新加载过滤驱动)
+# 设备管理器 → 操作 → 扫描硬件改动
 
-# 5. 重启后启动驱动
-sc.exe start CommModifyKit
+# 4. 验证安装
+pnputil /enum-drivers | Select-String "CommModifyKit"
 
-# 6. 拷贝 DLL
+# 5. 拷贝 DLL
 Copy-Item C:\CommModifyKit\CommModifyKit.dll "C:\Program Files\CommModifyService\"
 ```
 
@@ -274,13 +271,20 @@ cd C:\CommModifyKit
 ### 手动卸载
 
 ```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
+# 1. 找到并删除驱动包
+pnputil /enum-drivers | Select-String "CommModifyKit"
+# 假设显示 oem12.inf
+
+pnputil /delete-driver oem12.inf /uninstall /force
+
+# 2. 删除驱动文件
+Remove-Item "$env:windir\System32\drivers\CommModifyKit.sys" -Force -ErrorAction SilentlyContinue
+Remove-Item "$env:windir\System32\drivers\CommModifyKit.inf" -Force -ErrorAction SilentlyContinue
+
+# 3. 删除 DLL
 Remove-Item "C:\Program Files\CommModifyService\CommModifyKit.dll" -Force
 
-# 可选:关闭测试签名
+# 4. 可选:关闭测试签名
 bcdedit /set testsigning off
 Restart-Computer
 ```

+ 30 - 24
docs/CommModifyKit-Usage.md

@@ -45,33 +45,33 @@ cd CommModifyKit\installer
 
 脚本自动完成:
 1. 查找构建产物(.sys、.inf、.dll)
-2. 拷贝驱动文件到 `%SystemRoot%\System32\drivers\`
-3. 创建并启动驱动服务
-4. 拷贝 DLL 到 CommModifyService 目录(如检测到)
-5. 启用测试签名(开发阶段需要,需重启生效)
+2. 使用 `pnputil` 安装驱动(PnP 方式,注册为 Upper Filter)
+3. 拷贝 DLL 到 CommModifyService 目录(如检测到)
+4. 启用测试签名(开发阶段需要,需重启生效)
 
 **安装后确认**:
 - 计算机已重启(启用测试签名后)
 - `CommModifyKit.dll` 已拷贝到 `CommModifyService.exe` 所在目录
 - `config.json` 中 `comm.use_dll` 设为 `true`
 
+> **重要**:驱动作为串口设备类的 Upper Filters 安装,当 PnP 管理器枚举串口设备时,驱动会自动创建过滤设备并 attach 到设备栈。
+
 ### 3.2 手动安装
 
 #### 安装驱动
 
 ```powershell
-# 1. 拷贝驱动文件
-copy CommModifyKit.sys %SystemRoot%\System32\drivers\
-copy CommModifyKit.inf %SystemRoot%\System32\drivers\
+# 1. 启用测试签名(首次需要,重启生效)
+bcdedit /set testsigning on
 
-# 2. 创建驱动服务
-sc.exe create CommModifyKit binPath= "%SystemRoot%\System32\drivers\CommModifyKit.sys" type= kernel start= demand error= normal
+# 2. 使用 pnputil 安装驱动(PnP 方式)
+pnputil /add-driver CommModifyKit.inf /install
 
-# 3. 启动驱动服务
-sc.exe start CommModifyKit
+# 3. 扫描硬件改动(触发 PnP 重新加载过滤驱动)
+# 设备管理器 → 操作 → 扫描硬件改动
 
-# 4. 启用测试签名(首次需要,重启生效)
-bcdedit /set testsigning on
+# 4. 验证驱动安装
+pnputil /enum-drivers | Select-String "CommModifyKit"
 ```
 
 #### 安装 DLL
@@ -81,14 +81,17 @@ bcdedit /set testsigning on
 ### 3.3 验证安装
 
 ```powershell
-# 检查驱动服务状态
-sc.exe query CommModifyKit
+# 检查驱动是否在驱动库中
+pnputil /enum-drivers | Select-String "CommModifyKit"
 
 # 检查测试签名
 bcdedit /enum | Select-String "testsigning"
 
 # 检查 DLL 是否在目标目录
 dir CommModifyService\Release\CommModifyKit.dll
+
+# 检查串口设备是否有上层过滤驱动
+Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Enum\Root\PORTS\*\Device Parameters" -ErrorAction SilentlyContinue | Select-Object "UpperFilters"
 ```
 
 ---
@@ -105,18 +108,19 @@ cd CommModifyKit\installer
 ```
 
 脚本自动完成:
-1. 停止并删除驱动服务
-2. 删除 `%SystemRoot%\System32\drivers\` 下的 .sys 和 .inf 文件
-3. 检查并提示通过 `pnputil` 移除驱动包
+1. 使用 `pnputil` 删除驱动包
+2. 删除驱动文件
+3. 提示关闭测试签名(可选)
 
 ### 4.2 手动卸载
 
 ```powershell
-# 1. 停止驱动服务
-sc.exe stop CommModifyKit
+# 1. 删除驱动包(找到 oem 编号)
+pnputil /enum-drivers | Select-String "CommModifyKit"
+# 假设是 oem12.inf
 
-# 2. 删除驱动服务
-sc.exe delete CommModifyKit
+# 2. 删除驱动
+pnputil /delete-driver oem12.inf /uninstall /force
 
 # 3. 删除驱动文件
 del %SystemRoot%\System32\drivers\CommModifyKit.sys
@@ -373,8 +377,10 @@ MonitorManager 中关键流程节点输出 `OutputDebugStringA` 诊断信息,
 
 **解决**:
 1. 确认该 COM 端口存在且未被占用
-2. 确认驱动已作为 Upper Filter 正确挂载到串口设备栈
-3. 检查设备管理器中串口属性 → "驱动程序" → "详细设置" → "上层筛选器"
+2. 确认驱动已作为 Upper Filter 正确安装:`pnputil /enum-drivers | Select-String "CommModifyKit"`
+3. 在设备管理器中检查串口设备属性 → "驱动程序" 选项卡,确认显示 CommModifyKit
+4. 若未显示,尝试"扫描检测硬件改动"或重新插拔串口设备
+5. 检查驱动的 COM 编号解析是否成功(从 FriendlyName 解析)
 
 #### 回调不触发
 

+ 59 - 15
driver/ClientConnection.cpp

@@ -36,16 +36,16 @@ void ClientConnection::Cleanup() {
     KeReleaseSpinLock(&ports_lock_, old_irql);
 }
 
-void ClientConnection::RegisterFilterDevice(ULONG com_number, WDFDEVICE device) {
+void ClientConnection::RegisterFilterDevice(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_].ComNumber = 0;  // 延迟到 UpdatePortComNumber
         ports_table_[ports_count_].Context = ctx;
         ports_count_++;
-        ctx->ComNumber = com_number;
+        ctx->ComNumber = 0;
         ctx->MonitoringEnabled = FALSE;
         ctx->WdfDevice = device;
     }
@@ -53,6 +53,22 @@ void ClientConnection::RegisterFilterDevice(ULONG com_number, WDFDEVICE device)
     KeReleaseSpinLock(&ports_lock_, old_irql);
 }
 
+void ClientConnection::UpdatePortComNumber(WDFDEVICE device, ULONG com_number) {
+    KIRQL old_irql;
+    KeAcquireSpinLock(&ports_lock_, &old_irql);
+
+    for (ULONG i = 0; i < ports_count_; ++i) {
+        if (ports_table_[i].Context &&
+            ports_table_[i].Context->WdfDevice == device) {
+            ports_table_[i].ComNumber = com_number;
+            ports_table_[i].Context->ComNumber = com_number;
+            break;
+        }
+    }
+
+    KeReleaseSpinLock(&ports_lock_, old_irql);
+}
+
 void ClientConnection::UnregisterFilterDevice(ULONG com_number) {
     KIRQL old_irql;
     KeAcquireSpinLock(&ports_lock_, &old_irql);
@@ -150,27 +166,55 @@ NTSTATUS ClientConnection::HandleDetachPort(ULONG com_number) {
 }
 
 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) {
+    if (!ctx || !ctx->LowerDevice) {
         return STATUS_DEVICE_DOES_NOT_EXIST;
     }
-    // 注:完整的写数据流通过过滤驱动 IRP_MJ_WRITE 派遣完成
-    // 这里仅返回成功;实际数据发送由用户态通过 CreateFile(COMx) 直接写入
-    return STATUS_SUCCESS;
+
+    // 构造同步写 IRP 发送给下层串口设备
+    KEVENT event;
+    KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+    IO_STATUS_BLOCK io_status = {};
+    PIRP irp = IoBuildSynchronousFsdRequest(
+        IRP_MJ_WRITE, ctx->LowerDevice, data, len, nullptr, &event, &io_status);
+    if (!irp) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    NTSTATUS status = IoCallDriver(ctx->LowerDevice, irp);
+    if (status == STATUS_PENDING) {
+        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, nullptr);
+        status = io_status.Status;
+    }
+
+    return status;
 }
 
 NTSTATUS ClientConnection::HandleReadPort(ULONG com_number, PVOID data, ULONG len) {
-    UNREFERENCED_PARAMETER(data);
-    UNREFERENCED_PARAMETER(len);
     PDEVICE_CONTEXT ctx = FindPortContext(com_number);
-    if (!ctx) {
+    if (!ctx || !ctx->LowerDevice) {
         return STATUS_DEVICE_DOES_NOT_EXIST;
     }
-    return STATUS_SUCCESS;
+
+    // 构造同步读 IRP 发送给下层串口设备
+    KEVENT event;
+    KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+    IO_STATUS_BLOCK io_status = {};
+    PIRP irp = IoBuildSynchronousFsdRequest(
+        IRP_MJ_READ, ctx->LowerDevice, data, len, nullptr, &event, &io_status);
+    if (!irp) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    NTSTATUS status = IoCallDriver(ctx->LowerDevice, irp);
+    if (status == STATUS_PENDING) {
+        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, nullptr);
+        status = io_status.Status;
+    }
+
+    return status;
 }
 
 NTSTATUS ClientConnection::HandleReadEvents(PVOID out_buf, ULONG out_size, PULONG returned) {

+ 6 - 3
driver/ClientConnection.h

@@ -44,9 +44,12 @@ public:
     // FREE_ALL:停止所有监控
     NTSTATUS HandleFreeAll();
 
-    // 注册一个过滤设备(当 SerialFilter 创建时调用)
-    // 将 com_number ↔ WDFDEVICE 关联存入内部表
-    void RegisterFilterDevice(ULONG com_number, WDFDEVICE device);
+    // 注册一个过滤设备(当 EvtDeviceAdd 创建时调用)
+    // com_number 暂为 0,由 UpdatePortComNumber 更新
+    void RegisterFilterDevice(WDFDEVICE device);
+
+    // 更新端口表中设备的 COM 编号(EvtDevicePrepareHardware 解析后调用)
+    void UpdatePortComNumber(WDFDEVICE device, ULONG com_number);
 
     // 注销一个过滤设备
     void UnregisterFilterDevice(ULONG com_number);

+ 14 - 10
driver/CommModifyKit.inf

@@ -6,7 +6,7 @@ Signature   = "$Windows NT$"
 Class       = Ports
 ClassGuid   = {4D36E978-E325-11CE-BFC1-08002BE10318}
 Provider    = %ProviderName%
-DriverVer   = 07/19/2026,1.0.0.0
+DriverVer   = 07/22/2026,1.0.0.1
 CatalogFile = CommModifyKit.cat
 PnpLockdown = 1
 
@@ -17,26 +17,30 @@ PnpLockdown = 1
 CommModifyKit.sys = 1,,
 
 [DestinationDirs]
-CommModifyKit_CopyFiles = 12   ; %windir%\System32\drivers
+DefaultDestDir = 12   ; %windir%\System32\drivers
 
 ; ============================================================================
-; 默认安装节(按需扩展为 NTamd64
+; DDInstall 节(PnP 安装
 ; ============================================================================
-[DefaultInstall.NTamd64]
-CopyFiles  = CommModifyKit_CopyFiles
+[DDInstall.NTamd64]
+CopyFiles = CommModifyKit_CopyFiles
 
-[DefaultInstall.NTamd64.Services]
+[DDInstall.NTamd64.Services]
 AddService = CommModifyKit,,CommModifyKit_Service_Inst
 
 ; ============================================================================
-; 文件复制节
+; 注册为 UpperFilters
+; 在设备类注册表中添加 UpperFilters = CommModifyKit
 ; ============================================================================
+[DDInstall.NTamd64.HW]
+AddReg = CommModifyKit_AddReg
+
+[CommModifyKit_AddReg]
+HKR,,"UpperFilters",0x00010000,"CommModifyKit"
+
 [CommModifyKit_CopyFiles]
 CommModifyKit.sys
 
-; ============================================================================
-; 服务安装节
-; ============================================================================
 [CommModifyKit_Service_Inst]
 DisplayName    = %ServiceName%
 Description    = %ServiceDesc%

+ 7 - 1
driver/CommModifyKitDriver.vcxproj

@@ -21,7 +21,7 @@
     <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
     <ConfigurationType>Driver</ConfigurationType>
     <DriverType>KMDF</DriverType>
-    <DriverTargetPlatform>Universal</DriverTargetPlatform>
+    <DriverTargetPlatform>Desktop</DriverTargetPlatform>
     <TargetVersion>Windows10</TargetVersion>
   </PropertyGroup>
 
@@ -54,6 +54,12 @@
     <PackageDir>$(SolutionDir)build\$(Configuration)\$(Platform)\</PackageDir>
     <!-- 开发阶段禁用 Inf2Cat 验证(仅测试,发布前需重新启用) -->
     <EnableInf2cat>false</EnableInf2cat>
+    <RunCodeAnalysis>false</RunCodeAnalysis>
+    <!-- 开发阶段禁用 API 验证和 INF 验证 -->
+    <EnableInf2cat>false</EnableInf2cat>
+    <RunApiValidator>false</RunApiValidator>
+    <Inf2CatNoCatalogFiles>true</Inf2CatNoCatalogFiles>
+    <EnableInfVerification>false</EnableInfVerification>
   </PropertyGroup>
 
   <ItemDefinitionGroup>

+ 1 - 4
driver/DeviceContext.h

@@ -12,8 +12,6 @@
 // 前向声明
 struct COMMKIT_RING_ENTRY;
 
-namespace commkit_driver {
-
 // 每个串口过滤设备的上下文
 typedef struct _DEVICE_CONTEXT {
     // 该端口对应的 COM 编号(COM n 的 n),0 表示未知
@@ -35,6 +33,5 @@ typedef struct _DEVICE_CONTEXT {
 
 } DEVICE_CONTEXT, *PDEVICE_CONTEXT;
 
+// WDF_DECLARE_CONTEXT_TYPE_WITH_NAME 必须在全局作用域
 WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext)
-
-} // namespace commkit_driver

+ 183 - 9
driver/DriverEntry.cpp

@@ -15,9 +15,13 @@ extern "C" {
 DRIVER_INITIALIZE DriverEntry;
 EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd;
 EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP EvtDeviceContextCleanup;
 
 } // extern "C"
 
+// PnP 回调使用 C++ 链接(WDF 宏内部处理)
+EVT_WDF_DEVICE_PREPARE_HARDWARE EvtDevicePrepareHardware;
+
 namespace commkit_driver {
 
 // 全局驱动对象(仅用于内部日志/调试)
@@ -56,18 +60,188 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT driver_object,
 
 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)
-    // 此处为框架,留待按具体硬件环境调试完善
+
+    NTSTATUS status;
+
+    // 1. 标记为过滤设备
+    WdfFdoInitSetFilter(init);
+
+    // 2. 注册 PnP 电源回调(EvtDevicePrepareHardware 解析 COM 编号)
+    WDF_PNPPOWER_EVENT_CALLBACKS pnp_callbacks;
+    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnp_callbacks);
+    pnp_callbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
+    WdfDeviceInitSetPnpPowerEventCallbacks(init, &pnp_callbacks);
+
+    // 3. 注册 WDM IRP 预处理回调(拦截 READ/WRITE/DEVICE_CONTROL)
+    status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+        init, commkit_driver::SerialFilter::DispatchRead,
+        IRP_MJ_READ, nullptr, 0);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+        init, commkit_driver::SerialFilter::DispatchWrite,
+        IRP_MJ_WRITE, nullptr, 0);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+        init, commkit_driver::SerialFilter::DispatchDeviceControl,
+        IRP_MJ_DEVICE_CONTROL, nullptr, 0);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    // 4. 创建 WDF 设备
+    WDFDEVICE device;
+    WDF_OBJECT_ATTRIBUTES attrs;
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrs, DEVICE_CONTEXT);
+    attrs.EvtCleanupCallback = EvtDeviceContextCleanup;
+
+    status = WdfDeviceCreate(&init, &attrs, &device);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    // 5. 初始化设备上下文
+    PDEVICE_CONTEXT ctx = DeviceGetContext(device);
+    ctx->ComNumber = 0;          // 延迟到 EvtDevicePrepareHardware 解析
+    ctx->MonitoringEnabled = FALSE;
+    ctx->RingBuffer = nullptr;
+    ctx->WdfDevice = device;
+    ctx->LowerDevice = WdfDeviceWdmGetAttachedDevice(device);
+
+    // 6. 注册到全局端口表(com_number 暂为 0,PrepareHardware 时更新)
+    commkit_driver::GetClientConnection().RegisterFilterDevice(device);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS EvtDevicePrepareHardware(WDFDEVICE device,
+                                     WDFCMRESLIST resources,
+                                     WDFCMRESLIST resources_translated) {
+    UNREFERENCED_PARAMETER(resources);
+    UNREFERENCED_PARAMETER(resources_translated);
+
+    NTSTATUS status;
+    PDEVICE_CONTEXT ctx = DeviceGetContext(device);
+
+    // 从 PnP 属性解析 COM 编号
+    // 方式:获取 DevicePropertyFriendlyName 或 DevicePropertyDeviceDescription,
+    // 匹配 "COM%d" 模式
+    WDFMEMORY memory;
+    status = WdfDeviceAllocAndQueryProperty(
+        device, DevicePropertyFriendlyName, NonPagedPoolNx, 0, &memory);
+    if (!NT_SUCCESS(status)) {
+        // 尝试 DeviceDescription 作为后备
+        status = WdfDeviceAllocAndQueryProperty(
+            device, DevicePropertyDeviceDescription, NonPagedPoolNx, 0, &memory);
+    }
+
+    if (NT_SUCCESS(status)) {
+        PWCHAR name = (PWCHAR)WdfMemoryGetBuffer(memory, nullptr);
+        if (name) {
+            // 解析 "COMx" 模式(可能出现在 "COMx (port)" 或 "通信端口 (COMx)" 等格式中)
+            ULONG name_len = (ULONG)wcslen(name);
+            for (ULONG i = 0; i + 3 <= name_len; i++) {
+                if ((name[i] == L'C' || name[i] == L'c') &&
+                    (name[i+1] == L'O' || name[i+1] == L'o') &&
+                    (name[i+2] == L'M' || name[i+2] == L'm')) {
+                    if (name[i+3] >= L'0' && name[i+3] <= L'9') {
+                        ULONG num = 0;
+                        ULONG j = i + 3;
+                        while (j < name_len && name[j] >= L'0' && name[j] <= L'9') {
+                            num = num * 10 + (name[j] - L'0');
+                            j++;
+                        }
+                        if (num > 0 && num <= 256) {
+                            ctx->ComNumber = num;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        WdfObjectDelete(memory);
+    }
+
+    // 后备方案:FriendlyName/DeviceDescription 均未解析出 COM 编号时,
+    // 从 PDO 名称查询 HKLM\HARDWARE\DEVICEMAP\SERIALCOMM 获取 "COMx" 映射
+    if (ctx->ComNumber == 0) {
+        WDFMEMORY pdo_memory;
+        status = WdfDeviceAllocAndQueryProperty(
+            device, DevicePropertyPhysicalDeviceObjectName,
+            NonPagedPoolNx, 0, &pdo_memory);
+        if (NT_SUCCESS(status)) {
+            PWCHAR pdo_name = (PWCHAR)WdfMemoryGetBuffer(pdo_memory, nullptr);
+            if (pdo_name && pdo_name[0] != L'\0') {
+                UNICODE_STRING key_path;
+                RtlInitUnicodeString(&key_path,
+                    L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\SERIALCOMM");
+                OBJECT_ATTRIBUTES obj_attr;
+                InitializeObjectAttributes(&obj_attr, &key_path,
+                    OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+                HANDLE hkey;
+                status = ZwOpenKey(&hkey, KEY_READ, &obj_attr);
+                if (NT_SUCCESS(status)) {
+                    UNICODE_STRING value_name;
+                    RtlInitUnicodeString(&value_name, pdo_name);
+
+                    UCHAR result_buf[256] = {0};
+                    ULONG result_len = 0;
+                    status = ZwQueryValueKey(hkey, &value_name,
+                        KeyValuePartialInformation,
+                        result_buf, sizeof(result_buf), &result_len);
+                    if (NT_SUCCESS(status)) {
+                        KEY_VALUE_PARTIAL_INFORMATION* kv_info =
+                            (KEY_VALUE_PARTIAL_INFORMATION*)result_buf;
+                        if (kv_info->Type == REG_SZ && kv_info->DataLength >= sizeof(WCHAR)) {
+                            PWCHAR com_name = (PWCHAR)kv_info->Data;
+                            ULONG com_len = kv_info->DataLength / sizeof(WCHAR);
+                            // 解析 "COMx"
+                            if (com_len >= 4 &&
+                                (com_name[0] == L'C' || com_name[0] == L'c') &&
+                                (com_name[1] == L'O' || com_name[1] == L'o') &&
+                                (com_name[2] == L'M' || com_name[2] == L'm') &&
+                                com_name[3] >= L'0' && com_name[3] <= L'9') {
+                                ULONG num = 0;
+                                ULONG j = 3;
+                                while (j < com_len &&
+                                       com_name[j] >= L'0' && com_name[j] <= L'9') {
+                                    num = num * 10 + (com_name[j] - L'0');
+                                    j++;
+                                }
+                                if (num > 0 && num <= 256) {
+                                    ctx->ComNumber = num;
+                                }
+                            }
+                        }
+                    }
+                    ZwClose(hkey);
+                }
+            }
+            WdfObjectDelete(pdo_memory);
+        }
+    }
+
+    // 更新端口表中的 COM 编号
+    commkit_driver::GetClientConnection().UpdatePortComNumber(device, ctx->ComNumber);
+
     return STATUS_SUCCESS;
 }
 
+// 设备上下文清理回调:设备移除时注销端口表条目
+VOID EvtDeviceContextCleanup(WDFOBJECT object) {
+    WDFDEVICE device = (WDFDEVICE)object;
+    PDEVICE_CONTEXT ctx = DeviceGetContext(device);
+    if (ctx && ctx->ComNumber != 0) {
+        commkit_driver::GetClientConnection().UnregisterFilterDevice(ctx->ComNumber);
+    }
+}
+
 extern "C" VOID EvtDriverUnload(WDFDRIVER driver) {
     UNREFERENCED_PARAMETER(driver);
     commkit_driver::GetClientConnection().Cleanup();

+ 38 - 45
driver/SerialFilter.cpp

@@ -1,11 +1,12 @@
 // SerialFilter.cpp - 串口 IRP 拦截实现
-// 注意:完整 KMDF 上层过滤驱动需要 IoAttachDeviceToDeviceStack + EvtDeviceAdd
-// 本文件实现 IRP 派遣与完成例程,CreateAndAttach 为简化框架
+// KMDF 过滤驱动模式:通过 WdfDeviceInitAssignWdmIrpPreprocessCallback 注册
+// 在预处理回调中注册完成例程,捕获 READ/WRITE 数据
 #include <ntddk.h>
 #include <wdf.h>
 #include "SerialFilter.h"
 #include "EventRingBuffer.h"
 #include "ClientConnection.h"
+#include "DeviceContext.h"
 #include "../common/CommKitEvents.h"
 
 namespace commkit_driver {
@@ -18,21 +19,6 @@ static UINT64 GetCurrentTimeStamp() {
     return KeQueryInterruptTime();
 }
 
-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;
@@ -51,7 +37,6 @@ void SerialFilter::CaptureIrpData(PDEVICE_CONTEXT ctx, PIRP irp, ULONG event_typ
 
     // 根据 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;
@@ -75,12 +60,7 @@ void SerialFilter::CaptureIrpData(PDEVICE_CONTEXT ctx, PIRP irp, ULONG event_typ
     }
 
     if (!data_ptr || data_size == 0) {
-        // OP_OPEN / OP_CLOSE 事件无数据,也推送
-        if (event_type != COMMKIT_OP_READ && event_type != COMMKIT_OP_WRITE) {
-            // 落空,继续推送无数据事件
-        } else {
-            return;
-        }
+        return;
     }
 
     // 推入环形缓冲
@@ -97,6 +77,10 @@ NTSTATUS SerialFilter::OnReadComplete(PDEVICE_OBJECT dev, PIRP irp, PVOID ctx) {
     if (device_ctx) {
         CaptureIrpData(device_ctx, irp, COMMKIT_OP_READ);
     }
+    // 下层驱动返回 STATUS_PENDING 时需标记 IRP 为 pending
+    if (irp->PendingReturned) {
+        IoMarkIrpPending(irp);
+    }
     return STATUS_SUCCESS;
 }
 
@@ -106,38 +90,47 @@ NTSTATUS SerialFilter::OnWriteComplete(PDEVICE_OBJECT dev, PIRP irp, PVOID ctx)
     if (device_ctx) {
         CaptureIrpData(device_ctx, irp, COMMKIT_OP_WRITE);
     }
+    if (irp->PendingReturned) {
+        IoMarkIrpPending(irp);
+    }
     return STATUS_SUCCESS;
 }
 
-NTSTATUS SerialFilter::DispatchRead(PDEVICE_OBJECT dev, PIRP irp) {
-    PDEVICE_CONTEXT ctx = (PDEVICE_CONTEXT)dev->DeviceExtension;
-    if (ctx && ctx->MonitoringEnabled) {
-        IoMarkIrpPending(irp);
+NTSTATUS SerialFilter::DispatchRead(WDFDEVICE dev, PIRP irp) {
+    PDEVICE_CONTEXT ctx = DeviceGetContext(dev);
+
+    if (ctx && ctx->MonitoringEnabled && ctx->RingBuffer) {
         IoSetCompletionRoutine(irp, OnReadComplete, ctx, TRUE, TRUE, TRUE);
     }
-    return IoCallDriver(ctx->LowerDevice, irp);
+
+    return WdfDeviceWdmDispatchPreprocessedIrp(dev, irp);
 }
 
-NTSTATUS SerialFilter::DispatchWrite(PDEVICE_OBJECT dev, PIRP irp) {
-    PDEVICE_CONTEXT ctx = (PDEVICE_CONTEXT)dev->DeviceExtension;
-    if (ctx && ctx->MonitoringEnabled) {
-        IoMarkIrpPending(irp);
+NTSTATUS SerialFilter::DispatchWrite(WDFDEVICE dev, PIRP irp) {
+    PDEVICE_CONTEXT ctx = DeviceGetContext(dev);
+
+    if (ctx && ctx->MonitoringEnabled && ctx->RingBuffer) {
         IoSetCompletionRoutine(irp, OnWriteComplete, ctx, TRUE, TRUE, TRUE);
     }
-    return IoCallDriver(ctx->LowerDevice, irp);
+
+    return WdfDeviceWdmDispatchPreprocessedIrp(dev, irp);
 }
 
-NTSTATUS SerialFilter::DispatchDeviceControl(PDEVICE_OBJECT dev, PIRP irp) {
-    PDEVICE_CONTEXT ctx = (PDEVICE_CONTEXT)dev->DeviceExtension;
-    if (!ctx) {
-        irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
-        irp->IoStatus.Information = 0;
-        IoCompleteRequest(irp, IO_NO_INCREMENT);
-        return STATUS_INVALID_DEVICE_REQUEST;
-    }
-    // 透传 DeviceControl 给下层
-    IoSkipCurrentIrpStackLocation(irp);
-    return IoCallDriver(ctx->LowerDevice, irp);
+NTSTATUS SerialFilter::DispatchDeviceControl(WDFDEVICE dev, PIRP irp) {
+    // DeviceControl 透传,不拦截(串口 IOCTL 由应用层处理)
+    return WdfDeviceWdmDispatchPreprocessedIrp(dev, irp);
+}
+
+NTSTATUS SerialFilter::CreateAndAttach(WDFDRIVER driver,
+                                         PDEVICE_OBJECT serial_pdo,
+                                         ULONG com_number,
+                                         WDFDEVICE* out_filter_device) {
+    // 不再使用:过滤设备由 EvtDeviceAdd 自动创建
+    UNREFERENCED_PARAMETER(driver);
+    UNREFERENCED_PARAMETER(serial_pdo);
+    UNREFERENCED_PARAMETER(com_number);
+    *out_filter_device = nullptr;
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 } // namespace commkit_driver

+ 4 - 4
driver/SerialFilter.h

@@ -26,10 +26,10 @@ public:
     // 拦截 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);
+    // WDM IRP 预处理回调(KMDF 签名:WDFDEVICE, PIRP)
+    static NTSTATUS DispatchRead(WDFDEVICE dev, PIRP irp);
+    static NTSTATUS DispatchWrite(WDFDEVICE dev, PIRP irp);
+    static NTSTATUS DispatchDeviceControl(WDFDEVICE dev, PIRP irp);
 
 private:
     // 从 IRP 中提取数据并推入环形缓冲

+ 50 - 19
installer/install.ps1

@@ -1,4 +1,4 @@
-# install.ps1 - CommModifyKit 驱动 + CommModifyKit.dll 安装脚本
+# install.ps1 - CommModifyKit 驱动 + CommModifyKit.dll 安装脚本
 # 必须以管理员权限运行
 # 用法:以管理员身份打开 PowerShell,执行 .\install.ps1
 
@@ -8,32 +8,37 @@
 $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. 查找构建产物
+# 发布包场景:所有文件与脚本同目录
+# 开发场景:脚本在 installer\ 子目录,需回退到上级
 # ============================================================================
 $DriverSysCandidates = @(
-    Join-Path $SolutionRoot "build\Release\x64\CommModifyKit.sys"
-    Join-Path $SolutionRoot "build\Debug\x64\CommModifyKit.sys"
     Join-Path $ScriptDir "CommModifyKit.sys"
+    Join-Path $ScriptDir "..\build\Release\x64\CommModifyKit.sys"
+    Join-Path $ScriptDir "..\build\Debug\x64\CommModifyKit.sys"
+    Join-Path $ScriptDir "..\driver\build\Release\x64\CommModifyKit.sys"
+    Join-Path $ScriptDir "..\driver\build\Debug\x64\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"
+    Join-Path $ScriptDir "..\driver\CommModifyKit.inf"
+    Join-Path $ScriptDir "..\build\Release\x64\CommModifyKit.inf"
+    Join-Path $ScriptDir "..\build\Debug\x64\CommModifyKit.inf"
+    Join-Path $ScriptDir "..\driver\build\Release\x64\CommModifyKit.inf"
+    Join-Path $ScriptDir "..\driver\build\Debug\x64\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"
+    Join-Path $ScriptDir "CommModifyKit.dll"
+    Join-Path $ScriptDir "..\build\Release\Win32\CommModifyKit.dll"
+    Join-Path $ScriptDir "..\build\Debug\Win32\CommModifyKit.dll"
+    Join-Path $ScriptDir "..\build\Release\x64\CommModifyKit.dll"
+    Join-Path $ScriptDir "..\build\Debug\x64\CommModifyKit.dll"
 )
 
 $DriverSys = $DriverSysCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
@@ -91,23 +96,49 @@ if ($ExistingService) {
     Write-Host "  完成" -ForegroundColor Green
 }
 
+# ============================================================================
+# 3.5 注册为 Ports 类 UpperFilters
+# ============================================================================
+$PortsClassGuid = "{4D36E978-E325-11CE-BFC1-08002BE10318}"
+$ClassKey = "HKLM:\SYSTEM\CurrentControlSet\Control\Class\$PortsClassGuid"
+$currentFilters = @()
+$existingFilters = (Get-ItemProperty -Path $ClassKey -Name "UpperFilters" -ErrorAction SilentlyContinue)
+if ($existingFilters) {
+    $currentFilters = @($existingFilters.UpperFilters)
+}
+if ($currentFilters -notcontains "CommModifyKit") {
+    $newFilters = @($currentFilters) + "CommModifyKit"
+    Set-ItemProperty -Path $ClassKey -Name "UpperFilters" -Value $newFilters -Type MultiString
+    Write-Host "已将 CommModifyKit 添加到 Ports 类 UpperFilters" -ForegroundColor Green
+    # 重新枚举设备使 UpperFilter 生效
+    pnputil /scan-devices | Out-Null
+    Write-Host "已触发设备重新枚举" -ForegroundColor Green
+} else {
+    Write-Host "CommModifyKit 已在 UpperFilters 中" -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"
+        Join-Path $ScriptDir "CommModifyService.exe"
+        Join-Path $ScriptDir "..\CommModifyService\x64\Release\CommModifyService.exe"
+        Join-Path $ScriptDir "..\CommModifyService\Win32\Release\CommModifyService.exe"
+        Join-Path $ScriptDir "..\CommModifyService\x64\Debug\CommModifyService.exe"
+        Join-Path $ScriptDir "..\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
+        if ((Resolve-Path $DllPath -ErrorAction SilentlyContinue).Path -eq (Resolve-Path $TargetDll -ErrorAction SilentlyContinue).Path) {
+            Write-Host "DLL 已在目标目录,跳过拷贝" -ForegroundColor Green
+        } else {
+            Write-Host "拷贝 DLL 到 $TargetDll ..."
+            Copy-Item -Force $DllPath $TargetDll
+            Write-Host "  完成" -ForegroundColor Green
+        }
     } else {
         Write-Warning "未检测到 CommModifyService.exe,请手动拷贝 CommModifyKit.dll 到其目录"
     }

+ 48 - 66
installer/uninstall.ps1

@@ -1,78 +1,60 @@
-# uninstall.ps1 - CommModifyKit 驱动 + CommModifyKit.dll 卸载脚本
-# 必须以管理员权限运行
-#Requires -Version 5.1
+# uninstall.ps1 - CommModifyKit 驱动卸载脚本
 #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 移除驱动包..."
+Write-Host "通过 pnputil 删除驱动包..." -ForegroundColor Yellow
 try {
-    $Drivers = pnputil /enum-drivers | Select-String 'CommModifyKit' -Context 1,0
-    if ($Drivers) {
-        $Drivers | ForEach-Object {
-            Write-Host "  发现: $_"
+    $raw = pnputil /enum-drivers 2>&1
+    $lines = $raw -split "
+?
+"
+    $infs = @()
+    for ($i=0; $i -lt $lines.Count; $i++) {
+        if ($lines[$i] -match '(oem\d+\.inf)' -and $i+1 -lt $lines.Count -and $lines[$i+1] -match 'CommModifyKit') {
+            $infs += $matches[1]
         }
-        Write-Warning "请手动执行 pnputil /delete-driver oem*.inf /uninstall /force 移除"
-    } else {
-        Write-Host "  无已安装的驱动包" -ForegroundColor Green
     }
-} catch {
-    Write-Warning "pnputil 检查失败: $_"
+    if ($infs.Count -gt 0) {
+        foreach ($inf in $infs) {
+            Write-Host "  删除: $inf"
+            pnputil /delete-driver $inf /uninstall /force 2>&1 | Out-Null
+            if ($LASTEXITCODE -eq 0) { Write-Host "    完成" -ForegroundColor Green }
+            else { Write-Warning "    失败" }
+        }
+    } else { Write-Host "  未找到驱动包" -ForegroundColor Green }
+} catch { Write-Warning "pnputil 失败: $_" }
+
+$srv = Get-Service -Name 'CommModifyKit' -ErrorAction SilentlyContinue
+if ($srv) {
+    Write-Host ""
+    if ($srv.Status -eq 'Running') { sc.exe stop CommModifyKit | Out-Null; Start-Sleep 2 }
+    sc.exe delete CommModifyKit | Out-Null
+    Write-Host "服务已删除" -ForegroundColor Green
 }
 
-# ============================================================================
-# 4. 关闭测试签名(可选,谨慎执行)
-# ============================================================================
-Write-Host ""
-Write-Host "测试签名状态检查..."
-$TestSigning = bcdedit /enum | Select-String 'testsigning.*yes' -Quiet
-if ($TestSigning) {
-    Write-Warning "测试签名当前已启用"
-    Write-Host "如需关闭: bcdedit /set testsigning off(重启生效)"
+# 从 Ports 类 UpperFilters 移除 CommModifyKit
+$PortsClassGuid = "{4D36E978-E325-11CE-BFC1-08002BE10318}"
+$ClassKey = "HKLM:\SYSTEM\CurrentControlSet\Control\Class\$PortsClassGuid"
+$existingFilters = (Get-ItemProperty -Path $ClassKey -Name "UpperFilters" -ErrorAction SilentlyContinue)
+if ($existingFilters) {
+    $currentFilters = @($existingFilters.UpperFilters)
+    if ($currentFilters -contains "CommModifyKit") {
+        $newFilters = @($currentFilters | Where-Object { $_ -ne "CommModifyKit" })
+        if ($newFilters.Count -eq 0) {
+            Remove-ItemProperty -Path $ClassKey -Name "UpperFilters" -ErrorAction SilentlyContinue
+        } else {
+            Set-ItemProperty -Path $ClassKey -Name "UpperFilters" -Value $newFilters -Type MultiString
+        }
+        Write-Host "已从 Ports 类 UpperFilters 移除 CommModifyKit" -ForegroundColor Green
+    }
 }
 
+$sys = Join-Path $env:windir "System32\drivers\CommModifyKit.sys"
+$inf = Join-Path $env:windir "System32\drivers\CommModifyKit.inf"
+Write-Host ""
+if (Test-Path $sys) { Remove-Item $sys -Force -ErrorAction SilentlyContinue }
+if (Test-Path $inf) { Remove-Item $inf -Force -ErrorAction SilentlyContinue }
+Write-Host "驱动文件已删除" -ForegroundColor Green
 Write-Host ""
-Write-Host '=== 卸载完成 ===' -ForegroundColor Cyan
-Write-Host "如需删除 DLL,请手动删除 CommModifyService 目录下的 CommModifyKit.dll"
+Write-Host '=== 卸载完成 ===' -ForegroundColor Cyan