2
votes

I try to call Window API function SetupDiEnumDeviceInterfaces from C# on 64bits architecture. I import function and declare additional structures.

    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern bool SetupDiEnumDeviceInterfaces(
        IntPtr deviceInfoSet,
        SP_DEVINFO_DATA deviceInfoData,
        ref Guid interfaceClassGuid,
        int memberIndex,
        SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

    [StructLayout(LayoutKind.Sequential)]
    internal class SP_DEVINFO_DATA
    {
        internal int cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
        internal Guid classGuid = Guid.Empty; // temp
        internal int devInst = 0; // dumy
        internal int reserved = 0;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 2)]
    internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
    {
        internal int cbSize;
        internal short devicePath;
    }

Then I call this function as follows:

        int index = 0;
        Guid _classGuid = Guid.Empty;
        IntPtr _deviceInfoSet = IntPtr.Zero;

        Native.SP_DEVICE_INTERFACE_DATA interfaceData = new Native.SP_DEVICE_INTERFACE_DATA();

        if (!Native.SetupDiEnumDeviceInterfaces(_deviceInfoSet, null, ref _classGuid, index, interfaceData))
       {
             int error = Marshal.GetLastWin32Error();
             if (error != Native.ERROR_NO_MORE_ITEMS)
                 throw new Win32Exception(error);
             break;
         }

If runnig on 32bits architecture then all is well.

If runnig on 64bits architecture then SetupDiEnumDeviceInterfaces return false with last win error equal 1784. The reason is that in struct interfaceData field cbSize has not valid value for 64bits architecture(as int alias Int32).

From official documentation

DeviceInterfaceData [out] A pointer to a caller-allocated buffer that contains, on successful return, a completed SP_DEVICE_INTERFACE_DATA structure that identifies an interface that meets the search parameters. The caller must set DeviceInterfaceData.cbSize to sizeof(SP_DEVICE_INTERFACE_DATA) before calling this function.

Trying to replace the type int(alias Int32) of the type Int64 for fields: cbSize, devInt, reserved.

How Can I replace class Guid for 64bits architecture?

If I try replace Guid simply of the type long:

[StructLayout(LayoutKind.Sequential)]
    internal class SP_DEVICE_INTERFACE_DATA 
    {
        internal Int64 cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
        internal long  interfaceClassGuid = 0; // temp
        internal Int64 flags = 1;
        internal Int64 reserved = 0;
    }

With such a structure definition all works but I lose the convenience of working with a special class for guid. In the class definition Guid also used the type int so the right size will not be calculated on 64bits architecture.

3

3 Answers

0
votes

The problem is not your GUID declarations; the reason SetupDiEnumDeviceInterfaces is failing out on 64-bit platforms is that you're not using the correct data type for the reserved field on each of SP_DEVINFO_DATA and SP_DEVICE_INTERFACE_DATA.

The structure definitions for SP_DEVINFO_DATA and SP_DEVICE_INTERFACE_DATA show that the reserved fields are declared as UINT_PTR, which is a pointer type. These should be declared in your P/Invoke types as IntPtr.

(Also, all of your int fields should instead be defined as uint, as those fields map to the DWORD native type.)

[StructLayout(LayoutKind.Sequential)]
internal class SP_DEVINFO_DATA
{
    internal uint cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
    internal Guid classGuid;
    internal uint devInst;
    internal IntPtr reserved;
}

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
    internal uint cbSize;
    internal short devicePath;
}

[StructLayout(LayoutKind.Sequential)]
internal class SP_DEVICE_INTERFACE_DATA 
{
    internal uint cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
    internal Guid interfaceClassGuid;
    internal uint flags;
    internal IntPtr reserved;
}
0
votes

You might try setting your

[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal struct SP_DEVICE_INTERFACE_DETAIL_DATA

to:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct SP_DEVICE_INTERFACE_DETAIL_DATA

From what I've read, Pack = 8 for 32 bit, Pack = 1 for 64 bit.

0
votes

You need to use uint in all fields(not int), and IntPtr in Reserved field.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVINFO_DATA
{
    public uint cbSize;
    public Guid ClassGuid;
    public uint DevInst;
    public IntPtr Reserved;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DATA
{
    public uint cbSize;
    public Guid InterfaceClassGuid;
    public uint Flags;
    public IntPtr Reserved;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
    public uint cbSize;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string DevicePath;
}

To set cbSize in code use this:

Win32.SP_DEVICE_INTERFACE_DATA did = new Win32.SP_DEVICE_INTERFACE_DATA();
did.cbSize = (uint)Marshal.SizeOf(did);

Win32.SP_DEVICE_INTERFACE_DETAIL_DATA didd = new Win32.SP_DEVICE_INTERFACE_DETAIL_DATA();
didd.cbSize = (uint)Marshal.SizeOf(didd);