如何使用DEV_BROADCAST_DEVICEINTERFACE

时间:2016-05-19 08:41:16

标签: c# visual-studio usb

我试图从USB设备上捕获VIP和PID:

    public const int WM_DEVICECHANGE = 0x219;
    public const int DBT_DEVTYP_VOLUME = 0x00000002;
    public const int DBT_DEVICEARRIVAL = 0x8000;

    [StructLayout(LayoutKind.Sequential)]
    internal class DEV_BROADCAST_HDR
    {
        public int dbch_size;
        public int dbch_devicetype;
        public int dbch_reserved;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct DEV_BROADCAST_DEVICEINTERFACE
    {
        public int dbcc_size;
        public int dbcc_devicetype;
        public int dbcc_reserved;
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)]
        public byte[] dbcc_classguid;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public char[] dbcc_name;
    }

    public void WndProc(ref Message m)
    {
        if (m.Msg == WM_DEVICECHANGE) //Device state has changed
        {
           switch (m.WParam.ToInt32())
           {
               case DBT_DEVICEARRIVAL: //New device arrives
               DEV_BROADCAST_HDR hdr;
               hdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
               if (hdr.dbch_devicetype == DBT_DEVTYP_VOLUME) //If it is a USB Mass Storage or Hard Drive
               {
                    //Save Device name
                    DEV_BROADCAST_DEVICEINTERFACE deviceInterface;
                    string deviceName = "";
                    deviceInterface = (DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_DEVICEINTERFACE));
                    deviceName = new string(deviceInterface.dbcc_name).Trim();
                }
           }
        }
    }

但是deviceName总是返回一个包含非感知字符的字符串。我在CharSet结构中更改了DEV_BROADCAST_DEVICEINTERFACE并将dbcc.name声明为字符串,但结果是相同的。

我想避免从注册表中读取,在我读过的所有内容I have seen中,只有在DEV_BROADCAST_HEADER时才可以将DEV_BROADCAST_DEVICEINTERFACE投射到dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE 。在我的例子中,dbch_devicetype是2,而不是5,我正在使用一些常见的USB大容量存储设备。我究竟做错了什么?提前谢谢!

1 个答案:

答案 0 :(得分:0)

也许,有一种更优雅的方式来解决这个问题,但至少,经过长时间的搜索,这似乎有效。

一方面,我注册了应用程序以便接收正确的信息以将其转换为DEV_BROADCAST_DEVICEINTERFACE结构(在这种情况下DEV_BROADCAST_HEADER.dbch_devicetype为5)。所以,我能够检索VID和PID信息。另一方面,我保留WndProc收到的第一个Windows消息,以检索Windows在连接USB设备时所分配的卷(DEV_BROADCAST_HEADER.dbch_devicetype为2)。然后,我收到两条消息。

在代码中:

public const int WM_DEVICECHANGE = 0x219;
public const int DBT_DEVTYP_VOLUME = 0x00000002;
public const int DBT_DEVICEARRIVAL = 0x8000;
public const int DBT_DEVTYP_DEVICEINTERFACE = 0x00000005;

private IntPtr notificationHandle;

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);

[StructLayout(LayoutKind.Sequential)]
internal class DEV_BROADCAST_HDR
{
    public int dbch_size;
    public int dbch_devicetype;
    public int dbch_reserved;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
    public char[] dbcc_name;
}

public void WndProc(ref Message m)
{
    if (m.Msg == WM_DEVICECHANGE) //Device state has changed
    {
       switch (m.WParam.ToInt32())
       {
           case DBT_DEVICEARRIVAL: //New device arrives
           DEV_BROADCAST_HDR hdr;
           hdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));
                        if (hdr.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) //If it is a USB Mass Storage or Hard Drive
                        {
                            //Save Device name
                            DEV_BROADCAST_DEVICEINTERFACE deviceInterface = (DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_DEVICEINTERFACE));
                            deviceName = new string(deviceInterface.dbcc_name);
                            deviceNameFiltered = deviceName.Substring(0, deviceName.IndexOf('{'));
                            vid = GetVid(deviceName);
                            pid = GetPid(deviceName);
                        }
                        if (hdr.dbch_devicetype == DBT_DEVTYP_VOLUME)
                        { 
                            DEV_BROADCAST_VOLUME volume;
                            volume = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
                            //Translate mask to device letter
                            driveLetter = DriveMaskToLetter(volume.dbcv_unitmask);
       }
    }
}

要注册应用程序以便接收正确的信息以将其强制转换为DEV_BROADCAST_DEVICEINTERFACE结构,必须从Form类调用最后一个RegisterUsbDeviceNotification方法,并将其窗口处理程序作为参数。

    public void RegisterUsbDeviceNotification(IntPtr windowHandle)
    {
        DEV_BROADCAST_DEVICEINTERFACE deviceInterface = new DEV_BROADCAST_DEVICEINTERFACE
        {
            dbcc_classguid = GuidDevinterfaceUSBDevice,
            dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE,
            dbcc_reserved = 0,
        };

        deviceInterface.dbcc_size = Marshal.SizeOf(deviceInterface);
        IntPtr buffer = Marshal.AllocHGlobal(deviceInterface.dbcc_size);
        Marshal.StructureToPtr(deviceInterface, buffer, true);

        notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);
    }