Cannot marshal string from DEV_BROADCAST_DEVICEINTERFACE in WM_DEVICECHANGE message

Hi,

I am using a C# windows app to detect insertion or removal of a USB mass storage device, and to get the USB device name when this happens.

I got as far as detecting insert/remove events, but i'm having trouble getting the device name.

I have successfully used RegisterDeviceNotification() to tell windows to send my WM_DEVICECHANGE messages for USB class devices.

When I receive a WM_DEVICECHANGE of (WParam) type DBT_DEVICEARRIVAL the MSDN docs say that LParam should point to a DEV_BROADCAST_HDR structure with some optional extra data on the end.

As I am interested in the DBT_DEVTYP_DEVICEINTERFACE structure, I check that the DEV_BROADCAST_HDR.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE, if so then I use Marshal.PtrToStructure(...) to transfer the data in LParam to a managed struct declared as follows:

        //typedef struct _DEV_BROADCAST_DEVICEINTERFACE {
        //  DWORD dbcc_size;
        //  DWORD dbcc_devicetype;
        //  DWORD dbcc_reserved;
        //  GUID  dbcc_classguid;
        //  TCHAR dbcc_name[1];
        //} DEV_BROADCAST_DEVICEINTERFACE, *PDEV_BROADCAST_DEVICEINTERFACE;
        [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Auto)]
        internal struct DEV_BROADCAST_DEVICEINTERFACE
        {
            internal int dbcc_size;
            internal int dbcc_devicetype;
            internal int dbcc_reserved;
            internal Guid dbcc_classguid;
            internal string dbcc_name;
        }

Soooo, here is the problem:

 all the fields in my DEV_BROADCAST_DEVICEINTERFACE are populated correctly except for the one i'm after: dbcc_name.

In a sample run of my program, I plug in a USB pendrive and get the following DEV_BROADCAST_DEVICEINTERFACE data:

dbcc_size      172      int
dbcc_devicetype      5      int
dbcc_reserved      0      int
dbcc_classguid      {a5dcbf10-6530-11d2-901f-00c04fb951ed}      System.Guid
dbcc_name      ""      string

The value of 172 for dbcc_size suggests that windows IS sending the correct data, but that i'm not correctly marshalling it.

Thanks,

Ben.
ben84Asked:
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.

pgnatyukCommented:
This example will help you a lot:
Detecting Media Insertion or Removal
http://msdn.microsoft.com/en-us/library/aa363215(v=VS.85).aspx
(FirstDriveFromMask - pay attention on this function, this is the one character you are trying to convert to a string, but it is a mask)
DEV_BROADCAST_HDR Structure:
 http://msdn.microsoft.com/en-us/library/aa363246(v=VS.85).aspx
 I think you make a mistake with this structure.
Check here:
MSDN. DBT_DEVICEARRIVAL Event
http://msdn.microsoft.com/en-us/library/aa363205(v=VS.85).aspx
"If media is being inserted, the type of device arriving is a volume (the dbch_devicetype member is DBT_DEVTYP_VOLUME) and the change effects the media (the dbcv_flags member is DBTF_MEDIA)."
If I remember correctly, your app receives few such messages. Comment the code that parse this message in your app and add a Trace - you will see what's going on, no breakpoints.
 
0
ben84Author Commented:
Thanks for your reply pgnatyuk, but i'm afraid what you've posted doesn't really help.   I have already read all three of the above, and DBT_DEVTYP_VOLUME does not give me the info I need: the DevceID.  My problem is specifically with marshalling, and I think my question should have stressed that.

0
ben84Author Commented:
I have found a solution to my marshalling problem, which is discussed here:

http://stackoverflow.com/questions/2208722/how-to-get-friendly-device-name-from-dev-broadcast-deviceinterface-and-device-ins

When converting an unmanaged structure like:

typedef struct _DEV_BROADCAST_DEVICEINTERFACE {
  DWORD dbcc_size;
  DWORD dbcc_devicetype;
  DWORD dbcc_reserved;
  GUID  dbcc_classguid;
  TCHAR dbcc_name[1];
} DEV_BROADCAST_DEVICEINTERFACE, *PDEV_BROADCAST_DEVICEINTERFACE;

Where the length of the string dbcc_name can only be found by look at the total size of the structure ni dbcc_size...

Using a C# stuct like the following:

        [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
        internal struct DEV_BROADCAST_DEVICEINTERFACE
        {
            internal int dbcc_size;
            internal int dbcc_devicetype;
            internal int dbcc_reserved;
            internal Guid dbcc_classguid;
            internal string dbcc_name;
        }

Then .NET P/NIVOKE appears to assume the string is always zero length.   I have found two ways round this:

1) Use a pre-allocated buffer (for example 255 bytes) for the string by declaring the struct as follows:

public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 255, ArraySubType = System.Runtime.InteropServices.UnmanagedType.LPArray)]
    public string dbcc_name;
}

or....

2) populate the first C# struct (above) with PtrToStructure(), then get the string as a separate step:

Win32.DEV_BROADCAST_DEVICEINTERFACE bdi = new in32.DEV_BROADCAST_DEVICEINTERFACE();
bdi = (Win32.DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(m.LParam, typeof(Win32.DEV_BROADCAST_DEVICEINTERFACE));
IntPtr p = (IntPtr)((long)m.LParam + (long)Marshal.OffsetOf(typeof(Win32.DEV_BROADCAST_DEVICEINTERFACE), "dbcc_name"));
bdi.dbcc_name = Marshal.PtrToStringAuto(p);


Win32.DEV_BROADCAST_DEVICEINTERFACE bdi = new in32.DEV_BROADCAST_DEVICEINTERFACE();
bdi = (Win32.DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(m.LParam, typeof(Win32.DEV_BROADCAST_DEVICEINTERFACE));
IntPtr p = (IntPtr)((long)m.LParam + (long)Marshal.OffsetOf(typeof(Win32.DEV_BROADCAST_DEVICEINTERFACE), "dbcc_name"));
bdi.dbcc_name = Marshal.PtrToStringAuto(p);

Open in new window

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
absinghCommented:
what is the use of the application that you are trying to build
0
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
Microsoft Development

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.