Having trouble with retrieving device path using SetUpApi and Hidsdi

CakeDev 70 Reputation points
2024-07-27T21:14:24.1833333+00:00

I'm currently having an issue with my code that detects HID devices with a specific VID and PID for joy-cons. I'm using SetUpApi and Hidsdi that I platform invoked, but the device path is only showing as question marks. Can someone assist in finding a solution? Here's the code I'm working with.

using System;

using System.Runtime.InteropServices;

class Program

{

[DllImport("hid.dll")]

private static extern void HidD_GetHidGuid(out Guid HidGuid);

[DllImport("setupapi.dll")]

private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, string Enumerator, IntPtr hwndParent, uint Flags);

[DllImport("setupapi.dll")]

private static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref Guid InterfaceClassGuid, uint MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);

[DllImport("setupapi.dll")]

private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, uint DeviceInterfaceDetailDataSize, out uint RequiredSize, IntPtr DeviceInfoData);

[DllImport("setupapi.dll")]

private static extern void SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

[StructLayout(LayoutKind.Sequential)]

private struct SP_DEVICE_INTERFACE_DATA

{

public uint cbSize;

public Guid InterfaceClassGuid;

public uint Flags;

public IntPtr Reserved;

}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

private struct SP_DEVICE_INTERFACE_DETAIL_DATA

{

public uint cbSize;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]

public string DevicePath;

}

[StructLayout(LayoutKind.Sequential)]

private struct SP_DEVINFO_DATA

{

public uint cbSize;

public Guid ClassGuid;

public uint DevInst;

public IntPtr Reserved;

}

private const uint DIGCF_PRESENT = 0x00000002;

private const uint DIGCF_DEVICEINTERFACE = 0x00000010;

static void Main()

{

Console.WriteLine("Retrieving HID GUID...");

Guid hidGuid;

HidD_GetHidGuid(out hidGuid);

Console.WriteLine($"HID GUID: {hidGuid}");

Console.WriteLine("Getting device info set...");

IntPtr deviceInfoSet = SetupDiGetClassDevs(ref hidGuid, null, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

if (deviceInfoSet == (IntPtr)(-1))

{

Console.Error.WriteLine("Error getting device info set");

return;

}

Console.WriteLine("Device info set retrieved successfully.");

SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();

deviceInterfaceData.cbSize = (uint)Marshal.SizeOf(deviceInterfaceData);

Console.WriteLine("Initialized SP_DEVICE_INTERFACE_DATA.");

uint index = 0;

while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref hidGuid, index++, ref deviceInterfaceData))

{

Console.WriteLine($"Enumerating device interface #{index + 1}...");

uint requiredSize = 0;

SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, IntPtr.Zero, 0, out requiredSize, IntPtr.Zero);

if (requiredSize == 0)

{

Console.Error.WriteLine("Error getting required size for device interface detail");

continue;

}

Console.WriteLine($"Required size for device interface detail: {requiredSize}");

IntPtr detailDataBuffer = IntPtr.Zero;

try

{

detailDataBuffer = Marshal.AllocHGlobal((int)requiredSize);

Marshal.WriteInt32(detailDataBuffer, (int)(IntPtr.Size == 8 ? 8 : 5)); // cbSize for 64-bit or 32-bit

Console.WriteLine("Allocated memory for SP_DEVICE_INTERFACE_DETAIL_DATA.");

if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, detailDataBuffer, requiredSize, out requiredSize, IntPtr.Zero))

{

IntPtr pDevicePath = IntPtr.Add(detailDataBuffer, 4); // Offset for cbSize (4 bytes)

string devicePath = Marshal.PtrToStringAuto(pDevicePath);

Console.WriteLine($"Device path: {devicePath}");

string searchString = "pid&2007";

string otherSearchString = "pid&2006";

string VIDstring = "vid&0002057e";

if (devicePath != null && (devicePath.Contains(searchString) || devicePath.Contains(otherSearchString)))

{

if (devicePath.Contains(VIDstring))

{

Console.WriteLine("Yep, It's a joycon!");

}

}

}

else

{

Console.Error.WriteLine("Error getting device interface detail");

}

}

finally

{

if (detailDataBuffer != IntPtr.Zero)

{

Marshal.FreeHGlobal(detailDataBuffer);

Console.WriteLine("Freed allocated memory for SP_DEVICE_INTERFACE_DETAIL_DATA.");

}

}

}

SetupDiDestroyDeviceInfoList(deviceInfoSet);

Console.WriteLine("Destroyed device info set.");

}

}

(Srry if i'm not supposed to post all of this code, this is my first post.)

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,652 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,011 questions
0 comments No comments
{count} votes

Accepted answer
  1. Castorix31 85,881 Reputation points
    2024-07-28T00:35:25.01+00:00

    I get correct Device path with this test :

    {
        string sDevicePath = null;
        Guid hidGuid;
        HidD_GetHidGuid(out hidGuid);
        IntPtr hDeviceInfoSet = SetupDiGetClassDevs(ref hidGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
        if (hDeviceInfoSet != IntPtr.Zero)
        {
            var did = new SP_DEVICE_INTERFACE_DATA() { cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA)) };
            for (var nIndex = 0; SetupDiEnumDeviceInterfaces(hDeviceInfoSet, IntPtr.Zero, ref hidGuid, nIndex, ref did); nIndex++)
            {
                int dwBytes = 0;
                bool bRet = SetupDiGetDeviceInterfaceDetail(hDeviceInfoSet, ref did, IntPtr.Zero, 0, ref dwBytes, IntPtr.Zero);
                if (!bRet && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
                {
                    SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA();
                    didd.cbSize = IntPtr.Size == 8 ? 8 : 6;                
                    SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA();
                    dd.cbSize = Marshal.SizeOf(dd);
                    bRet = SetupDiGetDeviceInterfaceDetail(hDeviceInfoSet, ref did, ref didd, dwBytes, ref dwBytes, ref dd);
                    if (bRet)
                    {
                        sDevicePath = didd.DevicePath;
                        Console.WriteLine("Device Path : {0}", sDevicePath);
                    }
                }
            }
            SetupDiDestroyDeviceInfoList(hDeviceInfoSet);
        }
    }
    
    

    with :

            [DllImport("Hid.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern void HidD_GetHidGuid(out Guid HidGuid);
            
            [DllImport("Setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hWndParent, int Flags);
            
            public const int DIGCF_PRESENT = 0x00000002;
            public const int DIGCF_DEVICEINTERFACE = 0x00000010;
            public const int ERROR_INSUFFICIENT_BUFFER = 122;
            public const int MAX_PATH = 260;
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = PACK_SIZE)]
            public struct SP_DEVINFO_DATA
            {
                public int cbSize;
                public Guid ClassGuid;
                public int DevInst;
                public IntPtr Reserved;
            }
    #if WIN64
            public const int PACK_SIZE = 8;
    #else
            public const int PACK_SIZE = 1;
    #endif
    
            [DllImport("Setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, int DeviceInterfaceDetailDataSize, ref int RequiredSize, ref SP_DEVINFO_DATA DeviceInfoData );
    
            [DllImport("Setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr Ptr, int DeviceInterfaceDetailDataSize, ref int RequiredSize, IntPtr PtrInfo);
    
            [DllImport("Setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref Guid InterfaceClassGuid, int MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
    
            [StructLayout(LayoutKind.Sequential)]
            public struct SP_DEVICE_INTERFACE_DATA
            {
                public int cbSize;
                public Guid InterfaceClassGuid;
                public int Flags;
                public IntPtr Reserved;
            }
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            public struct SP_DEVICE_INTERFACE_DETAIL_DATA
            {
                public int cbSize;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
                public string DevicePath;
            }  
            
            [DllImport("Setupapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
          	public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
    
    
    1 person found this answer helpful.
    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.