WinUSB and SetupDiGetDeviceInterfaceDetail

Hi. This is a repeat of my earlier post (but with less code that is better formatted and more explanation).

I’m having difficulty getting to the point where I can open a handle to WinUSB
from an application. I have succesfully installed WinUSB under XP (x86). I
have code that will find the number of devices I have plugged in to the USB port
(devices that I am developing software for), but I can’t seem to get past this
point. I am following the code found on Microsoft’s website pretty closely:
http://msdn2.microsoft.com/en-us/library/aa476413.aspx

See below for my code. Let me know if you have any ideas. Specifically, I’m
trying to print out the device path. This is the critical element I need to Create a
file and retrieve a WinUSB handle…

Specifically, I think I’m having problems with SetupDiGetDeviceInterfaceDetail. Once I figure how many of my devices are on the USB port, I then enumerate through all of my devices, calling the above function. It fails the first time through (to get the size of the device path). Then, I call it again to actually get the device path. Is this correct?

–Brian Hindman

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
#include <setupapi.h>
#include “OSROnline.h”

//----------------------------------------------------------------------
// Main for finding DS2490s (USB devices)
//
int main(int argc, char **argv)
{
// Declare and initialize variables
GUID InterfaceGuid; // The GUID matches the one in the inf file
HDEVINFO hdevClassInfo; // Define a handle to a device information set.
SP_DEVICE_INTERFACE_DATA MyDeviceData; // Structure for interface data
SP_DEVINFO_DATA DevInfo;
PSP_INTERFACE_DEVICE_DETAIL_DATA pBuffer; // Structure for device details
DWORD nMemberIndex = 0; // Holds number of device interfaces
BOOL nStatus = FALSE; // Holds function call results
struct DeviceData // Declare DeviceList variable as
{
TCHAR HardwareId;
TCHAR Path;
TCHAR FriendlyName;
DWORD DeviceInstance;
} DeviceList;
DWORD i = 0;
DWORD nSize;
size_t nLen = 0;

InterfaceGuid = GUID_DEVINTERFACE_DS2490; // Taken from OSROnline.h
hdevClassInfo = SetupDiGetClassDevs (&InterfaceGuid, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hdevClassInfo == INVALID_HANDLE_VALUE)
{
// ERROR
printf(“Error retrieving a device information set for given GUID\n”);
}

// Determine how many elements are in the device information set.
MyDeviceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);

nMemberIndex = 0;
nStatus = TRUE;
while (nStatus == TRUE)
{
nMemberIndex++;
nStatus = SetupDiEnumDeviceInterfaces (hdevClassInfo, NULL, (LPGUID)
&InterfaceGuid, nMemberIndex, &MyDeviceData);
}

printf(“Number of DS2490 device interfaces: %i\n”, nMemberIndex);

//
// This is as far as I get
//

DeviceList = (struct DeviceData ) malloc ((nMemberIndex + 1) * sizeof(struct DeviceData));

if (DeviceList == NULL)
{
// ERROR
printf(“Error no devices.\n”);
return 0;
}

printf(“DeviceList successfully malloced\n\n”);

nStatus = FALSE;

// Enumerate devices that are associated with the interface.
for (i = 0; i < nMemberIndex; i++)
{
nStatus = SetupDiEnumDeviceInterfaces(hdevClassInfo, NULL,
(LPGUID)&InterfaceGuid,
i, &MyDeviceData);
if (nStatus != TRUE) break;
nStatus = FALSE;

// Retrieve the size of the device data.
nStatus = SetupDiGetDeviceInterfaceDetail(hdevClassInfo, &MyDeviceData,
NULL, 0, &nSize, NULL);

if (nStatus != FALSE) break; // This function is expected to fail
printf(“Size of device data is: %i\n”, nSize);

// Allocate memory for the device detail buffer.
pBuffer = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc (nSize);
if (pBuffer == NULL)
{
// ERROR
printf(“Error allocating memory for the device detail buffer.\n”);
return 0;
}

// Initialize variables.
MyDeviceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);

nStatus = SetupDiGetDeviceInterfaceDetail(hdevClassInfo, &MyDeviceData,
pBuffer, nSize, NULL,&DevInfo);
if (nStatus = FALSE)
{
// ERROR
printf(“Error retrieving device interface detail.\n”);
return 0;
}

//
***********************************
// Save the device interface path:
// This path can be used to open
// the interface with CreateFile.
// ****************************************

nLen = strlen(pBuffer->DevicePath) + 1;
DeviceList[i].Path = (TCHAR *) malloc (nLen * sizeof(TCHAR));
StringCchCopy(DeviceList[i].Path, nLen, pBuffer->DevicePath);
DeviceList[i].Path[nLen-1] = 0;

// Save the device instance.
DeviceList[i].DeviceInstance = DevInfo.DevInst;

printf(“DeviceList path is: %s\n”,DeviceList[i].Path);
//_tprintf(_T(“DeviceList path is: %s\n”),DeviceList[i].Path);
}

free(DeviceList); // we must clean up…
return 0;
}</setupapi.h></strsafe.h></tchar.h></stdlib.h></stdio.h></windows.h>

OK. I have refined the problem somewhat. Looks like I’m having difficulty with SetupDiGetDeviceInterfaceDetail. I call it to get the size of the “device interface path”. It returns a size (nsize) of 84. According to Microsoft, this call is supposed to “FAIL”. Then, I call it again with the correct size to get the “device interface path”. Then use the “device interface path” to eventually get a WinUSB handle. Can someone verify that this is the correct scenario? Is the “device interface path” something that can be printed out by printf?

Brian-

Yes to both of your questions. That’s the correct usage, and they print just fine (do it all the time in my test apps).

Thanks Bob. Looks like my troubles stem from the errors (actually several) in the code snippets found on the following Microsoft website for WinUSB (like passing incorrect structures to SetupDiXX routines, breaking a loop on an incorrect value, expecting the device interface path to be in an incorrect structure, etc):

http://msdn2.microsoft.com/en-us/library/aa476413.aspx

However, from the above link, I was able to at least understand what the general “flow” of what needs to be done to enumerate through and print out the device interface paths (to ultimately get a handle to WinUSB). And, now I have code (thanks to the WDK’s “USBView” program, specifically in the enum.c file) that works correctly!!!

Hi Brian! Is there a chance to post working code snippet?

Couldn’t wait :)), so here is the working part of the code. I’m still mad about MS publishing completely useless and unchecked code on their pages (http://msdn2.microsoft.com/en-us/library/aa476413.aspx).

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
#include <setupapi.h>
#include <initguid.h>

#include “WinUsbWrapper.h”

BOOL GetDevicePath (const GUID* InterfaceClassGuid, LPTSTR& lpDevicePath, DWORD dwMemberIndex)
{
BOOL bSuccess;

SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;

// Get handle to a device information set
HDEVINFO hdevClassInfo = SetupDiGetClassDevs (InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hdevClassInfo == INVALID_HANDLE_VALUE) {
printf (“Error retrieving device information set for given GUID\n”);
}

DWORD dwMembers;

for (dwMembers = 0; TRUE; dwMembers++)
{
DeviceInterfaceData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);

bSuccess = SetupDiEnumDeviceInterfaces (hdevClassInfo, NULL, InterfaceClassGuid, dwMembers, &DeviceInterfaceData);

if (!bSuccess) {
// Check if last item
if (GetLastError () == ERROR_NO_MORE_ITEMS);

break;
}
}

if (dwMemberIndex < dwMembers) {

DeviceInterfaceData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);

bSuccess = SetupDiEnumDeviceInterfaces(hdevClassInfo, NULL, InterfaceClassGuid, dwMemberIndex, &DeviceInterfaceData);

if (bSuccess) {

DWORD dwRequiredSize;

// Retrieve the size of the device data
bSuccess = SetupDiGetDeviceInterfaceDetail (hdevClassInfo, &DeviceInterfaceData, NULL, 0, &dwRequiredSize, NULL);

// This function is expected to fail
bSuccess = !bSuccess;

if (bSuccess) {
printf(“Size of device detail data is: %i\n”, dwRequiredSize);

// Allocate memory for the device detail buffer
PSP_INTERFACE_DEVICE_DETAIL_DATA pBuffer = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc (dwRequiredSize);
if (pBuffer) {

SP_DEVINFO_DATA DevInfoData;

// Initialize cbSize members, required by function call
pBuffer->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
DevInfoData.cbSize = sizeof (SP_DEVINFO_DATA);

bSuccess = SetupDiGetDeviceInterfaceDetail (hdevClassInfo, &DeviceInterfaceData, pBuffer, dwRequiredSize, NULL, &DevInfoData);

if (bSuccess) {
size_t nLength = wcslen (pBuffer->DevicePath) + 1;

lpDevicePath = (LPTSTR)malloc (nLength * sizeof(TCHAR));

if (lpDevicePath) {
StringCchCopy (lpDevicePath, nLength, pBuffer->DevicePath);
}
else {
bSuccess = FALSE;
}
}
else {
printf(“Error retrieving device interface detail.\n”);
}

free (pBuffer);
}
else {
printf (“Failed to allocate memory for the device detail data.\n”);

bSuccess = FALSE;
}
}
}
}

return bSuccess;
}

int main ()
{
LPTSTR lpDevicePath = NULL;

if (GetDevicePath (&GUID_DEVINTERFACE_TEST, lpDevicePath, 0)) {
printf (“Device path: %s\n”, lpDevicePath);

// Create a symbolic link to device interface
HANDLE hDeviceInterface;

hDeviceInterface = CreateFile (
lpDevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);

if (hDevice == INVALID_HANDLE_VALUE) {
// ERROR
}
else {
CWinUsbWrapper aWrapper;

aWrapper.Initialize (hDevice);
}
}

return 0;
}</initguid.h></setupapi.h></strsafe.h></tchar.h></stdlib.h></stdio.h></windows.h>

At the bottom of that page should be a link to “Add community content”- aka Wiki or close enough to it. So you could always just post your working snippet there.

I will ping the owner of this page to get it fixed

d

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@srce.hr
Sent: Wednesday, April 04, 2007 6:42 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] WinUSB and SetupDiGetDeviceInterfaceDetail

Couldn’t wait :)), so here is the working part of the code. I’m still
mad about MS publishing completely useless and unchecked code on their
pages (http://msdn2.microsoft.com/en-us/library/aa476413.aspx).

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
#include <setupapi.h>
#include <initguid.h>

#include “WinUsbWrapper.h”

BOOL GetDevicePath (const GUID* InterfaceClassGuid, LPTSTR&
lpDevicePath, DWORD dwMemberIndex)
{
BOOL bSuccess;

SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;

// Get handle to a device information set
HDEVINFO hdevClassInfo = SetupDiGetClassDevs
(InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hdevClassInfo == INVALID_HANDLE_VALUE) {
printf (“Error retrieving device information set for
given GUID\n”);
}

DWORD dwMembers;

for (dwMembers = 0; TRUE; dwMembers++)
{
DeviceInterfaceData.cbSize = sizeof
(SP_INTERFACE_DEVICE_DATA);

bSuccess = SetupDiEnumDeviceInterfaces (hdevClassInfo,
NULL, InterfaceClassGuid, dwMembers, &DeviceInterfaceData);

if (!bSuccess) {
// Check if last item
if (GetLastError () == ERROR_NO_MORE_ITEMS);

break;
}
}

if (dwMemberIndex < dwMembers) {

DeviceInterfaceData.cbSize = sizeof
(SP_INTERFACE_DEVICE_DATA);

bSuccess = SetupDiEnumDeviceInterfaces(hdevClassInfo,
NULL, InterfaceClassGuid, dwMemberIndex, &DeviceInterfaceData);

if (bSuccess) {

DWORD dwRequiredSize;

// Retrieve the size of the device data
bSuccess = SetupDiGetDeviceInterfaceDetail
(hdevClassInfo, &DeviceInterfaceData, NULL, 0, &dwRequiredSize, NULL);

// This function is expected to fail
bSuccess = !bSuccess;

if (bSuccess) {
printf(“Size of device detail data is:
%i\n”, dwRequiredSize);

// Allocate memory for the device detail
buffer
PSP_INTERFACE_DEVICE_DETAIL_DATA pBuffer
= (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc (dwRequiredSize);
if (pBuffer) {

SP_DEVINFO_DATA DevInfoData;

// Initialize cbSize members,
required by function call
pBuffer->cbSize = sizeof
(SP_DEVICE_INTERFACE_DETAIL_DATA);
DevInfoData.cbSize = sizeof
(SP_DEVINFO_DATA);

bSuccess =
SetupDiGetDeviceInterfaceDetail (hdevClassInfo, &DeviceInterfaceData,
pBuffer, dwRequiredSize, NULL, &DevInfoData);

if (bSuccess) {
size_t nLength = wcslen
(pBuffer->DevicePath) + 1;

lpDevicePath =
(LPTSTR)malloc (nLength * sizeof(TCHAR));

if (lpDevicePath) {
StringCchCopy
(lpDevicePath, nLength, pBuffer->DevicePath);
}
else {
bSuccess =
FALSE;
}
}
else {
printf(“Error retrieving
device interface detail.\n”);
}

free (pBuffer);
}
else {
printf (“Failed to allocate
memory for the device detail data.\n”);

bSuccess = FALSE;
}
}
}
}

return bSuccess;
}

int main ()
{
LPTSTR lpDevicePath = NULL;

if (GetDevicePath (&GUID_DEVINTERFACE_TEST, lpDevicePath, 0)) {
printf (“Device path: %s\n”, lpDevicePath);

// Create a symbolic link to device interface
HANDLE hDeviceInterface;

hDeviceInterface = CreateFile (
lpDevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);

if (hDevice == INVALID_HANDLE_VALUE) {
// ERROR
}
else {
CWinUsbWrapper aWrapper;

aWrapper.Initialize (hDevice);
}
}

return 0;
}


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer</initguid.h></setupapi.h></strsafe.h></tchar.h></stdlib.h></stdio.h></windows.h>