Marshalling estructuras dinámicas en tiempos de ejecución de 32 y 64 bits

I'm calling SetupDiGetDeviceInterfaceDetail() aquí and the SP_DEVICE_INTERFACE_DETAIL_DATA structure is not marshaling correctly. The structures definition can be found aquí. I've tried using the definition for this structure from, aquí, pero fue en vano.

So far, when the call to the function succeeds (i.e. the marshaler doesn't throw an error), the return value is 1784 (INVALID_USER_BUFFER). The kicker is, when this code executes from a 32-bit process on my box, all of this works just fine. When it's run in a 64-bit process, I have this problem.

My current SetupDiGetInterfaceDetailData() signature looks like this:

[DllImport(@"c:\Windows\System32\SetupApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
    SafeHandleZeroOrMinusOneIsInvalid deviceInfoSet,
    ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
    IntPtr deviceInterfaceDetailData,
    uint deviceInterfaceDetailDataSize,
    IntPtr requiredSize,
    IntPtr deviceInfoData);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public UInt32 cbSize;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string DevicePath;

Currently, I am allocating memory with Marshal.AllocHGlobal() and writing/reading data from that buffer using the Marshal.* family of functions.

For reference, this is what I'm doing:

public string GetPathToDevice(SafeHandleZeroOrMinusOneIsInvalid hDevList,
                              SP_DEVICE_INTERFACE_DATA devIntfData)
    uint sizeNeeded = 0;
    // get's the size needed
                                                 ref devIntfData,
                                                 ref sizeNeeded,

    IntPtr pBuffer = Marshal.AllocHGlobal((int)(sizeNeeded + 4)); // +4 for cbSize
                                                 ref devIntfData,

    // copy bytes from unmanaged space in pBuffer to a manged byte array
    // free unmanaged memory

    return theStringParsedFromByteArray;

As I mentioned, I've tried defining a structure as outlined by for SP_DEVICE_INTERFACE_DETAIL_DATA (see above link) and made a new PInvoke method signature to handle that. When running from the 64-bit system, I get the same issue, i.e. the function returns 1784. The reason seems to be that references in C#, when running in a 64-bit runtime, are 8-byte aligned (found that in another StackOverflow article). I've tried various layouts to that structure trying to force the layout (using explicit and field offset) to a 4-byte aligned struct, but that didn't work for me either. I had compile time problems.

I have tried using various decorations to the PInvoke method sigature parameters. Such as, MarshalAs(UnmanagedType.LPStruct) to which I'm continually pairing improperly. I'm now to the point that I need help with this.

What I really don't understand is why it's happening at all. Even if it does work on my box when running in a 32-bit runtime, wouldn't a 64-bit runtime simply connect me to the correct 64-bit versions of the Setup API? What's the problem?

Thanks for any help, Andy

El problema está resuelto

The good thing is, it's solved now, the irritating thing is I don't like fixing things within an hour or two of posting here. So, the problem was indeed that it was a 64 bit issue. The error code from Marshal.GetLastWin32Error() was telling me the problem. The cbSize value was incorrect. I changed it to 8 and everything works now.

Please, someone, explain to me why the size is now 8 on 64 bits? The structure is now above (a commenter asked me to include it). The structure consists of two members, a single DWORD and a TCHAR[ANYSIZE_ARRAY]. ANYSIZE_ARRAY evaluates to 1, TCHAR is always a WCHAR if Unicode and a char otherwise. A DWORD is always a 32-bit quantity (4 bytes) and a single TCHAR for Unicode is 2 bytes. So, 4 + 2 = 6. Why is it 8? Is this because of byte alignment for that structure in 64-bits? I'd really like to understand this.

At any rate, setting the cbSize member to 8 for 64 bit, and 6 for 32 bit, works and I'm able to use the structure defined above instead of the raw memory allocating/deallocating and marshaling.

preguntado el 09 de marzo de 12 a las 16:03

Post the structures you defined in C# You also need define the size of structure percisely. -

@Ramhound Presently, the structure is just raw memory that I'm writing to individually. The structure I've defined from PInvoke is at the link I posted. I will edit to show it there. -

Tener c:\Windows\System32\SetupApi.dll as your DLL name is a bad idea. Hard coding that path is dangerous. Just use setupapi.dll. -

If I were you I would not make a SP_DEVICE_INTERFACE_DETAIL_DATA struct declaration at all in the pinvoke. ByValTStr is just not useful here. Do it with AllocHGlobal and marshall the contents by hand. -

It's been quite a while, but to answer the question you posed in "Problem is solved" -- yes, 64-bit systems always align on 8-byte boundaries, so the 6-byte structure is padded out to 8-bytes on x64 -

1 Respuestas

I also fell over this problem, because i copied the code from this answer: which has an error in the struct definition. Maybe you did the same mistake.

Looking at the definition of SP_DEVINFO_DATA at it's last parameter is a pointer, not an uint as in the other post.

Since i changed the struct definition to:

private struct SP_DEVINFO_DATA
    /// <summary>Size of the structure, in bytes.</summary>
    public uint cbSize;
    /// <summary>GUID of the device interface class.</summary>
    public Guid ClassGuid;
    /// <summary>Handle to this device instance.</summary>
    public uint DevInst;
    /// <summary>Reserved; do not use.</summary>
    public IntPtr Reserved;


Marshall.SizeOf(new SP_DEVINFO_DATA ()) returns 32 now instead of 28 and my serial ports are shown.

contestado el 23 de mayo de 17 a las 13:05

My problem wasn't with the definition of SP_DEVINFO_DATA but with SP_DEVICE_INTERFACE_DETAIL_DATA. I believe the solution was to use David Heffernan's suggestion and simply allocate the memory needed using AllocHGlobal and do the hard work by hand. - Andrés Falanga

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.