使用RegisterDeviceNotification监听NFC令牌的两个技术疑问
关于使用
RegisterDeviceNotification监听NFC令牌的疑问及解答 问题背景
我正尝试使用RegisterDeviceNotification实现对新NFC令牌的监听,现有以下两个疑问:
- 从设备管理器获取的未知智能卡(即NFC令牌)的类GUID是否正确?我已从设备管理器中未知智能卡(NFC令牌)的属性窗口提取了类GUID(见附图)。
- 设备类型设置为
DBT_DEVTYP_DEVICEINTERFACE是否正确?如何判断DBT_DEVTYP_DEVICEINTERFACE是否适用?
我的Pascal代码
type PDEV_BROADCAST_DEVICEINTERFACE = ^DEV_BROADCAST_DEVICEINTERFACE; {$EXTERNALSYM PDEV_BROADCAST_DEVICEINTERFACE} DEV_BROADCAST_DEVICEINTERFACE = record dbcc_size: DWORD; dbcc_devicetype: DWORD; dbcc_reserved: DWORD; dbcc_classguid: TGUID; dbcc_name: array[0..0] of Char; end; {$EXTERNALSYM DEV_BROADCAST_DEVICEINTERFACE} procedure TMainF.BRegisterDeviceNotificationClick(Sender: TObject); const testguid: TGUID = '{990a2bd7-e738-46c7-b26f-1cf8fb9f1391}'; var x: DEV_BROADCAST_DEVICEINTERFACE; begin FillChar(x, SizeOf(x), 0); x.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE; x.dbcc_classguid := testguid; x.dbcc_size := SizeOf(x); p := RegisterDeviceNotification(Handle, @x, DEVICE_NOTIFY_WINDOW_HANDLE); if not Assigned(p) then RaiseLastOSError; end; procedure TMainF.WMDeviceChange(var Msg: TMessage); var x: PDEV_BROADCAST_DEVICEINTERFACE; begin x := @Msg.LParam; case Msg.WParam of DBT_DEVNODES_CHANGED: begin MOutput.Lines.Add('Device changed: ' + FormatDateTime('yyyymmdd hh:nn:ss.zzz', now)); MOutput.Lines.Add(GUIDToString(x.dbcc_classguid)); end; DBT_DEVICEARRIVAL: begin MOutput.Lines.Add('Device arrived ' + FormatDateTime('yyyymmdd hh:nn:ss.zzz', now)); MOutput.Lines.Add(GUIDToString(x.dbcc_classguid)); end; DBT_DEVICEREMOVECOMPLETE: begin MOutput.Lines.Add('Device removed ' + FormatDateTime('yyyymmdd hh:nn:ss.zzz', now)); MOutput.Lines.Add(GUIDToString(x.dbcc_classguid)); end; end; end;
程序运行输出
Device changed: 20250204 16:05:34.776 {77AB164B-0F08-0021-1902-000007000000} Device changed: 20250204 16:05:38.291 {77AB164B-0F08-0021-1902-000007000000} Device changed: 20250204 16:05:38.358 {77AB164B-0F08-0021-1902-000007000000}
USB Device Tree Viewer设备信息
+++++++++++++++++ Device Information ++++++++++++++++++ Device Description : 未知智能卡 Device ID : SCFILTER\CID_804F0CA00000030603000300000000\7&43C62A7&0&ACS_ACR1252_1S_CL_READER_PICC_0_SCFILTER_CID_804F0CA00000030603000300000000 Hardware IDs : SCFILTER\CID_804f0ca00000030603000300000000 Driver KeyName : {990a2bd7-e738-46c7-b26f-1cf8fb9f1391}\0004 Driver Inf : C:\Windows\inf\scunknown.inf Legacy BusType : Internal Class : SmartCard Class GUID : {990a2bd7-e738-46c7-b26f-1cf8fb9f1391} Enumerator : SCFILTER Location Info : ScFilter Address : 4294967295 (0xFFFFFFFF) Manufacturer Info : Microsoft Capabilities : 0x84 (Removable, SurpriseRemovalOK) Status : 0x01806000 (DN_DISABLEABLE, DN_REMOVABLE, DN_NT_ENUMERATOR, DN_NT_DRIVER) Problem Code : 0 EnhancedPowerMgmtEnabled : 0 Power State : D3 (supported: D0, D3, wake from D0)
疑问解答
1. 类GUID是否正确?
从你提供的USB Device Tree Viewer信息来看,Class GUID字段明确显示为{990a2bd7-e738-46c7-b26f-1cf8fb9f1391},这个GUID是Windows系统定义的智能卡设备类标准GUID,所以你提取的类GUID是正确的。
但程序输出的GUID与注册值不符,原因是DBT_DEVNODES_CHANGED消息的LParam并不指向DEV_BROADCAST_DEVICEINTERFACE结构——该消息没有关联的设备细节数据,你强行将其转为PDEV_BROADCAST_DEVICEINTERFACE会读取到错误内存数据,导致GUID显示异常。
2. 设备类型设置是否正确?
DBT_DEVTYP_DEVICEINTERFACE是正确的选择,原因如下:
- 当需要监听某一类设备的插拔事件时,尤其是像智能卡这类通过设备接口类暴露的设备,该类型基于设备接口GUID匹配设备,适合批量监听同类型设备。
- 判断适用规则:如果目标设备属于已定义的设备接口类(比如智能卡类),或需要监听符合特定接口标准的设备,就用
DBT_DEVTYP_DEVICEINTERFACE;若监听单个特定设备(通过设备实例路径),则用DBT_DEVTYP_DEVICEINSTANCE;监听卷设备用DBT_DEVTYP_VOLUME。
另外你的代码存在两个关键问题:
DEV_BROADCAST_DEVICEINTERFACE结构的dbcc_size计算错误:该结构末尾是可变长度的dbcc_name数组,注册时需计算完整结构大小,对于Unicode版本应设置为SizeOf(DEV_BROADCAST_DEVICEINTERFACE) - SizeOf(Char) + 1,当前仅设为SizeOf(x)会导致结构不完整,可能引发注册失败或事件接收异常。WMDeviceChange消息处理逻辑错误:仅DBT_DEVICEARRIVAL和DBT_DEVICEREMOVECOMPLETE的LParam指向DEV_BROADCAST_HDR结构,需先检查该结构的dbch_devicetype是否为DBT_DEVTYP_DEVICEINTERFACE,再强转为PDEV_BROADCAST_DEVICEINTERFACE;DBT_DEVNODES_CHANGED无有效LParam数据,不能直接读取其中的GUID。
内容的提问来源于stack exchange,提问作者complete_stranger
相关产品推荐
相关产品推荐


