Delcom Products
Welcome to Delcom Products.
 
Home->Web Notes

WebNoteId: 20
Title: The ScanForHidDevice() function in USBHIDIO.c returns the incorrect device.


Summary: This web note describes how to correct the ScanForHidDevice() function from returning the incorrect device.

DATE: Feb 10, 2020

ISSUE: OS: Windows10
Devices: All Delcom USB HID device with G2 firmware and above.
Software: All software applications that use the ScanForHidDevice() function from the USBHIDIO.c file to scan and open the device. This includes Delcom Applications, DLL, example software and any customer applications that used the USBHIDIO.c/h files in their code.

PROBLEM: The Delcom USB HID device is a composite device. Which means it appears to the OS as many types of devices, such as: Vendor Defined, Keyboard, Mouse, Game, System and Consumer devices. These are defined by the HID Usage IDs and Pages. The Vendor Defined usage id and page is the type that is required to send and receive Delcom IO commands. Historically the ScanForHidDevice() function has always returned the Vendor Defined type first, as it is the first HID Usage ID and Page reported and defined by the device. Recently this has changed and Windows may return the device type in a random order. If a non Vendor Defined type is returned first, the ScanForHidDevice() function will return the devicename of one of the other type. This other type will not be able to accept Delcom IO commands and will fail.

SOLUTION: There are two solutions, a quick fix that should resolve the issue most of the time. And an updated solution that will resolve the issue permanently. Updating the ScanForHidDevice() function is recommended.

Quick Solution: Plugging the device in to a different USB port usually fixes the issue. Reinstalling the device will also usually fix the issue. To reinstall the device see Webnote14

Updated Solution: The updated solution changes the ScanForHidDevice() function, by checking that the device’s Usage ID and Page are of the Vendor Defined type. This check assures the correct device is found. The updated ScanForHidDevice() function is listed below and can be cut and pasted in to the your project. Here is the link to the USBHIDIO files . All Delcom applications, DLL and sample software that have a version date of 2020 or greater have been updated and our available on our website. For a command line example see the USBCMDAP program.



// ------------------------------------------------------ //
// ScanForHidDevice(VID,PID,TID,SID,ENUM,SHARE) - Scan thru all the HID device looking
// for a match on the VID, PID and optional TID (Type ID) and SID (Serial Number).
// Sets the hDevice handle varible if found and opens the device
// Parameters:
//		VID	The Vendor Indenification Number. Delcom VID = 0x0FC5;
//		PID	The Product Indenification Number. Typically 0xB080 (see PID_ALT)
//		TID	The Family type number. Zero=all, else only serachs for matching TID
//		SID	The Serial number. Zero=all, else only serachs for matching SID
//		ENUM 	Eumeration. When non-zero list all matching device found. Does not leave open any device.
//		SHARE	Share Mode. When non-zero open device in shared mode.
//		PID_ALT An alternate PID to search for. Tyipcally 0xA080.
//
// Return zero if found, else non-zero error code.
//		0 = Success - A matching device has been found and openned.
//		1 = No matching HID devices.
// NOTES
// Enum if set - prints all the devices found
// 02/06/2020 - Added check for Usage ID and Page, to make should we are connecting to the Delcom Vendor Defined Device and
// not one of the other HID types. Also now check of all api return failures. Added PID_ALT to serach for alt too, set to zero if not used.
// ------------------------------------------------------ //
unsigned int CUSBHIDIO::ScanForHIDDevice(unsigned int VID, unsigned int PID, unsigned int TID, unsigned int SID, unsigned int Enum, unsigned int ShareMode,unsigned int PID_ALT )
{
	//Use a series of API calls to find a HID with a matching Vendor,Product, Type and Serial ID.
	DelcomDeviceInfoStruct		DelcomInfo;
	PSP_DEVICE_INTERFACE_DETAIL_DATA	pDetailData;
	GUID						HidGuid;
	HANDLE						hDevInfo;
	ULONG						Required;
	HIDD_ATTRIBUTES				Attributes;
	SP_DEVICE_INTERFACE_DATA	devInfoData;
	bool						LastDevice = FALSE;
	int							MemberIndex = 0;
	bool						MyDeviceDetected = FALSE; 
	LONG						Result;
	ULONG						Length;
	ULONG						devcnt;
	DWORD						SHARE_MODE;
	PHIDP_PREPARSED_DATA		pPreparsedData;
	PHIDP_CAPS					pCaps;

	// Variable init	
	devcnt=0;
	Length = 0;
	pDetailData = NULL;
	hDevice=NULL;
	MemberIndex = 0;
	LastDevice = FALSE;

	if(ShareMode)	SHARE_MODE = FILE_SHARE_READ|FILE_SHARE_WRITE;
	else			SHARE_MODE = 0;


	pCaps =(PHIDP_CAPS)malloc(sizeof(HIDP_CAPS));					
	
	// API function: HidD_GetHidGuid - Get the GUID for all system HIDs. Returns: the GUID in HidGuid.
	HidD_GetHidGuid(&HidGuid);	
	
	// API function: SetupDiGetClassDevs -Returns: a handle to a device information set for all installed devices.
	// Requires: the GUID returned by GetHidGuid.
	hDevInfo=SetupDiGetClassDevs(&HidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);	
		
	devInfoData.cbSize = sizeof(devInfoData);

	//Step through the available devices looking for the one we want. 
	//Quit on detecting the desired device or checking all available devices without success.
	
	do	{
		MyDeviceDetected=FALSE;
		
		//API function: SetupDiEnumDeviceInterfaces - On return, MyDeviceInterfaceData contains the handle to a
		// SP_DEVICE_INTERFACE_DATA structure for a detected device. Requires:
		// The DeviceInfoSet returned in SetupDiGetClassDevs. The HidGuid returned in GetHidGuid. An index to specify a device.
		
		Result=SetupDiEnumDeviceInterfaces(hDevInfo, 0, &HidGuid, MemberIndex, &devInfoData);
		if (Result != 0)
			{	//A device has been detected, so get more information about it.

			// API function: SetupDiGetDeviceInterfaceDetail - Returns: an SP_DEVICE_INTERFACE_DETAIL_DATA structure
			// containing information about a device. To retrieve the information, call this function twice.
			// The first time returns the size of the structure in Length. The second time returns a pointer to the data in DeviceInfoSet.
			// Requires: A DeviceInfoSet returned by SetupDiGetClassDevs The SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces.
			// The final parameter is an optional pointer to an SP_DEV_INFO_DATA structure. This application does not retrieve or use the structure.			
			// If retrieving the structure, set MyDeviceInfoData.cbSize = length of MyDeviceInfoData.
			// and pass the structures address.
	

			
			

			//Get the Length value.
			//The call will return with a "buffer too small" error which can be ignored.
			Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, NULL, 0, &Length, NULL);

			//Allocate memory for the hDevInfo structure, using the returned Length.
			pDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length);
			

			//Set cbSize in the detailData structure.
			pDetailData -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
			

			//Call the function again, this time passing it the returned buffer size.
			Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, pDetailData, Length, &Required, NULL);

			//Open a handle to the device.
			// API function: CreateFile - Returns: a handle that enables reading and writing to the device.
			// Requires: The DevicePath in the detailData structure	returned by SetupDiGetDeviceInterfaceDetail.
			// NOTES: details->DevicePath - holds the name of the device - 
			// NOTES: For file sharing set 3rd parameter to FILE_SHARE_READ|FILE_SHARE_WRITE
			hDevice = INVALID_HANDLE_VALUE;
			hDevice = CreateFile(pDetailData->DevicePath, GENERIC_READ|GENERIC_WRITE, SHARE_MODE,
								(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL);

			if( hDevice != INVALID_HANDLE_VALUE ) 
				{

				// API function: HidD_GetAttributes - Requests information from the device.
				// Requires: the handle returned by CreateFile.	Returns: a HIDD_ATTRIBUTES structure containing
				// the Vendor ID, Product ID, and Product Version Number. Use this information to decide if the detected device is
				// the one we are looking for.
				// To enable overlappedtransfer use: GetDeviceCapabilities() & PrepareForOverlappedTransfer()
			
				// Get the Attributes(VID,PID)
				Attributes.Size = sizeof(Attributes);
				Result = HidD_GetAttributes(hDevice, &Attributes);

				// Get Capabillities(USAGE ID & PAGE)pCaps->Usage = 0x0;				
				pCaps->UsagePage = 0x0;
				if(HidD_GetPreparsedData(hDevice, &pPreparsedData) )
					Result = HidP_GetCaps(pPreparsedData, pCaps);
					

				//Is it the desired device?
				if( Result && (Attributes.VendorID == VID) &&
					((Attributes.ProductID == PID) ||(Attributes.ProductID == PID_ALT))  && 
					(pCaps->UsagePage==0xFF00)  && (pCaps->Usage==0x00) )
					{	//Both the Product and Vendor IDs and Usage ID and Page match. Delcom vendor defined device found!
					MyDeviceDetected = TRUE;														// premark it found						
					_tcsncpy_s(DeviceName, sizeof(DeviceName), pDetailData->DevicePath, 512);		// save the devicename	
					
					// At this point we have found a match device. If  VID, PID or ENUM are set then check for them too.
					if(TID || SID || Enum) {	// Now check for TID SID and ENUM if non-zero
						Result = GetDeviceInfo(&DelcomInfo);		// TRY TO READ THE DEVICE
						if(Result ) MyDeviceDetected = FALSE;		// this function must succeed
						else {
							if(TID && (DelcomInfo.Family != TID)) MyDeviceDetected = FALSE;
							if(SID && (DelcomInfo.Serial != SID)) MyDeviceDetected = FALSE;
							if(Enum) {     // if Enumerate mode enabled, just print the info and make MyDeviceDetected=FALSE so it scans all of them.
								//_tprintf(L"Device Found(%u): TID=%u SID=%u VER=%d Usage(%x/%x).
%s
", ++devcnt, DelcomInfo.Family,DelcomInfo.Serial, DelcomInfo.Version, pCaps->UsagePage, pCaps->Usage, DeviceName);
								_tprintf(L"Device Found(%u): TID=%u SID=%u VER=%d.
%s
", ++devcnt, DelcomInfo.Family,DelcomInfo.Serial, DelcomInfo.Version, DeviceName);
								MyDeviceDetected = FALSE;		// Mark false, so keeps scanning
								}
							}	// end of TID or SID or 
						}
					
					} 

				
				if(MyDeviceDetected == FALSE) CloseHandle(hDevice);		// close the handle if device not found or enum mode
				} // eof if hDevice is valid
			

			//Free the memory used by the detailData structure (no longer needed).
			free(pDetailData);
		
		}  //if (Result != 0)

		else	{				// End of List - No HID devices detected!
			LastDevice=TRUE;	//SetupDiEnumDeviceInterfaces returned 0, so there are no more devices to check.
			}

		//If we have not found the device yet, and have not tried every available device,
		//try the next one.
		MemberIndex++;

	} // loop till either end of deivce list or we find our device 
	while ((LastDevice == FALSE) && (MyDeviceDetected == FALSE));


	SetupDiDestroyDeviceInfoList(hDevInfo);		//Free the memory reserved for hDevInfo by SetupDiClassDevs.
	free(pCaps);

	if (MyDeviceDetected == FALSE) {
		// Device not found
		hDevice = 0;
		return(1);
		}
	else	{
		// Device Found
		HidD_SetNumInputBuffers(hDevice,1);					//sets the maximum number of input reports that the HID class driver ring buffer can hold for a specified top-level collection.
		return(0); // Success
		}

	

	

	
}

homeproductsservicescontact usshopping cartlegal   © Copyright 2020 Delcom Products Inc - R720 7/12/2020