1515using System . Runtime . ExceptionServices ;
1616using System . Threading ;
1717using System . Threading . Tasks ;
18+ using Windows . Win32 ;
19+ using static ManipulationDemo . UsbNotification ;
20+ using System . Runtime . InteropServices ;
21+ using System . Text . RegularExpressions ;
1822
1923namespace ManipulationDemo
2024{
@@ -107,7 +111,7 @@ public MainWindow()
107111
108112 private readonly DispatcherTimer _timer ;
109113
110- private void OnTick ( object sender , EventArgs e )
114+ private unsafe void OnTick ( object sender , EventArgs e )
111115 {
112116 try
113117 {
@@ -154,6 +158,9 @@ private void OnTick(object sender, EventArgs e)
154158 device . Name , device . StylusDevices . Count , tabletSize ) ) ;
155159 }
156160 }
161+
162+ AppendPointerDeviceInfo ( builder ) ;
163+
157164 PhysicalSizeRun . Text = builder . ToString ( ) ;
158165 }
159166 catch ( Exception ex )
@@ -162,6 +169,38 @@ private void OnTick(object sender, EventArgs e)
162169 }
163170 }
164171
172+ /// <summary>
173+ /// 添加 Pointer 消息的信息
174+ /// </summary>
175+ /// <param name="stringBuilder"></param>
176+ private static unsafe void AppendPointerDeviceInfo ( StringBuilder stringBuilder )
177+ {
178+ try
179+ {
180+ // 获取 Pointer 设备数量
181+ uint deviceCount = 0 ;
182+ PInvoke . GetPointerDevices ( ref deviceCount ,
183+ ( Windows . Win32 . UI . Controls . POINTER_DEVICE_INFO * ) IntPtr . Zero ) ;
184+ Windows . Win32 . UI . Controls . POINTER_DEVICE_INFO [ ] pointerDeviceInfo =
185+ new Windows . Win32 . UI . Controls . POINTER_DEVICE_INFO [ deviceCount ] ;
186+ fixed ( Windows . Win32 . UI . Controls . POINTER_DEVICE_INFO * pDeviceInfo = & pointerDeviceInfo [ 0 ] )
187+ {
188+ // 这里需要拿两次,第一次获取数量,第二次获取信息
189+ PInvoke . GetPointerDevices ( ref deviceCount , pDeviceInfo ) ;
190+ stringBuilder . AppendLine ( $ "PointerDeviceCount:{ deviceCount } 设备列表:") ;
191+ foreach ( var info in pointerDeviceInfo )
192+ {
193+ stringBuilder . AppendLine ( $ " - { info . productString } ") ;
194+ }
195+ }
196+ }
197+ catch ( Exception e )
198+ {
199+ // 也许是在非 Win8 或以上的系统,抛出找不到方法,这个 GetPointerDevices 方法是 Win8 加的
200+ // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevices
201+ }
202+ }
203+
165204 private void OnStylusDown ( object sender , StylusDownEventArgs e )
166205 {
167206 StylusDownStoryboard . Begin ( ) ;
@@ -230,7 +269,8 @@ private void OnManipulationCompleted(object sender, ManipulationCompletedEventAr
230269 protected override void OnSourceInitialized ( EventArgs e )
231270 {
232271 base . OnSourceInitialized ( e ) ;
233- var source = ( HwndSource ) PresentationSource . FromVisual ( this ) ;
272+ var source = ( HwndSource ) PresentationSource . FromVisual ( this ) ! ;
273+ UsbNotification . RegisterUsbDeviceNotification ( source . Handle ) ;
234274 source ? . AddHook ( HwndHook ) ;
235275
236276 Log ( "程序启动时" ) ;
@@ -240,20 +280,80 @@ protected override void OnSourceInitialized(EventArgs e)
240280 private IntPtr HwndHook ( IntPtr hwnd , int msg , IntPtr wparam , IntPtr lparam , ref bool handled )
241281 {
242282 // 检查硬件设备插拔。
243- if ( msg == ( int ) WindowMessages . DEVICECHANGE )
283+ if ( msg == ( int ) WindowMessages . DEVICECHANGE )
244284 {
245- var eventText = $ "Event={ ( WindowsMessageDeviceChangeEventEnum ) wparam } ";
285+ // 是否应该加上通用的变更记录日志
286+ bool shouldCommonLog = true ;
287+
288+ bool isDeviceArrival = ( int ) wparam == ( int ) WindowsMessageDeviceChangeEventEnum . DBT_DEVICEARRIVAL ;
289+ bool isDeviceRemoveComplete = ( int ) wparam == ( int ) WindowsMessageDeviceChangeEventEnum . DBT_DEVICEREMOVECOMPLETE ;
290+
291+ if ( isDeviceArrival || isDeviceRemoveComplete )
292+ {
293+ // 设备被移除或插入,试试拿到具体是哪个设备
294+ DEV_BROADCAST_HDR hdr =
295+ ( DEV_BROADCAST_HDR ) Marshal . PtrToStructure ( lparam , typeof ( DEV_BROADCAST_HDR ) ) ;
296+ if ( hdr . dbch_devicetype == UsbNotification . DbtDevtypDeviceinterface )
297+ {
298+ DEV_BROADCAST_DEVICEINTERFACE deviceInterface =
299+ ( DEV_BROADCAST_DEVICEINTERFACE ) Marshal . PtrToStructure ( lparam ,
300+ typeof ( DEV_BROADCAST_DEVICEINTERFACE ) ) ;
301+
302+ var classguid = deviceInterface . dbcc_classguid ;
303+ // 这里的 classguid 默认会带上 name 上,于是就用不着
304+
305+ var size = Marshal . SizeOf ( typeof ( DEV_BROADCAST_DEVICEINTERFACE ) ) ;
306+ var namePtr = lparam + size ;
307+ var nameSize = hdr . dbch_size - size ;
308+ // 使用 Unicode 读取的话,一个字符是两个字节
309+ var charLength = nameSize / 2 ;
310+ var name = Marshal . PtrToStringUni ( namePtr , charLength ) ;
311+ if ( string . IsNullOrEmpty ( name ) )
312+ {
313+ name = "读取不到设备名" ;
314+ }
315+
316+ string pid = string . Empty ;
317+ string vid = string . Empty ;
318+
319+ var pidMatch = Regex . Match ( name , @"PID_([\dA-Fa-f]{4})" ) ;
320+ if ( pidMatch . Success )
321+ {
322+ pid = pidMatch . Groups [ 1 ] . Value ;
323+ }
324+
325+ var vidMatch = Regex . Match ( name , @"VID_([\dA-Fa-f]{4})" ) ;
326+ if ( vidMatch . Success )
327+ {
328+ vid = vidMatch . Groups [ 1 ] . Value ;
329+ }
330+
331+ Log ( DeviceChangeListenerTextBlock , $ "[WM_DEVICECHANGE] 设备{ ( isDeviceArrival ? "插入" : "拔出" ) } PID={ pid } VID={ vid } \r \n { name } ", true ) ;
332+
333+ // 换成带上更多信息的记录,不需要通用记录
334+ shouldCommonLog = false ;
335+ }
336+ }
246337
247- Log ( DeviceChangeListenerTextBlock , $ "[WM_DEVICECHANGE]设备发生插拔 0x{ wparam . ToString ( "X4" ) } -0x{ lparam . ToString ( "X4" ) } ;{ eventText } ", true ) ;
248- LogDevices ( ) ;
338+ if ( shouldCommonLog )
339+ {
340+ var eventText = $ "Event={ ( WindowsMessageDeviceChangeEventEnum ) wparam } ";
341+
342+ Log ( DeviceChangeListenerTextBlock ,
343+ $ "[WM_DEVICECHANGE]设备发生插拔 Param=0x{ wparam . ToString ( "X4" ) } -0x{ lparam . ToString ( "X4" ) } ;{ eventText } ",
344+ true ) ;
345+ LogDevices ( ) ;
346+ }
249347 }
250- else if ( msg == ( int ) WindowMessages . TABLET_ADDED )
348+ else if ( msg == ( int ) WindowMessages . TABLET_ADDED )
251349 {
252- Log ( DeviceChangeListenerTextBlock , $ "[TABLET_ADDED]触摸设备插入 0x{ wparam . ToString ( "X4" ) } - 0x{ lparam . ToString ( "X4" ) } ", true ) ;
350+ Log ( DeviceChangeListenerTextBlock ,
351+ $ "[TABLET_ADDED]触摸设备插入 0x{ wparam . ToString ( "X4" ) } - 0x{ lparam . ToString ( "X4" ) } ", true ) ;
253352 }
254- else if ( msg == ( int ) WindowMessages . TABLET_DELETED )
353+ else if ( msg == ( int ) WindowMessages . TABLET_DELETED )
255354 {
256- Log ( DeviceChangeListenerTextBlock , $ "[TABLET_DELETED]触摸设备拔出 0x{ wparam . ToString ( "X4" ) } - 0x{ lparam . ToString ( "X4" ) } ", true ) ;
355+ Log ( DeviceChangeListenerTextBlock ,
356+ $ "[TABLET_DELETED]触摸设备拔出 0x{ wparam . ToString ( "X4" ) } - 0x{ lparam . ToString ( "X4" ) } ", true ) ;
257357 }
258358
259359 // 输出消息。
@@ -262,7 +362,7 @@ private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref
262362 return IntPtr . Zero ;
263363 }
264364
265- var formattedMessage = $ "{ ( WindowMessages ) msg } ({ msg } )";
365+ var formattedMessage = $ "{ ( WindowMessages ) msg } ({ msg } )";
266366 Log ( HwndMsgTextBlock , formattedMessage ) ;
267367
268368 return IntPtr . Zero ;
0 commit comments