简体   繁体   English

如何使用BluetoothDeviceInfo中的信息识别HID设备,反之亦然

[英]How to identify HID Device with information from BluetoothDeviceInfo or vice-versa

I'm trying to write a program that makes management of Wiimotes paired with Windows a lot simpler and automated. 我正在尝试编写一个程序,使与Windows配对的Wiimotes的管理更加简单和自动化。 The program uses WiimoteLib (which uses hidsdi.h and setupapi.h ) to connect to Wiimote devices, and 32feet (uses Windows Bluetooth API ) to automatically pair/unpair devices. 该程序使用WiimoteLib (使用hidsdi.hsetupapi.h连接到Wiimote设备,并使用 32feet (使用Windows Bluetooth API自动配对/ 取消配对设备。 The code for the pairer/unpairer is based off of Wiipair . 配对器/取消配对器的代码基于Wiipair At the moment, the process is a little bumpy and slow, but it works. 目前,该过程有些颠簸且缓慢,但是可以正常工作。 (But only for one Wiimote) (但仅适用于一位Wiimote)

The problem is that my module to pair/unpair Bluetooth devices has no information on how to identify if the HID device (used by the Wiimote class) is the same device. 问题是我的蓝牙设备配对/取消配对模块没有关于如何识别HID设备(由Wiimote类使用)是否为同一设备的信息。 I want to be able to alert the Wiimote class if the Bluetooth device has been forcefully shutdown or unpaired so that it can gracefully disconnect itself. 如果蓝牙设备已被强制关闭或未配对,我希望能够向Wiimote类发出警报,以便它可以正常断开连接。 And vice-versa, I'd like the Wiimote to alert the pairer/unpairer when the HID device is disconnected so that the Bluetooth device can optionally be unpaired (assuming you plan on shutting down the Wiimote). 反之亦然,当HID设备断开连接时,我希望Wiimote提醒配对器/取消配对器,以便可以选择取消配对Bluetooth设备(假设您计划关闭Wiimote)。

If I only wanted access to one Wiimote then this wouldn't be much of a problem, but I'd like to be able to access multiple Wiimotes and be able to differentiate them by using their HID info and Bluetooth Info. 如果我只想访问一个Wiimote,那么这不是什么大问题,但是我希望能够访问多个Wiimotes,并能够通过使用其HID信息和蓝牙信息来区分它们。 I'm already using plenty of my own P/Invoke to cover for areas that 32feet lacks in so using any more isn't a problem. 我已经在使用自己的P / Invoke来弥补32feet所欠缺的区域,因此再使用也不是问题。

Here's the main code for my pairer. 这是我的配对器的主要代码。 (Although I'm not sure if it's really necessary): (尽管我不确定是否真的必要):

(Note: IsDiscoverable() , ToPin() , and ToMacAddress() are all extension methods.) (注意: IsDiscoverable()ToPin()ToMacAddress()都是扩展方法。)

// Once this is true, the Wiimote will
// attempt to connect to an HID device.
public bool IsAnyDeviceAvailable { get; private set; }

private void PairTask(CancellationToken token) {
    // Setup automatic authentication
    BluetoothWin32Authentication auth = new BluetoothWin32Authentication(OnHandleRequests);

    while (!token.IsCancellationRequested)
        PairLoop(token);
}

private void PairLoop(CancellationToken token) {
    // Get a copy of known addresses since
    // these are added to in another task.
    BluetoothAddress[] addresses;
    lock (knownAddresses)
        addresses = KnownAddresses.ToArray();

    bool available = false;
    foreach (BluetoothAddress address in addresses) {
        if (token.IsCancellationRequested)
            return;
        BluetoothDeviceInfo device = new BluetoothDeviceInfo(address);

        if (device.Connected) {
            if (!available && !IsAnyDeviceAvailable) {
                lock (knownAddresses)
                    IsAnyDeviceAvailable = true;
            }
            available = true;
            continue;
        }
        if (device.Remembered) {
            RemoveDevice(device, token);
        }
        else if (device.IsDiscoverable() && !device.Authenticated) {
            if (PairDevice(device, token, available))
                available = true;
        }
        token.WaitHandle.WaitOne(500);
    }
    if (!available && IsAnyDeviceAvailable) {
        Trace.WriteLine("No more devices connected");
        lock (knownAddresses)
            IsAnyDeviceAvailable = false;
    }
}

private void RemoveDevice(BluetoothDeviceInfo device, CancellationToken token) {
    token.WaitHandle.WaitOne(1000);
    if (BluetoothSecurity.RemoveDevice(device.DeviceAddress)) {
        Trace.WriteLine($"Wiimote removed: {device.DeviceAddress.ToMacAddress()}");
        token.WaitHandle.WaitOne(2000);
    }
}

private bool PairDevice(BluetoothDeviceInfo device, CancellationToken token,
    bool available)
{
    string pin = device.DeviceAddress.ToPin();

    try {
        if (BluetoothSecurity.PairRequest(device.DeviceAddress, pin)) {
            Trace.WriteLine($"Wiimote authenticated: {device.DeviceAddress.ToMacAddress()}");
            token.WaitHandle.WaitOne(1000);
            // Calling this before and after seems to help unsure
            // the device works when paired programmatically.
            Guid[] services = device.InstalledServices;
            device.SetServiceState(Uuids.HumanInterfaceDeviceServiceClass_UUID, true, true);
            services = device.InstalledServices;
            Trace.WriteLine($"Wiimote paired: {device.DeviceAddress.ToMacAddress()}");

            token.WaitHandle.WaitOne(8000);

            if (!available && !IsAnyDeviceAvailable) {
                Trace.WriteLine("First device has been connected");
                lock (knownAddresses)
                    IsAnyDeviceAvailable = true;
            }
            return true;
        }
        else {
            Trace.WriteLine($"Wiimote authentication failed: {device.DeviceAddress.ToMacAddress()}");
        }
    }
    catch {
        Trace.WriteLine($"Wiimote pairing failed: {device.DeviceAddress.ToMacAddress()}");
    }
    return false;
}

private void OnHandleRequests(object sender, BluetoothWin32AuthenticationEventArgs e) {
    e.Confirm = true;
}
  1. You do not need to pair with your Wiimote. 您无需与Wiimote配对。 Pairing with Wiimote does only one thing: Wiimote remembers MAC of paired device and then can trun it ON and connect to it (too Wii or other device). 与Wiimote配对仅做一件事:Wiimote记住配对设备的MAC,然后可以将其运行并连接到它(太Wii或其他设备)。 However it doe snot work with Windows so pairing is not required. 但是,它在Windows上无法使用,因此不需要配对。 If you need pairing then use legacy PIN pairing. 如果需要配对,请使用旧式PIN配对。 PIN is wiimote MAC in reversed bytes order. PIN是按反向字节顺序的Wiimote MAC。

  2. Use BluetoothSetServiceState to add your wiimote as HID device into the system. 使用BluetoothSetServiceState将您的Wiimote作为HID设备添加到系统中。

Here is code shows how to find Wiimote HID by its MAC (the code is taken from our Wireless Communication Library that includes support for Wiimote). 以下代码显示了如何通过其MAC查找Wiimote HID(该代码取自我们的无线通信库 ,其中包括对Wiimote的支持)。

m_fInstalled = true;

    Sleep(1000);

    CwclStringList* Wiis = new CwclStringList();

    m_sDevicePath = WCL_EMPTY_STR;
    DWORD dwTik = GetTickCount();
    while (m_sDevicePath == WCL_EMPTY_STR && GetTickCount() - dwTik < 20000)
        if (EnumHID(*Wiis) == WCL_E_SUCCESS)
        {
            CwclString sAddress = GetBluetoothParams()->GetAddress();
            CwclString sAdr = sAddress.Mid(1, 2) + sAddress.Mid(4, 2) + sAddress.Mid(7, 2) + sAddress.Mid(10, 2) + sAddress.Mid(13, 2) + sAddress.Mid(16, 2);
            HKEY hKey;
            CwclString aRegKey(WCL_WII_REG_KEY);
            DWORD dwRes = RegOpenKey(HKEY_LOCAL_MACHINE, aRegKey, &hKey);
            if (dwRes != ERROR_SUCCESS)
                aRegKey = CwclString(WCL_WII_REG_KEY_NEW);
            dwRes = RegOpenKey(HKEY_LOCAL_MACHINE, aRegKey, &hKey);
            if (dwRes == ERROR_SUCCESS)
            {
                DWORD dwNdx = 0;
                WCHAR szSubKeyName[512];
                DWORD dwSubKeySize = sizeof(szSubKeyName);
                ZeroMemory(szSubKeyName, dwSubKeySize);
                while (RegEnumKeyEx(hKey, dwNdx, szSubKeyName, &dwSubKeySize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
                {
                    CwclString sSubKeyStr = CwclString(szSubKeyName);
                    if (sSubKeyStr.Find(sAdr) >= 0)
                    {
                        CwclString sSubKeyPath = aRegKey + L"\\" + sSubKeyStr;
                        HKEY hSubKey;
                        dwRes = RegOpenKey(HKEY_LOCAL_MACHINE, sSubKeyPath, &hSubKey);
                        if (dwRes == ERROR_SUCCESS)
                        {
                            WCHAR szValue[512];
                            DWORD dwValueSize = sizeof(szValue);
                            ZeroMemory(szValue, dwValueSize);
                            dwRes = RegQueryValueEx(hSubKey, L"ParentIdPrefix", NULL, NULL, (LPBYTE)szValue, &dwValueSize);
                            if (dwRes == ERROR_SUCCESS)
                            {
                                CwclString sValueStr = CwclString(szValue);
                                for (INT_PTR i = 0; i < Wiis->GetCount(); i++)
                                    if (Wiis->GetItems(i).Find(sValueStr) >= 0)
                                    {
                                        m_sDevicePath = Wiis->GetItems(i);
                                        break;
                                    }
                            }
                            RegCloseKey(hSubKey);
                        }
                    }

                    if (m_sDevicePath != WCL_EMPTY_STR)
                        break;

                    dwSubKeySize = sizeof(szSubKeyName);
                    ZeroMemory(szSubKeyName, dwSubKeySize);
                    dwNdx++;
                }
                RegCloseKey(hKey);
            }
        }

Once you get DevicePath you can use CreateFile to open HID device's handle. 一旦获得DevicePath,就可以使用CreateFile打开HID设备的句柄。

I forgot to add EnumHid function. 我忘了添加EnumHid函数。 Here it is 这里是

int CwclWiimote::EnumHID(CwclStringList& rWiis)
{
    if (!wclIsTransportAvailable(trBluetooth))
        return WCL_E_TRANSPORT_NOT_AVAILABLE;

    GUID Guid;
    HidD_GetHidGuid(&Guid);

    HDEVINFO hDevInfo = SetupDiGetClassDevs(&Guid, NULL, 0, DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE)
        return WCL_E_INTERNAL;

    SP_DEVICE_INTERFACE_DATA diData;
    ZeroMemory(&diData, sizeof(SP_DEVICE_INTERFACE_DATA));
    diData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

    rWiis.Clear();
    DWORD dwIndex = 0;
    PSP_DEVICE_INTERFACE_DETAIL_DATA_W diDetail;
    while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &Guid, dwIndex, &diData))
    {
        DWORD dwSize = 0;
        SetupDiGetDeviceInterfaceDetail(hDevInfo, &diData, NULL, 0, &dwSize, NULL);
        diDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)LocalAlloc(LPTR, dwSize);
        if (diDetail)
        {
            ZeroMemory(diDetail, dwSize);
            diDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);

            if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &diData, diDetail, dwSize, &dwSize, NULL))
            {
                HANDLE hHandle = CreateFile(diDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
                if (hHandle != INVALID_HANDLE_VALUE)
                {
                    HIDD_ATTRIBUTES Attrib;
                    ZeroMemory(&Attrib, sizeof(HIDD_ATTRIBUTES));
                    Attrib.Size = sizeof(HIDD_ATTRIBUTES);

                    if (HidD_GetAttributes(hHandle, &Attrib))
                        if (Attrib.VendorID == WCL_WII_VID && (Attrib.ProductID == WCL_WII_PID || Attrib.ProductID == WCL_WII_PID_NEW))
                        {
                            CwclString Str = diDetail->DevicePath;
                            rWiis.Add(Str);
                        }

                    CloseHandle(hHandle);
                }
            }

            LocalFree((HLOCAL)diDetail);
        }

        dwIndex++;
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

    return WCL_E_SUCCESS;
}

To detect Wiimote disconnection we use error on ReadFile when reading HID packet from Wiimote. 为了检测Wiimote断开连接,当从Wiimote读取HID数据包时,我们在ReadFile上使用了错误。 The method works great with any Bluetooth drivers (all above about MS). 该方法适用于任何蓝牙驱动程序(所有关于MS的驱动程序)。 However with MS you can also handle WM_DEVICE_CHANGE with HCI_DISCONNECT flag. 但是,对于MS,您还可以使用HCI_DISCONNECT标志来处理WM_DEVICE_CHANGE。

Constancts used there: 在那里使用的常量:

#define WCL_WII_VID     0x057E
#define WCL_WII_PID     0x0306
#define WCL_WII_PID_NEW 0x0330

#define WCL_WII_REG_KEY     (L"SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002057e_PID&0306")
#define WCL_WII_REG_KEY_NEW (L"SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002057e_PID&0330")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将数据从C#推送到ZeroMQ并从Node.JS提取,反之亦然? - How to Push data from C# to ZeroMQ and Pull from Node.JS or vice-versa? 如何从点网库调用SL OOB应用程序中的方法,反之亦然? - How to call methods in SL OOB application from Dot net library and vice-versa? XML到数据库,反之亦然 - XML to database and vice-versa 如何从无符号类型的通用参数中得出带符号的类型,反之亦然? - How to derive signed type from unsigned type generic parameter, and vice-versa? 如何将 Windows 任务栏从“显示”切换到“自动隐藏”(反之亦然)? - How to toggle/switch Windows taskbar from “show” to “auto-hide” (and vice-versa)? 如何将数据库大小从兆字节转换为字节,反之亦然? - How do I convert the database size from mega bytes to bytes or vice-versa? 从Java运行C#代码,反之亦然 - Run C# code from Java and vice-versa 从javascript调用Unity Android方法,反之亦然 - Call Unity Android Method from javascript and vice-versa 从16到32的颜色转换,反之亦然 - Color conversion from 16 to 32 and vice-versa 如何将日期转换为毫秒(UTC),反之亦然? - How to convert date to ms (UTC) and vice-versa?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM