如何通过C#蓝牙指令关闭Xbox Series手柄(WinForms .NET 4.7.2)
如何通过C#蓝牙指令关闭Xbox Series手柄(WinForms .NET 4.7.2)
我懂你现在的困扰——想在游戏退出时单独断开Xbox Series手柄,而不是关掉整个PC蓝牙,之前试了各种释放资源的方法都没效果对吧?你的思路其实非常对:当PC蓝牙关闭时,确实会给手柄发送一个明确的断开指令,我们完全可以模拟这个指令,而不是一刀切关掉蓝牙。我来帮你梳理下问题和可行的解决方案:
为什么你当前的代码没效果?
你现在的代码有两个核心问题:
- 设备类型选错了:你用的是
BluetoothLEDevice(蓝牙低功耗设备),但Xbox Series手柄在蓝牙模式下,主要使用的是**经典蓝牙(BR/EDR)**而非BLE。用BLE的设备选择器可能根本找不到正确的设备实例,或者即使找到,也无法发送正确的断开指令。 - 单纯释放资源≠主动断开:你调用
Dispose()和GC回收,只是释放了PC端的设备资源,但并没有给手柄发送主动断开的GATT指令。手柄会一直保持连接直到超时(通常15分钟左右),因为它没收到明确的断开信号。
修正方案1:用经典蓝牙API主动发送断开指令
我们先调整代码,使用经典蓝牙的API来获取设备,然后主动断开GATT会话,而不是单纯释放资源:
using System; using System.Linq; using System.Threading.Tasks; using Windows.Devices.Bluetooth; using Windows.Devices.Enumeration; using Windows.Devices.Bluetooth.GenericAttributeProfile; private async Task TurnOffXboxController() { // 用经典蓝牙的设备选择器,而非BLE string deviceSelector = BluetoothDevice.GetDeviceSelector(); DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(deviceSelector); // 匹配Xbox手柄名称(部分手柄会带序列号后缀,建议用Contains而非严格Equals) var xboxControllerInfo = devices.FirstOrDefault(btDevice => btDevice.Name != null && btDevice.Name.Contains("Xbox Wireless Controller")); if (xboxControllerInfo == null) { Console.WriteLine("未找到Xbox手柄"); return; } // 获取经典蓝牙设备实例 var xboxController = await BluetoothDevice.FromIdAsync(xboxControllerInfo.Id); if (xboxController == null) { Console.WriteLine("无法获取手柄设备权限,请检查蓝牙访问权限"); return; } try { // 获取所有GATT服务 var servicesResult = await xboxController.GetGattServicesAsync(); if (servicesResult.Status != GattCommunicationStatus.Success) { Console.WriteLine("无法获取GATT服务"); return; } // 遍历服务,主动断开GATT会话 foreach (var service in servicesResult.Services) { if (service.Session != null) { // 发送主动断开指令 service.Session.Disconnect(); await Task.Delay(100); // 等待指令生效 } service.Dispose(); } // 监听连接状态变化,确认断开 xboxController.ConnectionStatusChanged += (s, e) => { if (xboxController.ConnectionStatus == BluetoothConnectionStatus.Disconnected) { Console.WriteLine("手柄已成功断开"); } }; } finally { xboxController.Dispose(); GC.Collect(); GC.WaitForPendingFinalizers(); } }
注意事项:
- 权限问题:如果打包成MSIX,需要在
Package.appxmanifest中添加bluetooth和bluetooth.rfcomm权限;普通EXE则需确保用户有蓝牙设备的访问权限(可通过DeviceInformation.RequestAccessAsync主动请求)。 - 设备名称匹配:部分手柄名称会带后缀(比如
Xbox Wireless Controller (1234)),建议用Contains而非严格相等。
更可靠的方案2:通过HID指令直接控制手柄断开
Xbox手柄本质是HID设备,它支持特定的HID报告指令用来主动断开连接。这个方法比蓝牙API更直接,因为是手柄原生支持的指令:
using System; using System.Linq; using System.Threading.Tasks; using Windows.Devices.Enumeration; using Windows.Devices.HumanInterfaceDevice; using Windows.Storage.Streams; private async Task TurnOffXboxControllerViaHID() { // HID设备选择器:匹配Xbox的VID(固定为0x045E)和对应PID // 不同手柄PID不同:Series X/S是0x0B14,One S是0x0B13,旧款One是0x02E0 string hidSelector = HidDevice.GetDeviceSelector(0x045E, 0x0B14); DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(hidSelector); var xboxControllerInfo = devices.FirstOrDefault(); if (xboxControllerInfo == null) { // 尝试其他PID hidSelector = HidDevice.GetDeviceSelector(0x045E, 0x0B13); devices = await DeviceInformation.FindAllAsync(hidSelector); xboxControllerInfo = devices.FirstOrDefault(); } if (xboxControllerInfo == null) { Console.WriteLine("未找到Xbox HID设备"); return; } var hidDevice = await HidDevice.FromIdAsync(xboxControllerInfo.Id, FileAccessMode.ReadWrite); if (hidDevice == null) { Console.WriteLine("无法获取HID设备访问权限"); return; } try { // Xbox手柄标准断开指令:报告ID 0x01,后跟7个0x00 var report = new HidOutputReport(hidDevice, 0x01); using (var writer = new DataWriter(report.Data)) { writer.WriteBytes(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); await writer.StoreAsync(); } // 发送断开指令 var result = await hidDevice.SendOutputReportAsync(report); if (result.Status == HidSendReportStatus.Success) { Console.WriteLine("已发送断开指令,手柄将在1-2秒内关闭"); } } finally { hidDevice.Dispose(); } }
说明:
- VID/PID匹配:Xbox的厂商ID(VID)固定为
0x045E,产品ID(PID)根据型号不同:Series X/S是0x0B14,One S是0x0B13,旧款One是0x02E0,可根据自己的手柄型号调整。 - HID报告效果:发送该报告后,手柄会在1-2秒内断开连接,灯效完全关闭,和手动关闭手柄效果一致。
最后通用注意事项
- 不管用哪种方法,都需要确保WinForms项目引用Windows Runtime API:可通过NuGet安装
Microsoft.Windows.SDK.Contracts包(选择对应你的Windows 10版本的版本号,比如1903+选10.0.18362.0)。 - 测试时需确保手柄处于蓝牙连接模式(而非Xbox无线适配器模式),适配器模式需使用Xbox专用API而非蓝牙/HID接口。
内容来源于stack exchange
相关产品推荐
相关产品推荐

