Skip to content

fix(audio-win): install 幂等——单例适配器下不再堆叠幽灵节点/漂移端点 GUID - #61

Closed
gqf2008 wants to merge 1 commit into
mainfrom
fix/audio-win-install-idempotent
Closed

gqf2008 wants to merge 1 commit into
mainfrom
fix/audio-win-install-idempotent

Conversation

@gqf2008

@gqf2008 gqf2008 commented Sep 16, 2026

Copy link
Copy Markdown
Owner

背景

Windows 侧虚拟声卡驱动在本机(跑 agent 的这台 Windows)其实已经真实装过多次(C:\Windows\INF\setupapi.dev.log 里 2026-09-16 装过 oem135.inf / oem67.inf,catalog 校验通过、ROOT\MEDIA\0001..0003 被 Start、SWD\MMDEVAPI\{...} 端点被创建/删除),C:\Windows\System32\drivers\vdev_audio.sys 现存。所以瓶颈不是「没装过」,而是装得不干净/不幂等。

问题

install 每次都「先清空全部节点、再新建一个」,踩了两件事:

  1. 清空那步会假成功——CM_Query_And_Remove_SubTreeW 回 CR_SUCCESS 而节点仍在设备树(2a2f4bd 的 M-c 修复)。没清掉时本次新建的节点与残留节点共存;而驱动侧适配器是单例(adapter::create 只允许一个实例),第二个节点在 start_device 里 create 返 null,直接上报 STATUS_INSUFFICIENT_RESOURCES(0xC000009A)——设备管理器里 ROOT\MEDIA\0002/0003 的报错就是这么来的。
  2. 即便清空成功,「删旧建新」也会换掉设备实例 ID,音频端点 GUID 随之漂移(日志里每次 install 都有一对 Delete Device - SWD\MMDEVAPI\{...}),正在使用该端点的应用被静默打断。

改动

  • 新增纯函数 install_plan(existing_nodes) -> Create | Reuse | Recreate:0 个节点 → 新建;恰好 1 个 → 就地更新驱动、不重建节点(端点 GUID 保持不变);≥2 个(历史残留)→ 先全清再重建;
  • 抽出 create_device_node();install() 改为幂等流程;DiInstallDriverW 仍按 INF 硬件 ID 更新所有匹配设备,因此复用路径无需额外绑定动作;
  • 装完后断言「恰好一个节点」(最多 ~3s 轮询),否则非零退出——宁可 CLI 报错,也不要静默留下「看着装好了、其实有个死节点」的现场;
  • README.md 补「驱动适配器是单例」已知限制,排查表新增「install 后出现多个 ROOT\MEDIA\000N / 0xC000009A」一行。

验证

本机(Windows)不涉及管理员/重启的部分:

  • cargo fmt --all -- --check —— 干净
  • cargo test -p vdev-audio-win —— 10 passed(含新增 install_plan_is_idempotent)
  • cargo clippy -p vdev-audio-win --all-targets -- -D warnings —— 干净

未做:以管理员提权实跑 release install / uninstall(会真的改动本机设备树,需 UAC);端到端验收(会议软件实际听到什么、实时链路延迟)也不在本 PR 范围。

记账说明

本机是 Windows,没有 walgit / ~/.walgit / 127.0.0.1:8081,无法按仓库 AGENTS.md 的协同铁律写 walgit collab 条目(issue/status/patch/review 均未写),因此先以 GitHub PR 提交;需要的话可以从 mac 侧补一条 collab 线程。

install 原先每次都「先清空全部节点、再新建一个」,真机上踩了两件事:

1. 清空那步会「假成功」——CM_Query_And_Remove_SubTreeW 回 CR_SUCCESS 而节点仍在
   设备树(2a2f4bd 的 M-c 修复)。没清掉时,本次新建的节点与残留节点共存;而驱动侧
   适配器是单例(adapter::create 只允许一个实例),第二个节点在 start_device 里
   create 返 null,直接上报 STATUS_INSUFFICIENT_RESOURCES(0xC000009A)——
   设备管理器里 ROOT\MEDIA\0002/0003 的报错就是这么来的。
2. 即便清空成功,「删旧建新」也会换掉设备实例 ID,音频端点 GUID 随之漂移
   (setupapi.dev.log 里每次 install 都有一对 Delete Device - SWD\MMDEVAPI\{...}),
   正在使用该端点的应用会被静默打断。

改法:

- 新增纯函数 `install_plan(existing_nodes) -> Create | Reuse | Recreate`:
  0 个节点 → 新建;恰好 1 个 → 就地更新驱动、不重建节点(端点 GUID 保持不变);
  ≥2 个(历史残留)→ 先全清再重建;
- 抽出 `create_device_node()`;`install()` 改为幂等流程;DiInstallDriverW 仍按 INF
  硬件 ID 更新所有匹配设备,因此复用路径无需额外绑定动作;
- 装完后断言「恰好一个节点」(最多 ~3s 轮询),否则非零退出——宁可 CLI 报错,也不要
  静默留下「看着装好了、其实有个死节点」的现场;
- README 补「驱动适配器是单例」已知限制,排查表新增「install 后出现多个
  ROOT\MEDIA\000N / 0xC000009A」一行。

验证(Windows 本机):`cargo fmt --all -- --check` 干净;`cargo test -p vdev-audio-win`
10 passed(含新增 install_plan_is_idempotent);`cargo clippy -p vdev-audio-win
--all-targets -- -D warnings` 干净。
@gqf2008 gqf2008 closed this Sep 16, 2026
@gqf2008
gqf2008 deleted the fix/audio-win-install-idempotent branch September 16, 2026 16:15
@gqf2008
gqf2008 restored the fix/audio-win-install-idempotent branch September 16, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant