c#中使用RegisterNotification来接收特定GUID的设备消息

在WIN32 API中提供了一个RegisterNotification函数来接收系统发过来的message。本文介绍一下在csharp中调用该接口来监听特定GUID的设备消息。

在MSDN中,RegisterDeviceNotification的原型为

1
2
3
4
5
HDEVNOTIFY WINAPI RegisterDeviceNotification(
_In_ HANDLE hRecipient,
_In_ LPVOID NotificationFilter,
_In_ DWORD Flags
);

其中NotificationFilter起到消息过滤的作用,也就是说如果我们只想收到特定的消息,可以传递特定的NotificationFilter参数。

NotificationFilter [in]

A pointer to a block of data that specifies the type of device for which notifications should be sent. This block always begins with the DEV_BROADCAST_HDR structure. The data following this header is dependent on the value of the dbch_devicetype member, which can be DBT_DEVTYP_DEVICEINTERFACE or DBT_DEVTYP_HANDLE. For more information, see Remarks.

需要注意的是,对于PORT设备,arrival和removal消息会自动广播给顶层窗口。所以如果你的设备不是PORT设备,又想收到arrival和removal消息,需要手动注册消息监听。

The DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE events are automatically broadcast to all top-level windows for port devices. Therefore, it is not necessary to call RegisterDeviceNotification for ports, and the function fails if the dbch_devicetypemember is DBT_DEVTYP_PORT. Volume notifications are also broadcast to top-level windows, so the function fails if dbch_devicetypeis DBT_DEVTYP_VOLUME. OEM-defined devices are not used directly by the system, so the function fails if dbch_devicetype is DBT_DEVTYP_OEM.

关于设备类型,猜测这个应该跟设备的驱动有关,在设备驱动中会指明设备的类型,然后在设备插入到系统中,会有一个枚举过程,这样系统就知道了该设备类型。上述的DBT_DEVICEARRIVAL和DBT_DEVICEREMOVECOMPLETE是WM_DEVICECHANGE消息中的参数。

如果设备类型为DBT_DEVTYP_DEVICEINTERFACE ,查看MSDN可知,该结构体中有一个guid的参数,正是我们需要的通过GUID来判断是否是特定设备的参数。我们在接受windows消息的函数中,判断是否是需要监测的设备即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public void ProcessWinMessage(int msg, IntPtr wParam, IntPtr lParam)
{
if (msg == Native.WM_DEVICECHANGE)
{
switch (wParam.ToInt32())
{
case (int)Native.WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL:

if (IsDesiredDevice(lParam))
{
if (StateChanged != null)
{
StateChanged(true);
}
}

break;

case (int)Native.WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE:

if (IsDesiredDevice(lParam))
{
if (StateChanged != null)
{
StateChanged(false);
}
}
break;
/*
* this event will be fired when device been added and removed
case Native.DBT_DEVNODES_CHANGED:

if (StateChanged != null)
{
StateChanged(false);
}
break;
* */
default:
break;
}
}
}

IsDesignedDevice定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private bool IsDesiredDevice(IntPtr lParam)
{
var hdr = (Native.DEV_BROADCAST_HDR)Marshal.PtrToStructure(lParam, typeof(Native.DEV_BROADCAST_HDR));
if (hdr.dbcc_devicetype == (uint)_deviceType)
{
if (_deviceType == Native.DeviceType.DBT_DEVTYP_DEVICEINTERFACE)
{
Native.DEV_BROADCAST_DEVICEINTERFACE deviceInterface =
(Native.DEV_BROADCAST_DEVICEINTERFACE)Marshal.PtrToStructure(lParam, typeof(Native.DEV_BROADCAST_DEVICEINTERFACE));

var str = Encoding.Default.GetString(deviceInterface.dbcc_classguid);
var guid = new Guid(deviceInterface.dbcc_classguid);
if (guid.ToString() == DEVICE_INTERFACE_GUID)
{
return true;
}
}
else
{
//TODO: other device type
}
}
return false;
}

DEV_BROADCAST_HDR结构体中的三个字段包含在所有设备类型结构体中,所以可以先将WM_DEVICECHANGE中的lParam转化为DEV_BROADCAST_HDR类型,然后根据其dbch_devicetype来转化为特定设备类型结构体类型,如DBT_DEVTYP_DEVICEINTERFACE

完整的例子请参考:UsbDetector