Как идентифицировать HID-устройство с помощью информации из BluetoothDeviceInfo или наоборот - PullRequest
0 голосов
/ 29 июня 2018

Я пытаюсь написать программу, которая делает управление Wiimotes в паре с Windows намного проще и автоматизированнее. Программа использует WiimoteLib (который использует hidsdi.h и setupapi.h ) для подключения к устройствам Wiimote и 32 футов (использует Windows Bluetooth API ) для автоматического сопряжения / отключения устройств. Код для pairer / unpairer основан на Wiipair . На данный момент процесс немного неровный и медленный, но он работает. (Но только для одного Wiimote)

Проблема заключается в том, что мой модуль для сопряжения / отключения устройств Bluetooth не имеет информации о том, как определить, является ли устройство HID (используемое классом Wiimote) тем же устройством. Я хочу иметь возможность предупреждать класс Wiimote, если устройство Bluetooth было принудительно отключено или не подключено, чтобы оно могло изящно отключиться. И наоборот, я бы хотел, чтобы Wiimote предупредил pairer / unpairer, когда устройство HID отключено, так что устройство Bluetooth может быть опционально отключено (если вы планируете отключить Wiimote).

Если бы я хотел получить доступ только к одному Wiimote, то это не было бы большой проблемой, но я бы хотел иметь доступ к нескольким Wiimote и различать их, используя информацию HID и информацию Bluetooth. Я уже использую множество своих собственных P / Invoke для покрытия областей, в которых не хватает 32 футов, так что их использование больше не проблема.

Вот основной код моего pairer. (Хотя я не уверен, действительно ли это необходимо):

(Примечание: 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;
}

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

Я забыл добавить функцию EnumHid. Вот оно

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;
}

Для обнаружения отключения Wiimote мы используем ошибку в ReadFile при чтении HID-пакета из Wiimote. Этот метод отлично работает с любыми драйверами Bluetooth (все выше о MS). Однако с MS вы также можете обрабатывать WM_DEVICE_CHANGE с флагом HCI_DISCONNECT.

Константы, используемые там:

#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")
0 голосов
/ 29 июня 2018
  1. Вам не нужно соединяться с Wiimote. Сопряжение с Wiimote делает только одно: Wiimote запоминает MAC-адрес сопряженного устройства, а затем может включить его и подключиться к нему (также Wii или другое устройство). Однако это не работает с Windows, поэтому сопряжение не требуется. Если вам требуется сопряжение , используйте устаревшее сопряжение PIN-кода. ПИН-код - это wiimote MAC в обратном порядке байтов.

  2. Используйте BluetoothSetServiceState, чтобы добавить свой wiimote в качестве устройства HID в систему.

Вот код, показывающий, как найти HID Wiimote по его MAC-адресу (код взят из нашей библиотеки беспроводной связи , которая включает поддержку 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);
            }
        }

Получив DevicePath, вы можете использовать CreateFile, чтобы открыть дескриптор устройства HID.

...