public static List <DeviceInfo> FindDevices(FindDeviceCallback callback = null)
    {
        findDeviceDebugInfo.Clear();

        // set up an empty return list
        List <DeviceInfo> devices = new List <DeviceInfo>();

        // get the list of devices matching the HID class GUID
        Guid guid = Guid.Empty;

        HIDImports.HidD_GetHidGuid(out guid);
        IntPtr hdev = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE);

        // set up the attribute structure buffer
        HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA();
        diData.cbSize = Marshal.SizeOf(diData);

        // read the devices in our list
        for (uint i = 0;
             HIDImports.SetupDiEnumDeviceInterfaces(hdev, IntPtr.Zero, ref guid, i, ref diData);
             ++i)
        {
            // get the size of the detail data structure
            UInt32 size = 0;
            HIDImports.SetupDiGetDeviceInterfaceDetail(hdev, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero);

            String debugInfo = "#" + i + " (" + hdev.ToString() + ")";

            // now actually read the detail data structure
            HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA();
            diDetail.cbSize = (IntPtr.Size == 8) ? (uint)8 : (uint)5;
            HIDImports.SP_DEVINFO_DATA devInfoData = new HIDImports.SP_DEVINFO_DATA();
            devInfoData.cbSize = Marshal.SizeOf(devInfoData);
            if (HIDImports.SetupDiGetDeviceInterfaceDetail(hdev, ref diData, ref diDetail, size, out size, out devInfoData))
            {
                debugInfo += " path=" + diDetail.DevicePath;

                // create a file handle to access the device
                IntPtr fp = HIDImports.CreateFile(
                    diDetail.DevicePath, HIDImports.GENERIC_READ_WRITE, HIDImports.SHARE_READ_WRITE,
                    IntPtr.Zero, HIDImports.OPEN_EXISTING, 0, IntPtr.Zero);

                debugInfo += " fp=" + fp.ToString();

                // read the attributes
                Thread.Sleep(1);
                HIDImports.HIDD_ATTRIBUTES attrs = new HIDImports.HIDD_ATTRIBUTES();
                attrs.Size = Marshal.SizeOf(attrs);
                if (HIDImports.HidD_GetAttributes(fp, ref attrs))
                {
                    // Read the product name string.  Note that
                    String name    = "<not available>";
                    byte[] nameBuf = new byte[256];
                    if (HIDImports.HidD_GetProductString(fp, nameBuf, 256))
                    {
                        name = System.Text.Encoding.Unicode.GetString(nameBuf).TrimEnd('\0');
                    }
                    else
                    {
                        debugInfo += " [GetProductString failed]";
                    }

                    debugInfo += " productString=\"" + name + "\"";

                    // if the vendor and product ID match an LedWiz, and the
                    // product name contains "pinscape", it's one of ours
                    DeviceInfo di;
                    if (CheckIDMatch(attrs) &&
                        Regex.IsMatch(name, @"\b(?i)pinscape controller\b") &&
                        (di = DeviceInfo.Create(
                             diDetail.DevicePath, name, attrs.VendorID, attrs.ProductID, attrs.VersionNumber)) != null)
                    {
                        debugInfo += " [device added]";

                        // add the device to our list
                        devices.Add(di);

                        // if there's a callback, invoke it
                        if (callback != null)
                        {
                            callback(di, hdev, ref devInfoData);
                        }
                    }

#if false
                    // report the results for debugging
                    Console.Out.WriteLine(
                        "Product " + attrs.ProductID.ToString("X4")
                        + ", vendor " + attrs.VendorID.ToString("X4")
                        + ", version " + attrs.VersionNumber.ToString("X4")
                        + ", name " + name);
#endif
                }
                else
                {
                    debugInfo += " [GetAttributes failed]";
                }

                // done with the file handle
                if (fp.ToInt32() != 0 && fp.ToInt32() != -1)
                {
                    HIDImports.CloseHandle(fp);
                }
            }
            else
            {
                debugInfo += " [GetDeviceInterfaceDetail failed]";
            }

            findDeviceDebugInfo.Add(debugInfo);
        }

        // done with the device info list
        HIDImports.SetupDiDestroyDeviceInfoList(hdev);

        // return the device list
        return(devices);
    }