Marshalling DevicePath out from SP_DEVICE_INTERFACE_DETAIL_DATA

LVL 6
DeadlyTrevAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

DeadlyTrevAuthor Commented:
I cannot get the DevicePath back from SetupDiGetDeviceInterfaceDetail using VB.NET.  Below is a snippet of code from where the problem occurs ...  (note: this code references a tweaked version of the Native.cs class defined in the project at http://www.codeproject.com/useritems/usbeject.asp).

        'Try to list all the USB printers and their device paths
        Dim index As Integer = 0
        Dim classGuid As Guid = New Guid(Native.GUID_DEVINTERFACE_PRINTER)
        Dim deviceInfoSet As IntPtr = Native.SetupDiGetClassDevs(classGuid, 0, 0, Native.DIGCF_DEVICEINTERFACE Or Native.DIGCF_PRESENT)
        If deviceInfoSet.ToInt32() = Native.INVALID_HANDLE_VALUE Then
            Throw New System.Exception("Error #" + Marshal.GetLastWin32Error().ToString)
            Exit Sub
        End If

        Do
            Dim interfaceData As NativeInterop.Library.Native.SP_DEVICE_INTERFACE_DATA = New Native.SP_DEVICE_INTERFACE_DATA
            If Not Native.SetupDiEnumDeviceInterfaces(deviceInfoSet, Nothing, classGuid, index, interfaceData) Then
                Dim errorNum As Long = Marshal.GetLastWin32Error()
                If errorNum <> Native.ERROR_NO_MORE_ITEMS Then
                    Throw New System.Exception("Error #" + errorNum.ToString)
                End If
                Exit Sub
            End If

            Dim devData As Native.SP_DEVINFO_DATA = New Native.SP_DEVINFO_DATA
            Dim size As Integer = 0
            If Not Native.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, interfaceData, IntPtr.Zero, 0, Size, devData) Then
                Dim errorNum As Long = Marshal.GetLastWin32Error()
                If errorNum <> Native.ERROR_INSUFFICIENT_BUFFER Then
                    Throw New System.Exception("Error #" + errorNum.ToString)
                End If
            End If

            Dim buffer As IntPtr = Marshal.AllocHGlobal(size)
            Dim detailData As Native.SP_DEVICE_INTERFACE_DETAIL_DATA = New Native.SP_DEVICE_INTERFACE_DETAIL_DATA
            detailData.cbSize = Marshal.SizeOf(detailData)
            Marshal.StructureToPtr(detailData, buffer, False)
            If Not Native.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, interfaceData, buffer, size, size, devData) Then
                Marshal.FreeHGlobal(buffer)
                Throw New System.Exception("Error #" + Marshal.GetLastWin32Error().ToString)
            End If

' \/ \/ This part is the problem  \/ \/ ... retrieving the DevicePath
'---------------------------------------------------------------------------
            Dim newDetailData As Native.SP_DEVICE_INTERFACE_DETAIL_DATA
            Marshal.PtrToStructure(buffer, newDetailData) ' Runtime error on this line: Structure must not be value class
            Dim devicePath As String = Marshal.PtrToStringAuto(detailData.devicePath)
'---------------------------------------------------------------------------
' /\ /\ This part is the problem  /\ /\ ... retrieving the DevicePath

            MsgBox("Interface GUID = " & classGuid.ToString & vbCrLf & devicePath)
            Marshal.FreeHGlobal(buffer)

            index = index + 1
        Loop Until False


Can anyone tell me the correct way to marshal the string back out from the pointer in the SP_DEVICE_INTERFACE_DETAIL_DATA structure?

The structure is defined in C# as;
        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        public struct SP_DEVICE_INTERFACE_DETAIL_DATA
        {
            public int cbSize;
            public short devicePath;
        }
0
AlexFMCommented:
Marshal.PtrToStructure Method (IntPtr, Object):
You cannot use this overload method with value types.

Marshal.PtrToStructure Method (IntPtr, Type):
You can pass a value type to this overload method. In this case, the returned object is a boxed instance.

Use other PtrToStructure overload.
0
DeadlyTrevAuthor Commented:
Sorry ... mildly confused.   Please explain value type vs boxed instance.  Should I be using something like newDetailData = Marshal.PrtToStructure(buffer, Native.SP_DEVICE_INTERFACE_DETAIL_DATA) ??

Cheeky request, but could you tell me exactly what WILL work in my code above ...
0
Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

AlexFMCommented:
SP_DEVICE_INTERFACE_DETAIL_DATA is structure, this means, value type. Marshal.PtrToStructure Method (IntPtr, Object) doesn't work with value types. Use Marshal.PtrToStructure Method (IntPtr, Type).

Dim newDetailData As Native.SP_DEVICE_INTERFACE_DETAIL_DATA = Marshal.PtrToStructure(buffer, GetType( Native.SP_DEVICE_INTERFACE_DETAIL_DATA ))
0
DeadlyTrevAuthor Commented:
AlexFM,  Thanks.  The compiler happily swallows that.

However ...   :-(    ...    the next line of code
          Dim devicePath As String = Marshal.PtrToStringAuto(detailData.devicePath)
although it executes correctly, I don't get a valid string as a result.

After executing
          Dim newDetailData As Native.SP_DEVICE_INTERFACE_DETAIL_DATA = Marshal.PtrToStructure(buffer, GetType( Native.SP_DEVICE_INTERFACE_DETAIL_DATA ))
the newDetailData structure contains; .cbSize = 6, .devicePath = 92

I was expecting a pointer to string ... not 92.  Any clues?

   
0
AlexFMCommented:
public short devicePath;

Short pointer? This looks strange, you need to understand what happens in unmanaged code and what data to expect. In such case is it good to have some working C code which calls the same unmanaged functions.
0
DeadlyTrevAuthor Commented:
I have working C code.  I want to make it work with dotNET code and have been trying to convert off-and-on for two weeks now .... hence my questions on Expert's Exchange.

Does anyone have working dotNet code to retrieve the device paths for USB printers?
0
AlexFMCommented:
typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA
{  
    DWORD cbSize;  
    TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA,  *PSP_DEVICE_INTERFACE_DETAIL_DATA;

This is structure with embedded array. Find some API sample with embedded array and handle it by the same way. For example:
http://pinvoke.net/default.aspx/Structures/TIME_ZONE_INFORMATION.html

See using of SizeConst attribute.
0
DeadlyTrevAuthor Commented:
Found a solution eventually.

With the C# structure defined thus:

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
        public class SP_DEVICE_INTERFACE_DETAIL_DATA
        {
            public int  cbSize;
            public char devicePath;
        }

Then using VB to do the work ...

            ' Retrieve the DevicePath from SP_DEVICE_INTERFACE_DETAIL_DATA
            ' 4 == Offset of first DevicePath character
            Dim pDevicePath As IntPtr = CType(buffer.ToInt32 + 4, IntPtr)
            Dim DevicePath As String = Marshal.PtrToStringAuto(pDevicePath)
            Marshal.FreeHGlobal(buffer)

The above code works; I can increment the buffer pointer by converting to Int32 then casting back to an IntPtr.  Ugly, and probably not strictly proper, but does the job.
0
DeadlyTrevAuthor Commented:
Sometimes I wish EE had the ability to award points for trying to help.  I don't want to flag a response as 'Correct' when it's not a correct answer, but would like to contribute something just to acknowlege that they tried.

Anyway ... the above post is the answer I was looking for.  PAQ and refund I guess.  I have seen this question asked before but with no resolution.

DT.
0
GranModCommented:
PAQed with points refunded (125)

GranMod
Community Support Moderator
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.