using System; using System.Diagnostics; using System.IO; using System.Security.Principal; using System.Threading.Tasks; namespace AIK.Service.Service { /// /// devcon.exe 封装:用于 Win7 上解决 USB 设备热切换(HID ↔ U 盘模式)时枚举失败(Code 10)的问题。 /// devcon 是 Windows Driver Kit(WDK)自带工具,**不是** Windows 系统自带,需要随程序分发: /// 将 devcon.exe(x86/x64 与程序位数一致)放到程序运行目录,或放在 LocateDevcon 指定的路径。 /// 注意:devcon remove / restart / rescan 需要**管理员权限**,建议程序以管理员身份运行。 /// 调用失败时全部降级返回 false,不抛异常,不影响原流程。 /// public static class UsbDevconHelper { /// /// 查找 devcon.exe。优先按当前进程位数查找运行目录下的 x64 或 x86 子目录, /// 其次回退到运行目录根下的 devcon.exe;找不到返回 null。 /// public static string LocateDevcon() { var candidates = new[] { AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.RelativeSearchPath ?? string.Empty }; // 优先位数匹配的子目录(x64/x86),程序 AnyCPU 编译时按运行进程位数选择 string archDir = IntPtr.Size == 8 ? "x64" : "x86"; foreach (var dir in candidates) { if (string.IsNullOrEmpty(dir)) continue; var archExe = Path.Combine(dir, archDir, "devcon.exe"); if (File.Exists(archExe)) return archExe; } // 回退:运行目录根下的 devcon.exe(兼容手动放置的场景) foreach (var dir in candidates) { if (string.IsNullOrEmpty(dir)) continue; var exe = Path.Combine(dir, "devcon.exe"); if (File.Exists(exe)) return exe; } return null; } /// 是否以管理员权限运行(devcon remove/restart/rescan 需要)。 public static bool IsAdministrator() { try { using var identity = WindowsIdentity.GetCurrent(); var principal = new WindowsPrincipal(identity); return principal.IsInRole(WindowsBuiltInRole.Administrator); } catch { return false; } } /// 执行 devcon rescan(重新扫描硬件)。 public static Task RescanAsync(int timeoutMs = 30000) { return RunAsync("rescan", timeoutMs); } /// 执行 devcon restart(重启设备:disable + enable,强制重新加载驱动)。 public static Task RestartDeviceAsync(int vid, int pid, int timeoutMs = 30000) { // 用通配符匹配设备实例 ID(形如 USB\VID_28E9&PID_FFF0\xxxxx) var hwid = $"USB\\VID_{vid:X4}&PID_{pid:X4}"; return RunAsync($"restart \"{hwid}\"", timeoutMs); } /// /// 执行 devcon remove + rescan(删除设备实例后重新扫描),等价于"软件拔插", /// 比 restart(disable+enable)更彻底,对 Win7 热切换 Code 10 最有效。 /// public static async Task RemoveAndRescanAsync(int vid, int pid, int timeoutMs = 30000) { var hwid = $"USB\\VID_{vid:X4}&PID_{pid:X4}"; bool removed = await RunAsync($"remove \"{hwid}\"", timeoutMs).ConfigureAwait(false); // 无论 remove 是否成功都执行 rescan(设备可能已不在,rescan 无副作用) bool rescanned = await RunAsync("rescan", timeoutMs).ConfigureAwait(false); return removed || rescanned; } /// 执行任意 devcon 命令;找不到 exe / 非管理员 / 超时 / 非零退出码均返回 false。 public static async Task RunAsync(string arguments, int timeoutMs = 30000) { var devcon = LocateDevcon(); if (devcon == null) { return false; // 未分发 devcon.exe,静默降级 } if (!IsAdministrator()) { return false; // 非管理员时 devcon 无法操作设备,静默降级 } Process process = null; try { var psi = new ProcessStartInfo { FileName = devcon, Arguments = arguments, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; process = Process.Start(psi); // .NET Framework 4.8 无 WaitForExitAsync,用 Task.Run 包装 + 超时控制 var exitTask = Task.Run(() => process.WaitForExit(timeoutMs)); bool exited = await exitTask.ConfigureAwait(false); if (!exited) { try { process.Kill(); } catch { /* 忽略 */ } return false; } return process.ExitCode == 0; } catch { return false; } finally { try { process?.Dispose(); } catch { /* 忽略 */ } } } } }