Beispiel #1
6
PTSTR DriverNameToDeviceDesc (__in PCTSTR DriverName, BOOLEAN DeviceId)
{
    DEVINST     devInst;
    DEVINST     devInstNext;
    CONFIGRET   cr;
    ULONG       walkDone = 0;
    ULONG       len;

    // Get Root DevNode
    //
    cr = CM_Locate_DevNode(&devInst,
                           NULL,
                           0);

    if (cr != CR_SUCCESS)
    {
        return NULL;
    }

    // Do a depth first search for the DevNode with a matching
    // DriverName value
    //
    while (!walkDone)
    {
        // Get the DriverName value
        //
        len = sizeof(buf) / sizeof(buf[0]);
        cr = CM_Get_DevNode_Registry_Property(devInst,
                                              CM_DRP_DRIVER,
                                              NULL,
                                              buf,
                                              &len,
                                              0);

        // If the DriverName value matches, return the DeviceDescription
        //
        if (cr == CR_SUCCESS && _tcsicmp(DriverName, buf) == 0)
        {
            len = sizeof(buf) / sizeof(buf[0]);

            if (DeviceId)
            {
                cr = CM_Get_Device_ID(devInst,
                                      buf,
                                      len,
                                      0);
            }
            else
            {
                cr = CM_Get_DevNode_Registry_Property(devInst,
                                                      CM_DRP_DEVICEDESC,
                                                      NULL,
                                                      buf,
                                                      &len,
                                                      0);
            }

            if (cr == CR_SUCCESS)
            {
                return buf;
            }
            else
            {
                return NULL;
            }
        }

        // This DevNode didn't match, go down a level to the first child.
        //
        cr = CM_Get_Child(&devInstNext,
                          devInst,
                          0);

        if (cr == CR_SUCCESS)
        {
            devInst = devInstNext;
            continue;
        }

        // Can't go down any further, go across to the next sibling.  If
        // there are no more siblings, go back up until there is a sibling.
        // If we can't go up any further, we're back at the root and we're
        // done.
        //
        for (;;)
        {
            cr = CM_Get_Sibling(&devInstNext,
                                devInst,
                                0);

            if (cr == CR_SUCCESS)
            {
                devInst = devInstNext;
                break;
            }

            cr = CM_Get_Parent(&devInstNext,
                               devInst,
                               0);


            if (cr == CR_SUCCESS)
            {
                devInst = devInstNext;
            }
            else
            {
                walkDone = 1;
                break;
            }
        }
    }

    return NULL;
}
nsresult
sbWinFindDeviceByClass(DEVINST*         aDevInst,
                       bool*          aFound,
                       DEVINST          aRootDevInst,
                       const nsAString& aClass)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aDevInst);
  NS_ENSURE_ARG_POINTER(aFound);

  // Function variables.
  bool    found = PR_FALSE;
  CONFIGRET cr;
  nsresult  rv;

  // Get the root device class.
  WCHAR propBuffer[256];
  ULONG length = sizeof(propBuffer);
  cr = CM_Get_DevNode_Registry_PropertyW(aRootDevInst,
                                         CM_DRP_CLASS,
                                         NULL,
                                         propBuffer,
                                         &length,
                                         0);
  NS_ENSURE_TRUE(cr == CR_SUCCESS, NS_ERROR_FAILURE);

  // Check if the root device class matches.  If it does, return.
  nsDependentString property(propBuffer);
  if (property.Equals(aClass)) {
    *aDevInst = aRootDevInst;
    *aFound = PR_TRUE;
    return NS_OK;
  }

  // Search all descendents of device.
  DEVINST childDevInst;
  cr = CM_Get_Child(&childDevInst, aRootDevInst, 0);
  while (cr == CR_SUCCESS) {
    // Search child.
    rv = sbWinFindDeviceByClass(aDevInst, &found, childDevInst, aClass);
    NS_ENSURE_SUCCESS(rv, rv);
    if (found) {
      found = PR_TRUE;
      break;
    }

    // Get the next child.  An error indicates that no more children are
    // present.
    cr = CM_Get_Sibling(&childDevInst, childDevInst, 0);
  }

  // Return results.
  *aFound = found;

  return NS_OK;
}
Beispiel #3
0
bool
CDeviceView::GetSiblingDevice(
    _In_ DEVINST PrevDevice,
    _Out_ PDEVINST DevInst
)
{
    CONFIGRET cr;
    cr = CM_Get_Sibling(DevInst,
                        PrevDevice,
                        0);
    return (cr == CR_SUCCESS);
}
Beispiel #4
0
static VOID
EnumChildDevices(HWND hTreeView,
                 HTREEITEM hRoot,
                 DEVINST dnParentDevInst,
                 BOOL bShowHidden)
{
    HTREEITEM hDevItem;
    DEVINST dnDevInst;
    CONFIGRET cr;

    cr = CM_Get_Child(&dnDevInst,
                      dnParentDevInst,
                      0);
    if (cr != CR_SUCCESS)
        return;

    hDevItem = AddDeviceToTree(hTreeView,
                               hRoot,
                               dnDevInst,
                               bShowHidden);
    if (hDevItem != NULL)
    {
        EnumChildDevices(hTreeView,
                         hDevItem,
                         dnDevInst,
                         bShowHidden);
    }

    while (cr == CR_SUCCESS)
    {
        cr = CM_Get_Sibling(&dnDevInst,
                            dnDevInst,
                            0);
        if (cr != CR_SUCCESS)
            break;

        hDevItem = AddDeviceToTree(hTreeView,
                                   hRoot,
                                   dnDevInst,
                                   bShowHidden);
        if (hDevItem != NULL)
        {
            EnumChildDevices(hTreeView,
                             hDevItem,
                             dnDevInst,
                             bShowHidden);
        }
    }

    (void)TreeView_SortChildren(hTreeView,
                                hRoot,
                                0);
}
nsresult
_sbWinFindDevicesByInterface(nsTArray<DEVINST>& aDevInstList,
                             DEVINST            aRootDevInst,
                             const GUID*        aGUID,
                             bool             aSearchAncestors)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aGUID);

  // Function variables.
  CONFIGRET cr;
  nsresult  rv;

  // Check if the root device has the specified interface.
  bool hasInterface;
  rv = sbWinDeviceHasInterface(aRootDevInst, aGUID, &hasInterface);
  NS_ENSURE_SUCCESS(rv, rv);
  if (hasInterface) {
    NS_ENSURE_TRUE(aDevInstList.AppendElement(aRootDevInst),
                   NS_ERROR_OUT_OF_MEMORY);
  }

  // Search all descendents or ancestors of device.
  if (aSearchAncestors) {
    DEVINST parentDevInst;
    cr = CM_Get_Parent(&parentDevInst, aRootDevInst, 0);
    if (cr == CR_SUCCESS) {
      // Search parent.
      rv = _sbWinFindDevicesByInterface(aDevInstList,
                                        parentDevInst,
                                        aGUID,
                                        aSearchAncestors);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  } else {
    DEVINST childDevInst;
    cr = CM_Get_Child(&childDevInst, aRootDevInst, 0);
    while (cr == CR_SUCCESS) {
      // Search child.
      rv = _sbWinFindDevicesByInterface(aDevInstList,
                                        childDevInst,
                                        aGUID,
                                        aSearchAncestors);
      NS_ENSURE_SUCCESS(rv, rv);

      // Get the next child.  An error indicates that no more children are
      // present.
      cr = CM_Get_Sibling(&childDevInst, childDevInst, 0);
    }
  }

  return NS_OK;
}
Beispiel #6
0
BOOL CDeviceSetup::GetSibling(PSP_DEVINFO_DATA pDevInfoData)
{
	assert(pDevInfoData!=NULL);
	if(pDevInfoData==NULL)
		return 0;

	DEVINST siblingDevInstance;
	if(CM_Get_Sibling(&siblingDevInstance,m_devInfoData.DevInst,0) != CR_SUCCESS)
		return 0;
	
	
	DevInstToDevInfoData(siblingDevInstance,pDevInfoData);

	return 1;


}
Beispiel #7
0
/*
 * Refresh the list of USB devices
 */
BOOL GetUSBDevices(DWORD devnum)
{
	// The first two are standard Microsoft drivers (including the Windows 8 UASP one).
	// The rest are the vendor UASP drivers I know of so far - list may be incomplete!
	const char* storage_name[] = { "USBSTOR", "UASPSTOR", "VUSBSTOR", "ETRONSTOR", "ASUSSTPT" };
	const char* scsi_name = "SCSI";
	const char* usb_speed_name[USB_SPEED_MAX] = { "USB", "USB 1.0", "USB 1.1", "USB 2.0", "USB 3.0" };
	// Hash table and String Array used to match a Device ID with the parent hub's Device Interface Path
	htab_table htab_devid = HTAB_EMPTY;
	StrArray dev_if_path;
	char letter_name[] = " (?:)";
	char uefi_togo_check[] = "?:\\EFI\\Rufus\\ntfs_x64.efi";
	BOOL r = FALSE, found = FALSE, is_SCSI;
	HDEVINFO dev_info = NULL;
	SP_DEVINFO_DATA dev_info_data;
	SP_DEVICE_INTERFACE_DATA devint_data;
	PSP_DEVICE_INTERFACE_DETAIL_DATA_A devint_detail_data;
	DEVINST parent_inst, grandparent_inst, device_inst;
	DWORD size, i, j, k, l, datatype, drive_index;
	ULONG list_size[ARRAYSIZE(storage_name)] = { 0 }, list_start[ARRAYSIZE(storage_name)] = { 0 }, full_list_size, ulFlags;
	HANDLE hDrive;
	LONG maxwidth = 0;
	int s, score, drive_number;
	char drive_letters[27], *device_id, *devid_list = NULL, entry_msg[128];
	char *label, *entry, buffer[MAX_PATH], str[MAX_PATH], *method_str;
	usb_device_props props;

	IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList));
	StrArrayClear(&DriveID);
	StrArrayClear(&DriveLabel);
	StrArrayCreate(&dev_if_path, 128);
	// Add a dummy for string index zero, as this is what non matching hashes will point to
	StrArrayAdd(&dev_if_path, "");

	device_id = (char*)malloc(MAX_PATH);
	if (device_id == NULL)
		goto out;

	// Build a hash table associating a CM Device ID of an USB device with the SetupDI Device Interface Path
	// of its parent hub - this is needed to retrieve the device speed
	dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_USB_HUB, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
	if (dev_info != INVALID_HANDLE_VALUE) {
		if (htab_create(DEVID_HTAB_SIZE, &htab_devid)) {
			dev_info_data.cbSize = sizeof(dev_info_data);
			for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
				if (usb_debug)
					uprintf("Processing Hub %d:", i + 1);
				devint_detail_data = NULL;
				devint_data.cbSize = sizeof(devint_data);
				// Only care about the first interface (MemberIndex 0)
				if ( (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_USB_HUB, 0, &devint_data))
				  && (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) 
				  && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) 
				  && ((devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size)) != NULL) ) {
					devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
					if (SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) {

						// Find the Device IDs for all the children of this hub
						if (CM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) {
							device_id[0] = 0;
							s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath);
							if ((s>= 0) && (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) {
								if ((k = htab_hash(device_id, &htab_devid)) != 0) {
									htab_devid.table[k].data = (void*)(uintptr_t)s;
								}
								if (usb_debug)
									uprintf("  Found ID[%03d]: %s", k, device_id);
								while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) {
									device_id[0] = 0;
									if (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS) {
										if ((k = htab_hash(device_id, &htab_devid)) != 0) {
											htab_devid.table[k].data = (void*)(uintptr_t)s;
										}
										if (usb_debug)
											uprintf("  Found ID[%03d]: %s", k, device_id);
									}
								}
							}
						}
					}
					free(devint_detail_data);
				}
			}
		}
		SetupDiDestroyDeviceInfoList(dev_info);
	}
	free(device_id);

	// Build a single list of Device IDs from all the storage enumerators we know of
	full_list_size = 0;
	ulFlags = CM_GETIDLIST_FILTER_SERVICE;
	if (nWindowsVersion >= WINDOWS_7)
		ulFlags |= CM_GETIDLIST_FILTER_PRESENT;
	for (s=0; s<ARRAYSIZE(storage_name); s++) {
		// Get a list of device IDs for all USB storage devices
		// This will be used to find if a device is UASP
		if (CM_Get_Device_ID_List_SizeA(&list_size[s], storage_name[s], ulFlags) != CR_SUCCESS)
			list_size[s] = 0;
		if (list_size[s] != 0)
			full_list_size += list_size[s]-1;	// remove extra NUL terminator
	}
	devid_list = NULL;
	if (full_list_size != 0) {
		full_list_size += 1;	// add extra NUL terminator
		devid_list = (char*)malloc(full_list_size);
		if (devid_list == NULL) {
			uprintf("Could not allocate Device ID list\n");
			return FALSE;
		}
		for (s=0, i=0; s<ARRAYSIZE(storage_name); s++) {
			list_start[s] = i;
			if (list_size[s] > 1) {
				if (CM_Get_Device_ID_ListA(storage_name[s], &devid_list[i], list_size[s], ulFlags) != CR_SUCCESS)
					continue;
				if (usb_debug) {
					uprintf("Processing IDs belonging to %s:", storage_name[s]);
					for (device_id = &devid_list[i]; *device_id != 0; device_id += strlen(device_id) + 1)
						uprintf("  %s", device_id);
				}
				// The list_size is sometimes larger than required thus we need to find the real end
				for (i += list_size[s]; i > 2; i--) {
					if ((devid_list[i-2] != '\0') && (devid_list[i-1] == '\0') && (devid_list[i] == '\0'))
						break;
				}
			}
		}
	}

	// Now use SetupDi to enumerate all our storage devices
	dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
	if (dev_info == INVALID_HANDLE_VALUE) {
		uprintf("SetupDiGetClassDevs (Interface) failed: %s\n", WindowsErrorString());
		goto out;
	}
	dev_info_data.cbSize = sizeof(dev_info_data);
	for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
		memset(buffer, 0, sizeof(buffer));
		method_str = "";
		if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME,
				&datatype, (LPBYTE)buffer, sizeof(buffer), &size)) {
			uprintf("SetupDiGetDeviceRegistryProperty (Enumerator Name) failed: %s\n", WindowsErrorString());
			continue;
		}
		// UASP drives are listed under SCSI (along with regular SYSTEM drives => "DANGER, WILL ROBINSON!!!")
		is_SCSI = (safe_stricmp(buffer, scsi_name) == 0);
		if ((safe_stricmp(buffer, storage_name[0]) != 0) && (!is_SCSI))
			continue;

		// We can't use the friendly name to find if a drive is a VHD, as friendly name string gets translated
		// according to your locale, so we poke the Hardware ID
		memset(&props, 0, sizeof(props));
		memset(buffer, 0, sizeof(buffer));
		props.is_VHD = SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_HARDWAREID,
			&datatype, (LPBYTE)buffer, sizeof(buffer), &size) && IsVHD(buffer);
		if (usb_debug)
			uprintf("Processing Device: '%s'", buffer);

		memset(buffer, 0, sizeof(buffer));
		if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_FRIENDLYNAME,
				&datatype, (LPBYTE)buffer, sizeof(buffer), &size)) {
			uprintf("SetupDiGetDeviceRegistryProperty (Friendly Name) failed: %s\n", WindowsErrorString());
			// We can afford a failure on this call - just replace the name with "USB Storage Device (Generic)"
			safe_strcpy(buffer, sizeof(buffer), lmprintf(MSG_045));
		} else if ((!props.is_VHD) && (devid_list != NULL)) {
			// Get the properties of the device. We could avoid doing this lookup every time by keeping
			// a lookup table, but there shouldn't be that many USB storage devices connected...
			// NB: Each of these Device IDs have an _only_ child, from which we get the Device Instance match.
			for (device_id = devid_list; *device_id != 0; device_id += strlen(device_id) + 1) {
				if ( (CM_Locate_DevNodeA(&parent_inst, device_id, 0) == CR_SUCCESS)
				  && (CM_Get_Child(&device_inst, parent_inst, 0) == CR_SUCCESS)
				  && (device_inst == dev_info_data.DevInst) ) {
					// If we're not dealing with the USBSTOR part of our list, then this is an UASP device
					props.is_UASP = ((((uintptr_t)device_id)+2) >= ((uintptr_t)devid_list)+list_start[1]);
					// Now get the properties of the device, and its Device ID, which we need to populate the properties
					j = htab_hash(device_id, &htab_devid);
					if (usb_debug)
						uprintf("  Matched with ID[%03d]: %s", j, device_id);
					// If the hash didn't match a populated string in dev_if_path[] (htab_devid.table[j].data > 0),
					// we might have an extra vendor driver in between (e.g. "ASUS USB 3.0 Boost Storage Driver"
					// for UASP devices in ASUS "Turbo Mode" or "Apple Mobile Device USB Driver" for iPods)
					// so try to see if we can match the grandparent.
					if ( ((uint32_t)htab_devid.table[j].data == 0)
					  && (CM_Get_Parent(&grandparent_inst, parent_inst, 0) == CR_SUCCESS)
					  && (CM_Get_Device_IDA(grandparent_inst, str, MAX_PATH, 0) == CR_SUCCESS) ) {
						device_id = str;
						method_str = "[GP]";
						j = htab_hash(device_id, &htab_devid);
						if (usb_debug)
							uprintf("  Matched with (GP) ID[%03d]: %s", j, device_id);
					}
					if ((uint32_t)htab_devid.table[j].data > 0)
						GetUSBProperties(dev_if_path.String[(uint32_t)htab_devid.table[j].data], device_id, &props);
					if (usb_debug)
						uprintf("  Props VID:PID = %04X:%04X", props.vid, props.pid);

					// If previous calls still didn't succeed, try reading the VID:PID from the device_id
					if ((props.vid == 0) && (props.pid == 0)) {
						BOOL post_backslash = FALSE;
						method_str = "[ID]";
						for (j=0, k=0; (j<strlen(device_id))&&(k<2); j++) {
							// The ID is in the form USB_VENDOR_BUSID\VID_xxxx&PID_xxxx\...
							if (device_id[j] == '\\')
								post_backslash = TRUE;
							if (!post_backslash)
								continue;
							if (device_id[j] == '_') {
								props.pid = (uint16_t)strtoul(&device_id[j+1], NULL, 16);
								if (k++==0)
									props.vid = props.pid;
							}
						}
					}
				}
			}
		}
		if (props.is_VHD) {
			uprintf("Found VHD device '%s'", buffer);
		} else {
			if ((props.vid == 0) && (props.pid == 0)) {
				if (is_SCSI) {
					// If we have an SCSI drive and couldn't get a VID:PID, we are most likely
					// dealing with a system drive => eliminate it!
					if (usb_debug)
						uprintf("  Non USB => Eliminated");
					continue;
				}
				safe_strcpy(str, sizeof(str), "????:????");	// Couldn't figure VID:PID
			} else {
				static_sprintf(str, "%04X:%04X", props.vid, props.pid);
			}
			if (props.speed >= USB_SPEED_MAX)
				props.speed = 0;
			uprintf("Found %s%s%s device '%s' (%s) %s\n", props.is_UASP?"UAS (":"", 
				usb_speed_name[props.speed], props.is_UASP?")":"", buffer, str, method_str);
			if (props.is_LowerSpeed)
				uprintf("NOTE: This device is an USB 3.0 device operating at lower speed...");
		}
		devint_data.cbSize = sizeof(devint_data);
		hDrive = INVALID_HANDLE_VALUE;
		devint_detail_data = NULL;
		for (j=0; ;j++) {
			safe_closehandle(hDrive);
			safe_free(devint_detail_data);

			if (!SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_DISK, j, &devint_data)) {
				if(GetLastError() != ERROR_NO_MORE_ITEMS) {
					uprintf("SetupDiEnumDeviceInterfaces failed: %s\n", WindowsErrorString());
				} else {
					uprintf("A device was eliminated because it didn't report itself as a disk\n");
				}
				break;
			}

			if (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) {
				if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
					devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size);
					if (devint_detail_data == NULL) {
						uprintf("Unable to allocate data for SP_DEVICE_INTERFACE_DETAIL_DATA\n");
						continue;
					}
					devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
				} else {
					uprintf("SetupDiGetDeviceInterfaceDetail (dummy) failed: %s\n", WindowsErrorString());
					continue;
				}
			}
			if (devint_detail_data == NULL) {
				uprintf("SetupDiGetDeviceInterfaceDetail (dummy) - no data was allocated\n");
				continue;
			}
			if(!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) {
				uprintf("SetupDiGetDeviceInterfaceDetail (actual) failed: %s\n", WindowsErrorString());
				continue;
			}

			hDrive = CreateFileA(devint_detail_data->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
				NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if(hDrive == INVALID_HANDLE_VALUE) {
				uprintf("Could not open '%s': %s\n", devint_detail_data->DevicePath, WindowsErrorString());
				continue;
			}

			drive_number = GetDriveNumber(hDrive, devint_detail_data->DevicePath);
			if (drive_number < 0)
				continue;

			drive_index = drive_number + DRIVE_INDEX_MIN;
			if (!IsMediaPresent(drive_index)) {
				uprintf("Device eliminated because it appears to contain no media\n");
				safe_closehandle(hDrive);
				safe_free(devint_detail_data);
				break;
			}

			if (GetDriveLabel(drive_index, drive_letters, &label)) {
				if ((!enable_HDDs) && (!props.is_VHD) &&
					((score = IsHDD(drive_index, (uint16_t)props.vid, (uint16_t)props.pid, buffer)) > 0)) {
					uprintf("Device eliminated because it was detected as an USB Hard Drive (score %d > 0)\n", score);
					uprintf("If this device is not an USB Hard Drive, please e-mail the author of this application\n");
					uprintf("NOTE: You can enable the listing of USB Hard Drives in 'Advanced Options' (after clicking the white triangle)");
					safe_closehandle(hDrive);
					safe_free(devint_detail_data);
					break;
				}

				// The empty string is returned for drives that don't have any volumes assigned
				if (drive_letters[0] == 0) {
					entry = lmprintf(MSG_046, label, drive_number,
						SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units));
				} else {
					// Find the UEFI:TOGO partition(s) (and eliminate them form our listing)
					for (k=0; drive_letters[k]; k++) {
						uefi_togo_check[0] = drive_letters[k];
						if (PathFileExistsA(uefi_togo_check)) {
							for (l=k; drive_letters[l]; l++)
								drive_letters[l] = drive_letters[l+1];
							k--;
						}
					}
					// We have multiple volumes assigned to the same device (multiple partitions)
					// If that is the case, use "Multiple Volumes" instead of the label
					safe_strcpy(entry_msg, sizeof(entry_msg), ((drive_letters[0] != 0) && (drive_letters[1] != 0))?
						lmprintf(MSG_047):label);
					for (k=0; drive_letters[k]; k++) {
						// Append all the drive letters we detected
						letter_name[2] = drive_letters[k];
						if (right_to_left_mode)
							safe_strcat(entry_msg, sizeof(entry_msg), RIGHT_TO_LEFT_MARK);
						safe_strcat(entry_msg, sizeof(entry_msg), letter_name);
						if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A')) break;
					}
					// Repeat as we need to break the outside loop
					if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A')) {
						uprintf("Removing %c: from the list: This is the disk from which " APPLICATION_NAME " is running!\n", app_dir[0]);
						safe_closehandle(hDrive);
						safe_free(devint_detail_data);
						break;
					}
					safe_sprintf(&entry_msg[strlen(entry_msg)], sizeof(entry_msg) - strlen(entry_msg),
						"%s [%s]", (right_to_left_mode)?RIGHT_TO_LEFT_MARK:"", SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units));
					entry = entry_msg;
				}

				// Must ensure that the combo box is UNSORTED for indexes to be the same
				StrArrayAdd(&DriveID, buffer);
				StrArrayAdd(&DriveLabel, label);

				IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), drive_index));
				maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry));
				safe_closehandle(hDrive);
				safe_free(devint_detail_data);
				break;
			}
		}
	}
	SetupDiDestroyDeviceInfoList(dev_info);

	// Adjust the Dropdown width to the maximum text size
	SendMessage(hDeviceList, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);

	if (devnum >= DRIVE_INDEX_MIN) {
		for (i=0; i<ComboBox_GetCount(hDeviceList); i++) {
			if ((DWORD)ComboBox_GetItemData(hDeviceList, i) == devnum) {
				found = TRUE;
				break;
			}
		}
	}
	if (!found)
		i = 0;
	IGNORE_RETVAL(ComboBox_SetCurSel(hDeviceList, i));
	SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE<<16) | IDC_DEVICE, 0);
	SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE<<16) | IDC_FILESYSTEM,
		ComboBox_GetCurSel(hFileSystem));
	r = TRUE;

out:
	// Set 'Start' as the selected button, so that tab selection works
	SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDC_START), TRUE);
	safe_free(devid_list);
	StrArrayDestroy(&dev_if_path);
	htab_destroy(&htab_devid);
	return r;
}
/*
 * Get the current latency timer value for ftdi devices.
 * return value read on success or -1 if any error occurs.
 */
jint get_latency_timer_value(JNIEnv *env, jstring comPortName) {

	const jchar* port_name = NULL;
	int x = 0;
	BOOL ret = FALSE;
	LONG status = 0;
	DWORD error_code = 0;
	DWORD size = 0;
	DWORD regproptype;
	DWORD charbuffer_size = 0;
	DWORD driver_name_size = 0;
	ULONG buffer_size = 0;
	DWORD usb_member_index = 0;
	HDEVINFO usb_dev_info_set;
	SP_DEVINFO_DATA usb_dev_instance;
	ULONG devprop_buffer_size = 0;
	DEVPROPTYPE proptype;
	CONFIGRET cmret = 0;
	DEVINST firstchild = 0;
	DEVINST next_sibling = 0;
	DEVINST current_sibling = 0;
	DWORD timer_value;
	DWORD timervalue_size;

	/* size of these buffers is hardcoded in functions using them */
	TCHAR buffer[1024];
	TCHAR devprop_buffer[1024];
	TCHAR keybuf[1024];
	TCHAR charbuffer[128];
	char cmerror[256];

	/* extract com port name to match (as an array of Unicode characters) */
	port_name = (*env)->GetStringChars(env, comPortName, JNI_FALSE);
	if ((port_name == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
		throw_serialcom_exception(env, 3, 0, E_GETSTRUTFCHARSTR);
		return -1;
	}

	/* get information set for all usb devices matching the GUID */
	usb_dev_info_set = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
	if (usb_dev_info_set == INVALID_HANDLE_VALUE) {
		SetupDiDestroyDeviceInfoList(usb_dev_info_set);
		(*env)->ReleaseStringChars(env, comPortName, port_name);
		throw_serialcom_exception(env, 4, HRESULT_FROM_SETUPAPI(GetLastError()), NULL);
		return -1;
	}

	/* enumerate all devices in this information set */
	usb_member_index = 0;

	while (1) {
		ZeroMemory(&usb_dev_instance, sizeof(usb_dev_instance));
		usb_dev_instance.cbSize = sizeof(usb_dev_instance);

		/* from information set, get device by index */
		ret = SetupDiEnumDeviceInfo(usb_dev_info_set, usb_member_index, &usb_dev_instance);
		if (ret == FALSE) {
			error_code = GetLastError();
			if (error_code == ERROR_NO_MORE_ITEMS) {
				break;
			}
			else {
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				throw_serialcom_exception(env, 4, HRESULT_FROM_SETUPAPI(error_code), NULL);
				return -1;
			}
		}

		/* for this device find its instance ID (USB\VID_04D8&PID_00DF\000098037)
		 * this is variable 'Device Instance Path' in device manager. */
		memset(buffer, '\0', 1024);
		ret = SetupDiGetDeviceInstanceId(usb_dev_info_set, &usb_dev_instance, buffer, 1024, &size);
		if (ret == FALSE) {
			SetupDiDestroyDeviceInfoList(usb_dev_info_set);
			(*env)->ReleaseStringChars(env, comPortName, port_name);
			throw_serialcom_exception(env, 4, HRESULT_FROM_SETUPAPI(GetLastError()), NULL);
			return -1;
		}

		cmret = CM_Get_Child(&firstchild, usb_dev_instance.DevInst, 0);
		if (cmret != CR_SUCCESS) {
			if (cmret == CR_NO_SUCH_DEVNODE) {
				/* this device does not have any child, so check if this device itself is a "Ports" class device or not */
				memset(devprop_buffer, '\0', 1024);
				ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_CLASSGUID, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
				if (ret == FALSE) {
					SetupDiDestroyDeviceInfoList(usb_dev_info_set);
					(*env)->ReleaseStringChars(env, comPortName, port_name);
					throw_serialcom_exception(env, 4, HRESULT_FROM_SETUPAPI(GetLastError()), NULL);
					return -1;
				}

				/* match GUID */
				ret = _tcsicmp(devprop_buffer, TEXT("{4D36E978-E325-11CE-BFC1-08002BE10318}"));
				if (ret != 0) {
					usb_member_index++;
					continue;
				}

				/* reaching here means that the device is COM port device (CDC/ACM), get its COM port name/number
				 * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS\VID_VID+PID_PID+Serial_Number\0000\DeviceParameters\PortName
				 * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_VID+PID_PID\Serial_Number\DeviceParameters\PortName */
				memset(keybuf, '\0', 1024);
				_stprintf_s(keybuf, 1024, TEXT("SYSTEM\\CurrentControlSet\\Enum\\%s\\Device Parameters"), buffer);

				charbuffer_size = sizeof(charbuffer);
				memset(charbuffer, '\0', 128);
				status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("PortName"), RRF_RT_REG_SZ, NULL, (PVOID)charbuffer, &charbuffer_size);
				if (status != ERROR_SUCCESS) {
					SetupDiDestroyDeviceInfoList(usb_dev_info_set);
					(*env)->ReleaseStringChars(env, comPortName, port_name);
					throw_serialcom_exception(env, 4, GetLastError(), NULL);
					return -1;
				}

				/* match port name */
				ret = _tcsicmp(charbuffer, port_name);
				if (ret != 0) {
					usb_member_index++;
					continue;
				}

				/* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS\VID_VID+PID_PID+Serial_Number\0000\DeviceParameters\LatencyTimer */
				status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("LatencyTimer"), RRF_RT_REG_DWORD, NULL, &timer_value, &timervalue_size);
				if (status != ERROR_SUCCESS) {
					SetupDiDestroyDeviceInfoList(usb_dev_info_set);
					(*env)->ReleaseStringChars(env, comPortName, port_name);
					throw_serialcom_exception(env, 4, status, NULL);
					return -1;
				}

				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				return (jint)timer_value;
			}else {
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				_snprintf_s(cmerror, 256, 256, "CM_Get_DevNode_Registry_Property CR_xxxx error code : 0x%X\0", cmret);
				throw_serialcom_exception(env, 3, 0, cmerror);
				return -1;
			}
		}

		/* reaching here means that this USB device has at-least one child device node, examine child now */

		devprop_buffer_size = sizeof(devprop_buffer);
		memset(devprop_buffer, '\0', sizeof(devprop_buffer));
		cmret = CM_Get_DevNode_Registry_Property(firstchild, CM_DRP_CLASSGUID, &proptype, (PVOID)devprop_buffer, &devprop_buffer_size, 0);
		if (cmret != CR_SUCCESS) {
			SetupDiDestroyDeviceInfoList(usb_dev_info_set);
			(*env)->ReleaseStringChars(env, comPortName, port_name);
			_snprintf_s(cmerror, 256, 256, "CM_Get_DevNode_Registry_Property CR_xxxx error code : 0x%X\0", cmret);
			throw_serialcom_exception(env, 3, 0, cmerror);
			return -1;
		}

		/* match GUID */
		ret = _tcsicmp(devprop_buffer, TEXT("{4D36E978-E325-11CE-BFC1-08002BE10318}"));
		if (ret == 0) {
			/* reaching here means that the child device is COM port device (CDC/ACM), get its COM port name/number */
			memset(buffer, '\0', 1024);
			cmret = CM_Get_Device_ID(firstchild, buffer, 1024, 0);
			if (cmret != CR_SUCCESS) {
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				_snprintf_s(cmerror, 256, 256, "CM_Get_Device_ID CR_xxxx error code : 0x%X\0", cmret);
				throw_serialcom_exception(env, 3, 0, cmerror);
				return -1;
			}

			memset(keybuf, '\0', 1024);
			_stprintf_s(keybuf, 1024, TEXT("SYSTEM\\CurrentControlSet\\Enum\\%s\\Device Parameters"), buffer);

			charbuffer_size = sizeof(charbuffer);
			memset(charbuffer, '\0', 128);

			status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("PortName"), RRF_RT_REG_SZ, NULL, (PVOID)charbuffer, &charbuffer_size);
			if (status != ERROR_SUCCESS) {
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				throw_serialcom_exception(env, 4, GetLastError(), NULL);
				return -1;
			}

			/* match port name */
			ret = _tcsicmp(charbuffer, port_name);
			if (ret == 0) {
				status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("LatencyTimer"), RRF_RT_REG_DWORD, NULL, &timer_value, &timervalue_size);
				if (status != ERROR_SUCCESS) {
					SetupDiDestroyDeviceInfoList(usb_dev_info_set);
					(*env)->ReleaseStringChars(env, comPortName, port_name);
					throw_serialcom_exception(env, 4, GetLastError(), NULL);
					return -1;
				}

				/* clean up and return timer value found */
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				return (jint)timer_value;
			}
		}

		/* reaching here means first child of this USB device was not CDC/ACM interface, need to
		   iterate over all the interfaces (siblings) this device has examining them for matching our criteria */
		current_sibling = firstchild;
		while (1) {
			cmret = CM_Get_Sibling(&next_sibling, current_sibling, 0);
			if (cmret != CR_SUCCESS) {
				if (cmret == CR_NO_SUCH_DEVNODE) {
					/* done iterating over all interfaces, move to next examine next USB device */
					break;
				}
				else {
					SetupDiDestroyDeviceInfoList(usb_dev_info_set);
					_snprintf_s(cmerror, 256, 256, "CM_Get_Sibling failed with CR_xxxx error code : 0x%X\0", cmret);
					(*env)->ReleaseStringChars(env, comPortName, port_name);
					throw_serialcom_exception(env, 3, 0, cmerror);
					return -1;
				}

			}

			/* reaching here means USB device has more than 1 interfaces, get class of this interface (sibling) */
			devprop_buffer_size = sizeof(devprop_buffer);
			memset(devprop_buffer, '\0', sizeof(devprop_buffer));
			cmret = CM_Get_DevNode_Registry_Property(next_sibling, CM_DRP_CLASSGUID, &proptype, (VOID *)devprop_buffer, &devprop_buffer_size, 0);
			if (cmret != CR_SUCCESS) {
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				_snprintf_s(cmerror, 256, 256, "CM_Get_DevNode_Registry_Property failed with CR_xxxx error code : 0x%X\0", cmret);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				throw_serialcom_exception(env, 3, 0, cmerror);
				return -1;
			}

			/* match GUID for this sibling */
			ret = _tcsicmp(devprop_buffer, TEXT("{4D36E978-E325-11CE-BFC1-08002BE10318}"));
			if (ret != 0) {
				/* this interface is not CDC/ACM, loop over to next interface */
				current_sibling = next_sibling;
				continue;
			}

			/* reaching here means that this sibling (interface) is a CDC/ACM type, get its COM port name/number */
			buffer_size = (ULONG)_tcslen(buffer);
			memset(buffer, '\0', 1024);
			cmret = CM_Get_Device_ID(next_sibling, buffer, 1024, 0);
			if (cmret != CR_SUCCESS) {
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				_snprintf_s(cmerror, 256, 256, "CM_Get_Device_ID failed with CR_xxxx error code : 0x%X\0", cmret);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				throw_serialcom_exception(env, 3, 0, cmerror);
				return -1;
			}

			memset(keybuf, '\0', 1024);
			_stprintf_s(keybuf, 1024, TEXT("SYSTEM\\CurrentControlSet\\Enum\\%s\\Device Parameters"), buffer);

			charbuffer_size = sizeof(charbuffer);
			memset(charbuffer, '\0', 128);
			status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("PortName"), RRF_RT_REG_SZ, NULL, (PVOID)charbuffer, &charbuffer_size);
			if (status != ERROR_SUCCESS) {
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				throw_serialcom_exception(env, 4, GetLastError(), NULL);
				return -1;
			}

			/* match port name */
			ret = _tcsicmp(charbuffer, port_name);
			if (ret == 0) {
				status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("LatencyTimer"), RRF_RT_REG_DWORD, NULL, &timer_value, &timervalue_size);
				if (status != ERROR_SUCCESS) {
					SetupDiDestroyDeviceInfoList(usb_dev_info_set);
					(*env)->ReleaseStringChars(env, comPortName, port_name);
					throw_serialcom_exception(env, 4, GetLastError(), NULL);
					return -1;
				}

				/* clean up and return timer value found */
				SetupDiDestroyDeviceInfoList(usb_dev_info_set);
				(*env)->ReleaseStringChars(env, comPortName, port_name);
				return (jint)timer_value;
			}

			/* set this sibling as base sibling for fetching next sibling, loop over to get and check next
			interface (sibling) */
			current_sibling = next_sibling;
		}

		/* increment to get and examine the next usb device for COM ports class */
		usb_member_index++;
	}

	/* reaching here means that probably given com port does not represent ftdi device or does not exist at all */
	SetupDiDestroyDeviceInfoList(usb_dev_info_set);
	(*env)->ReleaseStringChars(env, comPortName, port_name);
	throw_serialcom_exception(env, 3, 0, E_NOTFTDIPORT);
	return -1;
}
Beispiel #9
0
/*
 * Refresh the list of USB devices
 */
BOOL GetDevices(DWORD devnum)
{
	// List of USB storage drivers we know - list may be incomplete!
	const char* usbstor_name[] = {
		// Standard MS USB storage driver
		"USBSTOR",
		// USB card readers, with proprietary drivers (Realtek,etc...)
		// Mostly "guessed" from http://www.carrona.org/dvrref.php
		"RTSUER", "CMIUCR", "EUCR",
		// UASP Drivers *MUST* be listed after this, starting with "UASPSTOR"
		// (which is Microsoft's native UASP driver for Windows 8 and later)
		// as we use "UASPSTOR" as a delimiter
		"UASPSTOR", "VUSBSTOR", "ETRONSTOR", "ASUSSTPT"
	};
	// These are the generic (non USB) storage enumerators we also test
	const char* genstor_name[] = {
		// Generic storage drivers (Careful now!)
		"SCSI", // "STORAGE",	// "STORAGE" is used by 'Storage Spaces" and stuff => DANGEROUS!
		// Non-USB card reader drivers - This list *MUST* start with "SD" (delimiter)
		// See http://itdoc.hitachi.co.jp/manuals/3021/30213B5200e/DMDS0094.HTM
		// Also  http://www.carrona.org/dvrref.php. NB: These should be reported
		// as enumerators by Rufus when Enum Debug is enabled
		"SD", "PCISTOR", "RTSOR", "JMCR", "JMCF", "RIMMPTSK", "RIMSPTSK", "RISD", "RIXDPTSK",
		"TI21SONY", "ESD7SK", "ESM7SK", "O2MD", "O2SD", "VIACR"
	};
	// Oh, and we also have card devices (e.g. 'SCSI\DiskO2Micro_SD_...') under the SCSI enumerator...
	const char* scsi_disk_prefix = "SCSI\\Disk";
	const char* scsi_card_name[] = {
		"_SD_", "_SDHC_", "_MMC_", "_MS_", "_MSPro_", "_xDPicture_", "_O2Media_"
	};
	const char* usb_speed_name[USB_SPEED_MAX] = { "USB", "USB 1.0", "USB 1.1", "USB 2.0", "USB 3.0" };
	// Hash table and String Array used to match a Device ID with the parent hub's Device Interface Path
	htab_table htab_devid = HTAB_EMPTY;
	StrArray dev_if_path;
	char letter_name[] = " (?:)";
	char drive_name[] = "?:\\";
	char uefi_togo_check[] = "?:\\EFI\\Rufus\\ntfs_x64.efi";
	char scsi_card_name_copy[16];
	BOOL r = FALSE, found = FALSE, post_backslash;
	HDEVINFO dev_info = NULL;
	SP_DEVINFO_DATA dev_info_data;
	SP_DEVICE_INTERFACE_DATA devint_data;
	PSP_DEVICE_INTERFACE_DETAIL_DATA_A devint_detail_data;
	DEVINST parent_inst, grandparent_inst, device_inst;
	DWORD size, i, j, k, l, datatype, drive_index;
	DWORD uasp_start = ARRAYSIZE(usbstor_name), card_start = ARRAYSIZE(genstor_name);
	ULONG list_size[ARRAYSIZE(usbstor_name)] = { 0 }, list_start[ARRAYSIZE(usbstor_name)] = { 0 }, full_list_size, ulFlags;
	HANDLE hDrive;
	LONG maxwidth = 0;
	int s, score, drive_number, remove_drive;
	char drive_letters[27], *device_id, *devid_list = NULL, entry_msg[128];
	char *p, *label, *entry, buffer[MAX_PATH], str[MAX_PATH], *method_str, *hub_path;
	usb_device_props props;

	IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList));
	StrArrayClear(&DriveID);
	StrArrayClear(&DriveLabel);
	StrArrayClear(&DriveHub);
	StrArrayCreate(&dev_if_path, 128);
	// Add a dummy for string index zero, as this is what non matching hashes will point to
	StrArrayAdd(&dev_if_path, "", TRUE);

	device_id = (char*)malloc(MAX_PATH);
	if (device_id == NULL)
		goto out;

	// Build a hash table associating a CM Device ID of an USB device with the SetupDI Device Interface Path
	// of its parent hub - this is needed to retrieve the device speed
	dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_USB_HUB, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
	if (dev_info != INVALID_HANDLE_VALUE) {
		if (htab_create(DEVID_HTAB_SIZE, &htab_devid)) {
			dev_info_data.cbSize = sizeof(dev_info_data);
			for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
				uuprintf("Processing Hub %d:", i + 1);
				devint_detail_data = NULL;
				devint_data.cbSize = sizeof(devint_data);
				// Only care about the first interface (MemberIndex 0)
				if ( (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_USB_HUB, 0, &devint_data))
				  && (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL))
				  && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
				  && ((devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size)) != NULL) ) {
					devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
					if (SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) {

						// Find the Device IDs for all the children of this hub
						if (CM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) {
							device_id[0] = 0;
							s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath, TRUE);
							uuprintf("  Hub[%d] = '%s'", s, devint_detail_data->DevicePath);
							if ((s>= 0) && (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) {
								ToUpper(device_id);
								if ((k = htab_hash(device_id, &htab_devid)) != 0) {
									htab_devid.table[k].data = (void*)(uintptr_t)s;
								}
								uuprintf("  Found ID[%03d]: %s", k, device_id);
								while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) {
									device_id[0] = 0;
									if (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS) {
										ToUpper(device_id);
										if ((k = htab_hash(device_id, &htab_devid)) != 0) {
											htab_devid.table[k].data = (void*)(uintptr_t)s;
										}
										uuprintf("  Found ID[%03d]: %s", k, device_id);
									}
								}
							}
						}
					}
					free(devint_detail_data);
				}
			}
		}
		SetupDiDestroyDeviceInfoList(dev_info);
	}
	free(device_id);

	// Build a single list of Device IDs from all the storage enumerators we know of
	full_list_size = 0;
	ulFlags = CM_GETIDLIST_FILTER_SERVICE | CM_GETIDLIST_FILTER_PRESENT;
	for (s=0; s<ARRAYSIZE(usbstor_name); s++) {
		// Get a list of device IDs for all USB storage devices
		// This will be used to find if a device is UASP
		// Also compute the uasp_start index
		if (strcmp(usbstor_name[s], "UASPSTOR") == 0)
			uasp_start = s;
		if (CM_Get_Device_ID_List_SizeA(&list_size[s], usbstor_name[s], ulFlags) != CR_SUCCESS)
			list_size[s] = 0;
		if (list_size[s] != 0)
			full_list_size += list_size[s]-1;	// remove extra NUL terminator
	}
	// Compute the card_start index
	for (s=0; s<ARRAYSIZE(genstor_name); s++) {
		if (strcmp(genstor_name[s], "SD") == 0)
			card_start = s;
	}

	// Better safe than sorry. And yeah, we could have used arrays of
	// arrays to avoid this, but it's more readable this way.
	assert((uasp_start > 0) && (uasp_start < ARRAYSIZE(usbstor_name)));
	assert((card_start > 0) && (card_start < ARRAYSIZE(genstor_name)));

	devid_list = NULL;
	if (full_list_size != 0) {
		full_list_size += 1;	// add extra NUL terminator
		devid_list = (char*)malloc(full_list_size);
		if (devid_list == NULL) {
			uprintf("Could not allocate Device ID list\n");
			goto out;
		}
		for (s=0, i=0; s<ARRAYSIZE(usbstor_name); s++) {
			list_start[s] = i;
			if (list_size[s] > 1) {
				if (CM_Get_Device_ID_ListA(usbstor_name[s], &devid_list[i], list_size[s], ulFlags) != CR_SUCCESS)
					continue;
				if (usb_debug) {
					uprintf("Processing IDs belonging to '%s':", usbstor_name[s]);
					for (device_id = &devid_list[i]; *device_id != 0; device_id += strlen(device_id) + 1)
						uprintf("  %s", device_id);
				}
				// The list_size is sometimes larger than required thus we need to find the real end
				for (i += list_size[s]; i > 2; i--) {
					if ((devid_list[i-2] != '\0') && (devid_list[i-1] == '\0') && (devid_list[i] == '\0'))
						break;
				}
			}
		}
	}

	// Now use SetupDi to enumerate all our disk storage devices
	dev_info = SetupDiGetClassDevsA(&_GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
	if (dev_info == INVALID_HANDLE_VALUE) {
		uprintf("SetupDiGetClassDevs (Interface) failed: %s\n", WindowsErrorString());
		goto out;
	}
	dev_info_data.cbSize = sizeof(dev_info_data);
	for (i=0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
		memset(buffer, 0, sizeof(buffer));
		memset(&props, 0, sizeof(props));
		method_str = "";
		hub_path = NULL;
		if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME,
				&datatype, (LPBYTE)buffer, sizeof(buffer), &size)) {
			uprintf("SetupDiGetDeviceRegistryProperty (Enumerator Name) failed: %s\n", WindowsErrorString());
			continue;
		}

		for (j = 0; j < ARRAYSIZE(usbstor_name); j++) {
			if (safe_stricmp(buffer, usbstor_name[0]) == 0) {
				props.is_USB = TRUE;
				if ((j != 0) && (j < uasp_start))
					props.is_CARD = TRUE;
				break;
			}
		}

		// UASP drives are listed under SCSI, and we also have non USB card readers to populate
		for (j = 0; j < ARRAYSIZE(genstor_name); j++) {
			if (safe_stricmp(buffer, genstor_name[j]) == 0) {
				props.is_SCSI = TRUE;
				if (j >= card_start)
					props.is_CARD = TRUE;
				break;
			}
		}

		uuprintf("Processing '%s' device:", buffer);
		if ((!props.is_USB) && (!props.is_SCSI)) {
			uuprintf("  Disabled by policy");
			continue;
		}

		// We can't use the friendly name to find if a drive is a VHD, as friendly name string gets translated
		// according to your locale, so we poke the Hardware ID
		memset(buffer, 0, sizeof(buffer));
		props.is_VHD = SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_HARDWAREID,
			&datatype, (LPBYTE)buffer, sizeof(buffer), &size) && IsVHD(buffer);
		// Additional detection for SCSI card readers
		if ((!props.is_CARD) && (safe_strnicmp(buffer, scsi_disk_prefix, sizeof(scsi_disk_prefix)-1) == 0)) {
			for (j = 0; j < ARRAYSIZE(scsi_card_name); j++) {
				static_strcpy(scsi_card_name_copy, scsi_card_name[j]);
				if (safe_strstr(buffer, scsi_card_name_copy) != NULL) {
					props.is_CARD = TRUE;
					break;
				}
				// Also test for "_SD&" instead of "_SD_" and so on to allow for devices like
				// "SCSI\DiskRicoh_Storage_SD&REV_3.0" to be detected.
				scsi_card_name_copy[strlen(scsi_card_name_copy) - 1] = '&';
				if (safe_strstr(buffer, scsi_card_name_copy) != NULL) {
					props.is_CARD = TRUE;
					break;
				}
			}
		}
		uuprintf("  Hardware ID: '%s'", buffer);

		memset(buffer, 0, sizeof(buffer));
		props.is_Removable = SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_REMOVAL_POLICY,
			&datatype, (LPBYTE)buffer, sizeof(buffer), &size) && IsRemovable(buffer);

		memset(buffer, 0, sizeof(buffer));
		if (!SetupDiGetDeviceRegistryPropertyU(dev_info, &dev_info_data, SPDRP_FRIENDLYNAME,
				&datatype, (LPBYTE)buffer, sizeof(buffer), &size)) {
			uprintf("SetupDiGetDeviceRegistryProperty (Friendly Name) failed: %s\n", WindowsErrorString());
			// We can afford a failure on this call - just replace the name with "USB Storage Device (Generic)"
			static_strcpy(buffer, lmprintf(MSG_045));
		} else if ((!props.is_VHD) && (devid_list != NULL)) {
			// Get the properties of the device. We could avoid doing this lookup every time by keeping
			// a lookup table, but there shouldn't be that many USB storage devices connected...
			// NB: Each of these Device IDs should have a child, from which we get the Device Instance match.
			for (device_id = devid_list; *device_id != 0; device_id += strlen(device_id) + 1) {
				if (CM_Locate_DevNodeA(&parent_inst, device_id, 0) != CR_SUCCESS) {
					uuprintf("Could not locate device node for '%s'", device_id);
					continue;
				}
				if (CM_Get_Child(&device_inst, parent_inst, 0) != CR_SUCCESS) {
					uuprintf("Could not get children of '%s'", device_id);
					continue;
				}
				if (device_inst != dev_info_data.DevInst) {
					// Try the siblings
					while (CM_Get_Sibling(&device_inst, device_inst, 0) == CR_SUCCESS) {
						if (device_inst == dev_info_data.DevInst) {
							uuprintf("NOTE: Matched instance from sibling for '%s'", device_id);
							break;
						}
					}
					if (device_inst != dev_info_data.DevInst)
						continue;
				}
				post_backslash = FALSE;
				method_str = "";

				// If we're not dealing with the USBSTOR part of our list, then this is an UASP device
				props.is_UASP = ((((uintptr_t)device_id)+2) >= ((uintptr_t)devid_list)+list_start[uasp_start]);
				// Now get the properties of the device, and its Device ID, which we need to populate the properties
				ToUpper(device_id);
				j = htab_hash(device_id, &htab_devid);
				uuprintf("  Matched with ID[%03d]: %s", j, device_id);

				// Try to parse the current device_id string for VID:PID
				// We'll use that if we can't get anything better
				for (k = 0, l = 0; (k<strlen(device_id)) && (l<2); k++) {
					// The ID is in the form USB_VENDOR_BUSID\VID_xxxx&PID_xxxx\...
					if (device_id[k] == '\\')
						post_backslash = TRUE;
					if (!post_backslash)
						continue;
					if (device_id[k] == '_') {
						props.pid = (uint16_t)strtoul(&device_id[k + 1], NULL, 16);
						if (l++ == 0)
							props.vid = props.pid;
					}
				}
				if (props.vid != 0)
					method_str = "[ID]";

				// If the hash didn't match a populated string in dev_if_path[] (htab_devid.table[j].data > 0),
				// we might have an extra vendor driver in between (e.g. "ASUS USB 3.0 Boost Storage Driver"
				// for UASP devices in ASUS "Turbo Mode" or "Apple Mobile Device USB Driver" for iPods)
				// so try to see if we can match the grandparent.
				if ( ((uintptr_t)htab_devid.table[j].data == 0)
					&& (CM_Get_Parent(&grandparent_inst, parent_inst, 0) == CR_SUCCESS)
					&& (CM_Get_Device_IDA(grandparent_inst, str, MAX_PATH, 0) == CR_SUCCESS) ) {
					device_id = str;
					method_str = "[GP]";
					ToUpper(device_id);
					j = htab_hash(device_id, &htab_devid);
					uuprintf("  Matched with (GP) ID[%03d]: %s", j, device_id);
				}
				if ((uintptr_t)htab_devid.table[j].data > 0) {
					uuprintf("  Matched with Hub[%d]: '%s'", (uintptr_t)htab_devid.table[j].data,
							dev_if_path.String[(uintptr_t)htab_devid.table[j].data]);
					if (GetUSBProperties(dev_if_path.String[(uintptr_t)htab_devid.table[j].data], device_id, &props)) {
						method_str = "";
						hub_path = dev_if_path.String[(uintptr_t)htab_devid.table[j].data];
					}
#ifdef FORCED_DEVICE
					props.vid = FORCED_VID;
					props.pid = FORCED_PID;
					static_strcpy(buffer, FORCED_NAME);
#endif
				}
				break;
			}
		}
		if (props.is_VHD) {
			uprintf("Found VHD device '%s'", buffer);
		} else if ((props.is_CARD) && ((!props.is_USB) || ((props.vid == 0) && (props.pid == 0)))) {
			uprintf("Found card reader device '%s'", buffer);
		} else if ((!props.is_USB) && (!props.is_UASP) && (props.is_Removable)) {
			if (!list_non_usb_removable_drives) {
				uprintf("Found non-USB removable device '%s' => Eliminated", buffer);
				uuprintf("If you *REALLY* need, you can enable listing of this device with <Ctrl><Alt><F>");
				continue;
			}
			uprintf("Found non-USB removable device '%s'", buffer);
		} else {
			if ((props.vid == 0) && (props.pid == 0)) {
				if (!props.is_USB) {
					// If we have a non removable SCSI drive and couldn't get a VID:PID,
					// we are most likely dealing with a system drive => eliminate it!
					uuprintf("Found non-USB non-removable device '%s' => Eliminated", buffer);
					continue;
				}
				static_strcpy(str, "????:????");	// Couldn't figure VID:PID
			} else {
				static_sprintf(str, "%04X:%04X", props.vid, props.pid);
			}
			if (props.speed >= USB_SPEED_MAX)
				props.speed = 0;
			uprintf("Found %s%s%s device '%s' (%s) %s\n", props.is_UASP?"UAS (":"",
				usb_speed_name[props.speed], props.is_UASP?")":"", buffer, str, method_str);
			if (props.is_LowerSpeed)
				uprintf("NOTE: This device is an USB 3.0 device operating at lower speed...");
		}
		devint_data.cbSize = sizeof(devint_data);
		hDrive = INVALID_HANDLE_VALUE;
		devint_detail_data = NULL;
		for (j=0; ;j++) {
			safe_closehandle(hDrive);
			safe_free(devint_detail_data);

			if (!SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, &_GUID_DEVINTERFACE_DISK, j, &devint_data)) {
				if(GetLastError() != ERROR_NO_MORE_ITEMS) {
					uprintf("SetupDiEnumDeviceInterfaces failed: %s\n", WindowsErrorString());
				} else {
					uprintf("A device was eliminated because it didn't report itself as a disk\n");
				}
				break;
			}

			if (!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, NULL, 0, &size, NULL)) {
				if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
					devint_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)calloc(1, size);
					if (devint_detail_data == NULL) {
						uprintf("Unable to allocate data for SP_DEVICE_INTERFACE_DETAIL_DATA\n");
						continue;
					}
					devint_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
				} else {
					uprintf("SetupDiGetDeviceInterfaceDetail (dummy) failed: %s\n", WindowsErrorString());
					continue;
				}
			}
			if (devint_detail_data == NULL) {
				uprintf("SetupDiGetDeviceInterfaceDetail (dummy) - no data was allocated\n");
				continue;
			}
			if(!SetupDiGetDeviceInterfaceDetailA(dev_info, &devint_data, devint_detail_data, size, &size, NULL)) {
				uprintf("SetupDiGetDeviceInterfaceDetail (actual) failed: %s\n", WindowsErrorString());
				continue;
			}

			hDrive = CreateFileA(devint_detail_data->DevicePath, GENERIC_READ|GENERIC_WRITE,
				FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if(hDrive == INVALID_HANDLE_VALUE) {
				uprintf("Could not open '%s': %s\n", devint_detail_data->DevicePath, WindowsErrorString());
				continue;
			}

			drive_number = GetDriveNumber(hDrive, devint_detail_data->DevicePath);
			if (drive_number < 0)
				continue;

			drive_index = drive_number + DRIVE_INDEX_MIN;
			if (!IsMediaPresent(drive_index)) {
				uprintf("Device eliminated because it appears to contain no media\n");
				safe_closehandle(hDrive);
				safe_free(devint_detail_data);
				break;
			}
			if (GetDriveSize(drive_index) < (MIN_DRIVE_SIZE*MB)) {
				uprintf("Device eliminated because it is smaller than %d MB\n", MIN_DRIVE_SIZE);
				safe_closehandle(hDrive);
				safe_free(devint_detail_data);
				break;
			}

			if (GetDriveLabel(drive_index, drive_letters, &label)) {
				if ((props.is_SCSI) && (!props.is_UASP) && (!props.is_VHD)) {
					if (!props.is_Removable) {
						// Non removables should have been eliminated above, but since we
						// are potentially dealing with system drives, better safe than sorry
						safe_closehandle(hDrive);
						safe_free(devint_detail_data);
						break;
					}
					if (!list_non_usb_removable_drives) {
						// Go over the mounted partitions and find if GetDriveType() says they are
						// removable. If they are not removable, don't allow the drive to be listed
						for (p = drive_letters; *p; p++) {
							drive_name[0] = *p;
							if (GetDriveTypeA(drive_name) != DRIVE_REMOVABLE)
								break;
						}
						if (*p) {
							uprintf("Device eliminated because it contains a mounted partition that is set as non-removable");
							safe_closehandle(hDrive);
							safe_free(devint_detail_data);
							break;
						}
					}
				}
				if ((!enable_HDDs) && (!props.is_VHD) && (!props.is_CARD) &&
					((score = IsHDD(drive_index, (uint16_t)props.vid, (uint16_t)props.pid, buffer)) > 0)) {
					uprintf("Device eliminated because it was detected as a Hard Drive (score %d > 0)", score);
					if (!list_non_usb_removable_drives)
						uprintf("If this device is not a Hard Drive, please e-mail the author of this application");
					uprintf("NOTE: You can enable the listing of Hard Drives under 'advanced drive properties'");
					safe_closehandle(hDrive);
					safe_free(devint_detail_data);
					break;
				}

				// The empty string is returned for drives that don't have any volumes assigned
				if (drive_letters[0] == 0) {
					entry = lmprintf(MSG_046, label, drive_number,
						SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units));
				} else {
					// Find the UEFI:TOGO partition(s) (and eliminate them form our listing)
					for (k=0; drive_letters[k]; k++) {
						uefi_togo_check[0] = drive_letters[k];
						if (PathFileExistsA(uefi_togo_check)) {
							for (l=k; drive_letters[l]; l++)
								drive_letters[l] = drive_letters[l+1];
							k--;
						}
					}
					// We have multiple volumes assigned to the same device (multiple partitions)
					// If that is the case, use "Multiple Volumes" instead of the label
					static_strcpy(entry_msg, (((drive_letters[0] != 0) && (drive_letters[1] != 0))?
						lmprintf(MSG_047):label));
					for (k=0, remove_drive=0; drive_letters[k] && (!remove_drive); k++) {
						// Append all the drive letters we detected
						letter_name[2] = drive_letters[k];
						if (right_to_left_mode)
							static_strcat(entry_msg, RIGHT_TO_LEFT_MARK);
						static_strcat(entry_msg, letter_name);
						if (drive_letters[k] == (PathGetDriveNumberU(app_dir) + 'A'))
							remove_drive = 1;
						if (drive_letters[k] == (PathGetDriveNumberU(system_dir) + 'A'))
							remove_drive = 2;
					}
					// Make sure that we don't list any drive that should not be listed
					if (remove_drive) {
						uprintf("Removing %C: from the list: This is the %s!", drive_letters[--k],
							(remove_drive==1)?"disk from which " APPLICATION_NAME " is running":"system disk");
						safe_closehandle(hDrive);
						safe_free(devint_detail_data);
						break;
					}
					safe_sprintf(&entry_msg[strlen(entry_msg)], sizeof(entry_msg) - strlen(entry_msg),
						"%s [%s]", (right_to_left_mode)?RIGHT_TO_LEFT_MARK:"", SizeToHumanReadable(GetDriveSize(drive_index), FALSE, use_fake_units));
					entry = entry_msg;
				}

				// Must ensure that the combo box is UNSORTED for indexes to be the same
				StrArrayAdd(&DriveID, buffer, TRUE);
				StrArrayAdd(&DriveLabel, label, TRUE);
				if ((hub_path != NULL) && (StrArrayAdd(&DriveHub, hub_path, TRUE) >= 0))
					DrivePort[DriveHub.Index - 1] = props.port;

				IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), drive_index));
				maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry));
				safe_closehandle(hDrive);
				safe_free(devint_detail_data);
				break;
			}
		}
	}
	SetupDiDestroyDeviceInfoList(dev_info);

	// Adjust the Dropdown width to the maximum text size
	SendMessage(hDeviceList, CB_SETDROPPEDWIDTH, (WPARAM)maxwidth, 0);

	if (devnum >= DRIVE_INDEX_MIN) {
		for (i=0; i<ComboBox_GetCount(hDeviceList); i++) {
			if ((DWORD)ComboBox_GetItemData(hDeviceList, i) == devnum) {
				found = TRUE;
				break;
			}
		}
	}
	if (!found)
		i = 0;
	IGNORE_RETVAL(ComboBox_SetCurSel(hDeviceList, i));
	SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE<<16) | IDC_DEVICE, 0);
	r = TRUE;

out:
	// Set 'Start' as the selected button, so that tab selection works
	SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDC_START), TRUE);
	safe_free(devid_list);
	StrArrayDestroy(&dev_if_path);
	htab_destroy(&htab_devid);
	return r;
}
/*
 * Finds information about USB HID devices using setup class API.
 *
 * The sequence of entries in array must match with what java layer expect. If a particular USB
 * attribute is not set in descriptor or can not be obtained "---" is placed in its place.
 *
 * The array returned will be in following sequence; transport, device node, vendor ID,
 * product ID, serial, product, manufacturer, USB bus number, USB device number, location.
 *
 * For an HID interface in a USB device, Windows create a device instance for the USB interface
 * (GUID_DEVINTERFACE_HID_DEVICE) and another device instance for the HID collection (GUID_DEVINTERFACE_HID).
 * This function relate HID collection device instance to its USB interface (physical USB device) through
 * HardwareID. A hardware ID is a vendor-defined identification string that Windows uses to match a
 * device to an INF file. In most cases, a device has associated with it a list of hardware IDs.
 * (However, there are exceptions − see Identifiers for 1394 Devices). When an enumerator reports a
 * list of hardware IDs for a device, the hardware IDs should be listed in order of decreasing suitability.
 *
 * This key contains symbolic links to HID device instances :
 * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses\{4d1e55b2-f16f-11cf-88cb-001111000030}
 * 
 * This function basically collect information from USB and HID subsystems and relate them to identify a device
 * and then create a unified information to be sent to java application.
 */
jobjectArray enumerate_usb_hid_devices(JNIEnv *env, jint vendor_to_match) {

	int q = 0;
	int i = 0;
	int x = 0;
	BOOL ret = FALSE;
	LONG status = 0;
	DWORD error_code = 0;
	DWORD errorVal = 0;
	DWORD size = 0;
	DWORD charbuffer_size = 0;
	DWORD driver_name_size = 0;
	ULONG buffer_size = 0;
	ULONG devprop_buffer_size = 0;
	DEVPROPTYPE proptype;
	DWORD regproptype;

	CONFIGRET cmret = 0;
	DEVINST firstchild = 0;
	DEVINST next_sibling = 0;
	DEVINST current_sibling = 0;

	struct hiddev_inst_cont_id *instidinfo;
	struct hiddev_instance_list hiddevinst_list = { 0 };
	struct jstrarray_list list = { 0 };

	DWORD hid_member_index = 0;
	HDEVINFO hid_dev_info_set;
	SP_DEVINFO_DATA hid_dev_instance;

	DWORD usb_member_index = 0;
	HDEVINFO usb_dev_info_set;
	SP_DEVINFO_DATA usb_dev_instance;

	/* size of these buffers is hardcoded in functions using them */
	TCHAR buffer[1024];
	TCHAR devprop_buffer[1024];
	TCHAR keybuf[1024];
	TCHAR charbuffer[512];
	TCHAR tmpbuf[128];
	char cmerror[256];

	jstring usb_dev_info;
	jclass strClass = NULL;
	jobjectArray usbHidDevicesFound = NULL;

	/* allocate memory to hold information used during processing and returning information
	   to caller. */
	x = init_hiddev_instance_list(&hiddevinst_list, 25);
	if (x < 0) {
		return clean_throw_exp_usbenumeration(env, 0, 1, 0, E_CALLOCSTR, NULL, NULL, NULL, NULL);
	}

	/* ~~~~~~~~~~~~~ ENUMERATE ALL HID DEVICES ~~~~~~~~~~~~~ */

	/* get information set for all HID devices matching the GUID. It an array of 
	   structures containing information about all attached and enumerated HID devices.	*/
	hid_dev_info_set = SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
	if (hid_dev_info_set == INVALID_HANDLE_VALUE) {
		return clean_throw_exp_usbenumeration(env, 1, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, NULL, &hiddevinst_list, NULL, &hid_dev_info_set);
	}

	/* enumerate all devices in this information set starting from 0th index */
	hid_member_index = 0;
	while (1) {
		ZeroMemory(&hid_dev_instance, sizeof(hid_dev_instance));
		hid_dev_instance.cbSize = sizeof(hid_dev_instance);

		/* from information set, get device by index */
		ret = SetupDiEnumDeviceInfo(hid_dev_info_set, hid_member_index, &hid_dev_instance);
		if (ret == FALSE) {
			error_code = GetLastError();
			if (error_code == ERROR_NO_MORE_ITEMS) {
				break;
			}else {
				return clean_throw_exp_usbenumeration(env, 1, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, NULL, &hiddevinst_list, NULL, &hid_dev_info_set);
			}
		}

		/* for this device find its instance ID, for example; HID\VID_04D8&PID_00DF&MI_02\7&33842C3F&0&0000
		 * this is variable 'Device Instance Path' in device manager. */
		memset(buffer, '\0', 1024);
		ret = SetupDiGetDeviceInstanceId(hid_dev_info_set, &hid_dev_instance, buffer, 1024, &size);
		if (ret == FALSE) {
			return clean_throw_exp_usbenumeration(env, 1, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, NULL, &hiddevinst_list, NULL, &hid_dev_info_set);
		}

		/* get HardwareID of this device;
		   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\HID\VID_04D8&PID_00DF&MI_02\7&33842C3F&0&0000\HardwareID */
		memset(keybuf, '\0', 1024);
		_stprintf_s(keybuf, 1024, TEXT("SYSTEM\\CurrentControlSet\\Enum\\%s"), buffer);

		charbuffer_size = sizeof(charbuffer);
		memset(charbuffer, '\0', 512);

		status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("HardwareID"), RRF_RT_REG_MULTI_SZ, NULL, (PVOID)charbuffer, &charbuffer_size);
		if (status != ERROR_SUCCESS) {
			return clean_throw_exp_usbenumeration(env, 1, 2, GetLastError(), NULL, NULL, &hiddevinst_list, NULL, &hid_dev_info_set);
		}

		/* save device instance and hardware id (including terminating null character) in the
		   list for later comparision */
		instidinfo = NULL;
		instidinfo = (struct hiddev_inst_cont_id *) malloc(sizeof(struct hiddev_inst_cont_id));
		if (instidinfo == NULL) {
			return clean_throw_exp_usbenumeration(env, 1, 1, 0, E_MALLOCSTR, NULL, &hiddevinst_list, NULL, &hid_dev_info_set);
		}
		_tcscpy_s(instidinfo->instance, 512, buffer);
		_tcscpy_s(instidinfo->hwid, 512, charbuffer);

		insert_hiddev_instance_list(&hiddevinst_list, instidinfo);

		/* increment to get the next HID device instance */
		hid_member_index++;
	}

	/* release HID info set as it is no longer needed */
	SetupDiDestroyDeviceInfoList(hid_dev_info_set);

	/* allocate memory to hold information used during processing and returning information
	to caller. */
	x = init_jstrarraylist(&list, 100);
	if (x < 0) {
		return clean_throw_exp_usbenumeration(env, 2, 1, 0, E_CALLOCSTR, NULL, &hiddevinst_list, NULL, NULL);
	}

	/* From here onwards, enumerate over all USB interfaces looking for HID interface and try to
	   associate with its device instance and then create information that will be passed to
	   application. */

	/* ~~~~~~~~~~~~~ ENUMERATE ALL USB DEVICES ~~~~~~~~~~~~~ */

	/* get information set for all usb devices matching the GUID */
	usb_dev_info_set = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
	if (usb_dev_info_set == INVALID_HANDLE_VALUE) {
		return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
	}

	/* enumerate all devices in this information set */
	usb_member_index = 0;
	while (1) {
		ZeroMemory(&usb_dev_instance, sizeof(usb_dev_instance));
		usb_dev_instance.cbSize = sizeof(usb_dev_instance);

		/* from information set, get device by index */
		ret = SetupDiEnumDeviceInfo(usb_dev_info_set, usb_member_index, &usb_dev_instance);
		if (ret == FALSE) {
			error_code = GetLastError();
			if (error_code == ERROR_NO_MORE_ITEMS) {
				break;
			}else {
				return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
			}
		}

		/* for this device find its instance ID (USB\VID_04D8&PID_00DF\000098037)
		 * this is the variable 'Device Instance Path' in device manager. */
		memset(buffer, '\0', sizeof(buffer));
		ret = SetupDiGetDeviceInstanceId(usb_dev_info_set, &usb_dev_instance, buffer, 1024, &size);
		if (ret == FALSE) {
			return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
		}

		/* fetch and examine USB interface */
		cmret = CM_Get_Child(&firstchild, usb_dev_instance.DevInst, 0);
		if (cmret != CR_SUCCESS) {
			if (cmret == CR_NO_SUCH_DEVNODE) {
				/* this device does not have any child, so check if this is a HID class device or not */
				memset(devprop_buffer, '\0', 1024);
				ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_CLASSGUID, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
				if (ret == FALSE) {
					return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}

				/* check if this is a HID device interface, if it is not than loop back to examine next USB device */
				ret = _tcsicmp(devprop_buffer, TEXT("{745A17A0-74D3-11D0-B6FE-00A0C90F57DA}"));
				if (ret != 0) {
					usb_member_index++;
					continue;
				}

				/* reaching here means that the device is a HID device, so create all the information
				that will be passed to java layer. */

				/* get the HardwareID of this USB HID interface. HardwareID is multi-sz, however
				   we use only 1st string from multi-string HardwareID for our matching.
				   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_04D8&PID_00DF&MI_02\7&33842C3F&0&0000\HardwareID 
				   The buffer contains device instance path of USB device */
				memset(keybuf, '\0', 1024);
				_stprintf_s(keybuf, 1024, TEXT("SYSTEM\\CurrentControlSet\\Enum\\%s"), buffer);

				charbuffer_size = sizeof(charbuffer);
				memset(charbuffer, '\0', 512);

				/* USB\VID_04D8&PID_00DF&REV_0101 and USB\VID_04D8&PID_00DF and so on */
				status = RegGetValue(HKEY_LOCAL_MACHINE, keybuf, TEXT("HardwareID"), RRF_RT_REG_MULTI_SZ, NULL, (PVOID)charbuffer, &charbuffer_size);
				if (status != ERROR_SUCCESS) {
					return clean_throw_exp_usbenumeration(env, 3, 2, GetLastError(), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}

				/* charbuffer now contains hardwareID, cook it a little bit to enable suitable matching */
				if ((charbuffer[0] == 'U') && (charbuffer[1] == 'S') && (charbuffer[2] == 'B')) {
					charbuffer[0] = 'H';
					charbuffer[1] = 'I';
					charbuffer[2] = 'D';
				}

				for (q = 0; q < hiddevinst_list.index; q++) {

					/* check association between HID collection device instance and USB device instance */
					ret = _tcsicmp(charbuffer, hiddevinst_list.base[q]->hwid);
					if (ret != 0) {
						continue;
					}

					/* reaching here means this HID collection belongs to this USB HID device, so glean information
					   to be passed to java layer. */

					/* TRANSPORT */
					usb_dev_info = (*env)->NewStringUTF(env, "USB");
					if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
						return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					insert_jstrarraylist(&list, usb_dev_info);

					/* DEVICE NODE */
					usb_dev_info = (*env)->NewString(env, hiddevinst_list.base[q]->instance, (jsize)_tcslen(hiddevinst_list.base[q]->instance));
					if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
						return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					insert_jstrarraylist(&list, usb_dev_info);

					/* USB-IF VENDOR ID
					   (extracted from USB device instance for example: USB\VID_04D8&PID_00DF\000098037) */
					x = 0;
					while (buffer[x] != '\0') {
						if ((buffer[x] == 'V') && (buffer[x + 1] == 'I') && (buffer[x + 2] == 'D') && (buffer[x + 3] == '_')) {
							break;
						}
						x++;
					}
					x = x + 4;
					i = 0;
					while (buffer[x] != '&') {
						tmpbuf[i] = buffer[x];
						i++;
						x++;
					}
					tmpbuf[i] = '\0'; /* indicate end of string */
					usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
					if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
						return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					insert_jstrarraylist(&list, usb_dev_info);

					/* USB product ID
					   (extracted from USB device instance for example: USB\VID_04D8&PID_00DF\000098037) */
					x = 6;
					while (buffer[x] != '\0') {
						if ((buffer[x] == 'P') && (buffer[x + 1] == 'I') && (buffer[x + 2] == 'D') && (buffer[x + 3] == '_')) {
							break;
						}
						x++;
					}
					x = x + 4;
					i = 0;
					while (buffer[x] != '\\') {
						tmpbuf[i] = buffer[x];
						i++;
						x++;
					}
					tmpbuf[i] = '\0'; /* indicate end of string */
					usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
					if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
						return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					insert_jstrarraylist(&list, usb_dev_info);

					/* SERIAL NUMBER */
					x++;
					i = 0;
					while (buffer[x] != '\0') {
						tmpbuf[i] = buffer[x];
						i++;
						x++;
					}
					tmpbuf[i] = '\0';
					usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
					if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
						return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					insert_jstrarraylist(&list, usb_dev_info);

					/* PRODUCT
					   (iProduct field of USB device descriptor) */
					memset(devprop_buffer, '\0', sizeof(devprop_buffer));
					ret = SetupDiGetDeviceProperty(usb_dev_info_set, &usb_dev_instance, &DEVPKEY_Device_BusReportedDeviceDesc, &proptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size, 0);
					if (ret == FALSE) {
						/* fallback to SPDRP_DEVICEDESC if DEVPKEY_Device_BusReportedDeviceDesc fails */
						ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_DEVICEDESC, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
						if (ret == FALSE) {
							/* if second attempt fails, throw error, we need to investigate drivers/firmware etc */
							return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
						}
					}
					usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
					insert_jstrarraylist(&list, usb_dev_info);

					/* MANUFACTURER */
					memset(devprop_buffer, '\0', sizeof(devprop_buffer));
					ret = SetupDiGetDeviceProperty(usb_dev_info_set, &usb_dev_instance, &DEVPKEY_Device_Manufacturer, &proptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size, 0);
					if (ret == FALSE) {
						return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
					insert_jstrarraylist(&list, usb_dev_info);

					/* LOCATION
					   (Location paths + Location info, get separately and then create a single string) */

					// PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(3)
					memset(devprop_buffer, '\0', sizeof(devprop_buffer));
					ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_LOCATION_PATHS, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
					if (ret == FALSE) {
						return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					// Port_#0003.Hub_#0001
					memset(charbuffer, '\0', sizeof(charbuffer));
					ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_LOCATION_INFORMATION, &regproptype, (BYTE *)charbuffer, sizeof(charbuffer), &size);
					if (ret == FALSE) {
						return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}

					i = 0;
					x = (int)_tcslen(devprop_buffer);
					devprop_buffer[x] = '-';
					x++;
					for (i = 0; i < (int)_tcslen(charbuffer); i++) {
						devprop_buffer[x] = charbuffer[i];
						x++;
					}
					devprop_buffer[x] = '\0';

					usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
					insert_jstrarraylist(&list, usb_dev_info);
				}

				/* loop back to get next USB device */
				usb_member_index++;
				continue;
			}else {
				/* error happend when getting child of USB device */
				_snprintf_s(cmerror, 256, 256, "CM_Get_Child failed with CR_xxxx error code : 0x%X\0", cmret);
				return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
			}
		}

		/* reaching here means that this USB device has at-least one child device node, examine first child now */
		devprop_buffer_size = sizeof(devprop_buffer);
		memset(devprop_buffer, '\0', 1024);

		cmret = CM_Get_DevNode_Registry_Property(firstchild, CM_DRP_CLASSGUID, &proptype, (PVOID)devprop_buffer, &devprop_buffer_size, 0);
		if (cmret != CR_SUCCESS) {
			_snprintf_s(cmerror, 256, 256, "CM_Get_DevNode_Registry_Property failed with CR_xxxx error code : 0x%X\0", cmret);
			return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
		}

		/* check if first child is a HID device interface */
		ret = _tcsicmp(devprop_buffer, TEXT("{745A17A0-74D3-11D0-B6FE-00A0C90F57DA}"));
		if (ret == 0) {
			/* reaching here means that this sibling (interface) is a HID type, get its device instance path */
			memset(devprop_buffer, '\0', 1024);
			cmret = CM_Get_Device_ID(firstchild, devprop_buffer, 1024, 0);
			if (cmret != CR_SUCCESS) {
				_snprintf_s(cmerror, 256, 256, "CM_Get_Device_ID failed with CR_xxxx error code : 0x%X\0", cmret);
				return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
			}

			/* get the HardwareID of this USB HID interface. HardwareID is multi-sz, however
			   we use only 1st string from multi-string HardwareID for our matching.
			   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_04D8&PID_00DF&MI_02\7&33842C3F&0&0000\HardwareID */
			memset(charbuffer, '\0', 512);
			buffer_size = sizeof(charbuffer);
			cmret = CM_Get_DevNode_Registry_Property(firstchild, CM_DRP_HARDWAREID, NULL, (PVOID)charbuffer, &buffer_size, 0);
			if (cmret != CR_SUCCESS) {
				_snprintf_s(cmerror, 256, 256, "CM_Get_Device_ID failed with CR_xxxx error code : 0x%X\0", cmret);
				return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
			}

			if ((charbuffer[0] == 'U') && (charbuffer[1] == 'S') && (charbuffer[2] == 'B')) {
				charbuffer[0] = 'H';
				charbuffer[1] = 'I';
				charbuffer[2] = 'D';
			}

			for (q = 0; q < hiddevinst_list.index; q++) {

				/* check association between HID collection device instance and USB interface device instance */
				ret = _tcsncicmp(charbuffer, hiddevinst_list.base[q]->hwid, 512);
				if (ret != 0) {
					continue;
				}

				/* reaching here means this HID collection belongs to this USB HID interface, so glean information
				   to be passed to java layer. */

				/* TRANSPORT */
				usb_dev_info = (*env)->NewStringUTF(env, "USB");
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* DEVICE NODE */
				usb_dev_info = (*env)->NewString(env, hiddevinst_list.base[q]->instance, (jsize)_tcslen(hiddevinst_list.base[q]->instance));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* USB-IF VENDOR ID
				   (extracted from USB device instance for example: USB\VID_04D8&PID_00DF\000098037) */
				x = 0;
				while (buffer[x] != '\0') {
					if ((buffer[x] == 'V') && (buffer[x + 1] == 'I') && (buffer[x + 2] == 'D') && (buffer[x + 3] == '_')) {
						break;
					}
					x++;
				}
				x = x + 4;
				i = 0;
				while (buffer[x] != '&') {
					tmpbuf[i] = buffer[x];
					i++;
					x++;
				}
				tmpbuf[i] = '\0'; /* indicate end of string */
				usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* USB product ID
				   (extracted from USB device instance for example: USB\VID_04D8&PID_00DF\000098037) */
				x = 6;
				while (buffer[x] != '\0') {
					if ((buffer[x] == 'P') && (buffer[x + 1] == 'I') && (buffer[x + 2] == 'D') && (buffer[x + 3] == '_')) {
						break;
					}
					x++;
				}
				x = x + 4;
				i = 0;
				while (buffer[x] != '\\') {
					tmpbuf[i] = buffer[x];
					i++;
					x++;
				}
				tmpbuf[i] = '\0'; /* indicate end of string */
				usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* SERIAL NUMBER */
				x++;
				i = 0;
				while (buffer[x] != '\0') {
					tmpbuf[i] = buffer[x];
					i++;
					x++;
				}
				tmpbuf[i] = '\0';
				usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* PRODUCT
				   (iProduct field of USB device descriptor) */
				memset(devprop_buffer, '\0', sizeof(devprop_buffer));
				ret = SetupDiGetDeviceProperty(usb_dev_info_set, &usb_dev_instance, &DEVPKEY_Device_BusReportedDeviceDesc, &proptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size, 0);
				if (ret == FALSE) {
					/* fallback to SPDRP_DEVICEDESC if DEVPKEY_Device_BusReportedDeviceDesc fails */
					ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_DEVICEDESC, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
					if (ret == FALSE) {
						/* if second attempt fails, throw error, we need to investigate drivers/firmware etc */
						return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
				}
				usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
				insert_jstrarraylist(&list, usb_dev_info);

				/* MANUFACTURER */
				memset(devprop_buffer, '\0', sizeof(devprop_buffer));
				ret = SetupDiGetDeviceProperty(usb_dev_info_set, &usb_dev_instance, &DEVPKEY_Device_Manufacturer, &proptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size, 0);
				if (ret == FALSE) {
					return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
				insert_jstrarraylist(&list, usb_dev_info);

				/* LOCATION
				  (Location paths + Location info, get separately and then create a single string) */

				// PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(3)
				memset(devprop_buffer, '\0', sizeof(devprop_buffer));
				ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_LOCATION_PATHS, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
				if (ret == FALSE) {
					return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				// Port_#0003.Hub_#0001
				memset(charbuffer, '\0', sizeof(charbuffer));
				ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_LOCATION_INFORMATION, &regproptype, (BYTE *)charbuffer, sizeof(charbuffer), &size);
				if (ret == FALSE) {
					return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}

				i = 0;
				x = (int)_tcslen(devprop_buffer);
				devprop_buffer[x] = '-';
				x++;
				for (i = 0; i < (int)_tcslen(charbuffer); i++) {
					devprop_buffer[x] = charbuffer[i];
					x++;
				}
				devprop_buffer[x] = '\0';

				usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
				insert_jstrarraylist(&list, usb_dev_info);
			}
		}

		/* check if this usb device has more than one interface. if it has enumerate over each
		   one by one and collecting information for every HID interface found and sending it to 
		   java layer. */
		current_sibling = firstchild;
		while (1) {
			cmret = CM_Get_Sibling(&next_sibling, current_sibling, 0);
			if (cmret != CR_SUCCESS) {
				if (cmret == CR_NO_SUCH_DEVNODE) {
					/* done iterating over all interfaces, move to next examine next USB device */
					break;
				}else {
					_snprintf_s(cmerror, 256, 256, "CM_Get_Sibling failed with CR_xxxx error code : 0x%X\0", cmret);
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
			}

			/* reaching here means USB device has more than 1 interfaces, get class of this interface (sibling) */
			devprop_buffer_size = sizeof(devprop_buffer);
			memset(devprop_buffer, '\0', sizeof(devprop_buffer));
			cmret = CM_Get_DevNode_Registry_Property(next_sibling, CM_DRP_CLASSGUID, &proptype, (VOID *)devprop_buffer, &devprop_buffer_size, 0);
			if (cmret != CR_SUCCESS) {
				_snprintf_s(cmerror, 256, 256, "CM_Get_DevNode_Registry_Property failed with CR_xxxx error code : 0x%X\0", cmret);
				return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
			}

			/* check if this is a HID device interface */
			ret = _tcsicmp(devprop_buffer, TEXT("{745A17A0-74D3-11D0-B6FE-00A0C90F57DA}"));
			if (ret != 0) {
				/* this is not HID interface, move to check next interface */
				current_sibling = next_sibling;
				continue;
			}

			/* reaching here means that this sibling (interface) is a HID type, get its device instance path */
			memset(devprop_buffer, '\0', 1024);
			cmret = CM_Get_Device_ID(next_sibling, devprop_buffer, 1024, 0);
			if (cmret != CR_SUCCESS) {
				_snprintf_s(cmerror, 256, 256, "CM_Get_Device_ID failed with CR_xxxx error code : 0x%X\0", cmret);
				return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
			}

			/* get the HardwareID of this USB HID interface. HardwareID is multi-sz, however
			   we use only 1st string from multi-string HardwareID for our matching.
			   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_04D8&PID_00DF&MI_02\7&33842C3F&0&0000\HardwareID */
			memset(charbuffer, '\0', 512);
			buffer_size = sizeof(charbuffer);

			cmret = CM_Get_DevNode_Registry_Property(next_sibling, CM_DRP_HARDWAREID, NULL, (PVOID)charbuffer, &buffer_size, 0);
			if (cmret != CR_SUCCESS) {
				_snprintf_s(cmerror, 256, 256, "CM_Get_Device_ID failed with CR_xxxx error code : 0x%X\0", cmret);
				return clean_throw_exp_usbenumeration(env, 3, 1, 0, cmerror, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
			}

			if ((charbuffer[0] == 'U') && (charbuffer[1] == 'S') && (charbuffer[2] == 'B')) {
				charbuffer[0] = 'H';
				charbuffer[1] = 'I';
				charbuffer[2] = 'D';
			}

			for (q = 0; q < hiddevinst_list.index; q++) {

				/* check association between HID collection device instance and USB interface device instance */
				ret = _tcsncicmp(charbuffer, hiddevinst_list.base[q]->hwid, 512);
				if (ret != 0) {
					continue;
				}

				/* reaching here means this HID collection belongs to this USB HID interface, so glean information
				   to be passed to java layer. */

				/* TRANSPORT */
				usb_dev_info = (*env)->NewStringUTF(env, "USB");
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* DEVICE NODE */
				usb_dev_info = (*env)->NewString(env, hiddevinst_list.base[q]->instance, (jsize)_tcslen(hiddevinst_list.base[q]->instance));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* USB-IF VENDOR ID
				   (extracted from USB device instance for example: USB\VID_04D8&PID_00DF\000098037) */
				x = 0;
				while (buffer[x] != '\0') {
					if ((buffer[x] == 'V') && (buffer[x + 1] == 'I') && (buffer[x + 2] == 'D') && (buffer[x + 3] == '_')) {
						break;
					}
					x++;
				}
				x = x + 4;
				i = 0;
				while (buffer[x] != '&') {
					tmpbuf[i] = buffer[x];
					i++;
					x++;
				}
				tmpbuf[i] = '\0'; /* indicate end of string */
				usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* USB product ID
				   (extracted from USB device instance for example: USB\VID_04D8&PID_00DF\000098037) */
				x = 6;
				while (buffer[x] != '\0') {
					if ((buffer[x] == 'P') && (buffer[x + 1] == 'I') && (buffer[x + 2] == 'D') && (buffer[x + 3] == '_')) {
						break;
					}
					x++;
				}
				x = x + 4;
				i = 0;
				while (buffer[x] != '\\') {
					tmpbuf[i] = buffer[x];
					i++;
					x++;
				}
				tmpbuf[i] = '\0'; /* indicate end of string */
				usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* SERIAL NUMBER */
				x++;
				i = 0;
				while (buffer[x] != '\0') {
					tmpbuf[i] = buffer[x];
					i++;
					x++;
				}
				tmpbuf[i] = '\0';
				usb_dev_info = (*env)->NewString(env, tmpbuf, (jsize)_tcslen(tmpbuf));
				if ((usb_dev_info == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
					return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWSTRUTFSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				insert_jstrarraylist(&list, usb_dev_info);

				/* PRODUCT
				   (iProduct field of USB device descriptor) */
				memset(devprop_buffer, '\0', sizeof(devprop_buffer));
				ret = SetupDiGetDeviceProperty(usb_dev_info_set, &usb_dev_instance, &DEVPKEY_Device_BusReportedDeviceDesc, &proptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size, 0);
				if (ret == FALSE) {
					/* fallback to SPDRP_DEVICEDESC if DEVPKEY_Device_BusReportedDeviceDesc fails */
					ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_DEVICEDESC, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
					if (ret == FALSE) {
						/* if second attempt fails, throw error, we need to investigate drivers/firmware etc */
						return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
					}
					usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
					insert_jstrarraylist(&list, usb_dev_info);
				}else {
					usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
					insert_jstrarraylist(&list, usb_dev_info);
				}

				/* MANUFACTURER */
				memset(devprop_buffer, '\0', sizeof(devprop_buffer));
				ret = SetupDiGetDeviceProperty(usb_dev_info_set, &usb_dev_instance, &DEVPKEY_Device_Manufacturer, &proptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size, 0);
				if (ret == FALSE) {
					return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
				insert_jstrarraylist(&list, usb_dev_info);

				/* LOCATION
				   (Location paths + Location info, get separately and then create a single string) */

				// PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(3)
				memset(devprop_buffer, '\0', sizeof(devprop_buffer));
				ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_LOCATION_PATHS, &regproptype, (BYTE *)devprop_buffer, sizeof(devprop_buffer), &size);
				if (ret == FALSE) {
					return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}
				// Port_#0003.Hub_#0001
				memset(charbuffer, '\0', sizeof(charbuffer));
				ret = SetupDiGetDeviceRegistryProperty(usb_dev_info_set, &usb_dev_instance, SPDRP_LOCATION_INFORMATION, &regproptype, (BYTE *)charbuffer, sizeof(charbuffer), &size);
				if (ret == FALSE) {
					return clean_throw_exp_usbenumeration(env, 3, 2, HRESULT_FROM_SETUPAPI(GetLastError()), NULL, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
				}

				i = 0;
				x = (int)_tcslen(devprop_buffer);
				devprop_buffer[x] = '-';
				x++;
				for (i = 0; i < (int)_tcslen(charbuffer); i++) {
					devprop_buffer[x] = charbuffer[i];
					x++;
				}
				devprop_buffer[x] = '\0';

				usb_dev_info = (*env)->NewString(env, devprop_buffer, (jsize)_tcslen(devprop_buffer));
				insert_jstrarraylist(&list, usb_dev_info);
			}

			/* set this sibling as base sibling for fetching next sibling, loop over to get and check next
			   interface (sibling) */
			current_sibling = next_sibling;
		}

		/* increment to get and examine the next usb device for HID class */
		usb_member_index++;
	}

	strClass = (*env)->FindClass(env, JAVALSTRING);
	if ((strClass == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
		return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_FINDCLASSSSTRINGSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
	}

	usbHidDevicesFound = (*env)->NewObjectArray(env, (jsize)list.index, strClass, NULL);
	if ((usbHidDevicesFound == NULL) || ((*env)->ExceptionOccurred(env) != NULL)) {
		return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_NEWOBJECTARRAYSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
	}

	for (x = 0; x < list.index; x++) {
		(*env)->SetObjectArrayElement(env, usbHidDevicesFound, x, list.base[x]);
		if ((*env)->ExceptionOccurred(env)) {
			return clean_throw_exp_usbenumeration(env, 3, 1, 0, E_SETOBJECTARRAYSTR, &list, &hiddevinst_list, &usb_dev_info_set, NULL);
		}
	}

	/* clean up and return result to java layer application */
	free_jstrarraylist(&list);
	free_hiddev_instance_list(&hiddevinst_list);
	SetupDiDestroyDeviceInfoList(usb_dev_info_set);
	return usbHidDevicesFound;
}