Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1210
  • Last Modified:

Marshalling DevicePath out from SP_DEVICE_INTERFACE_DETAIL_DATA

0
DeadlyTrev
Asked:
DeadlyTrev
  • 7
  • 4
1 Solution
 
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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
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

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 7
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now